Develocity Provenance Governor
Develocity Provenance Governor is an enterprise solution designed to help automate governance, compliance, and risk for the software supply chain. It enables organizations to define, manage, and enforce provenance and attestation policies for software packages, ensuring that only trusted and compliant artifacts are promoted and consumed across environments.
1. How it works
Leveraging the build data in Develocity, Develocity Provenance Governor collects, verifies, and evaluates provenance data and attestations. The product offers detailed provenance attestations, declarative domain-aware policies, and automated evaluation, empowering teams to:
-
Enforce organizational and regulatory requirements for software provenance
-
Prevent the use of untrusted or non-compliant artifacts
-
Gain visibility into the origin and integrity of software components
-
Streamline compliance reporting and incident response
1.1. Key Concepts
Below are the terms and concepts you need to understand to use Develocity Provenance Governor:
-
Attestations are signed statements about the origin (or provenance) of a software package, including details about its build process, dependencies, and other metadata. Attestations can be used to verify the integrity and authenticity of software packages.
-
Policies define rules and conditions that software packages must adhere to. You can use policies to prevent non-compliant or untrusted packages from being released or deployed.
-
Policy scans evaluate packages against defined policies and report on the package’s level of compliance.
1.2. Components
1.3. Flow
Below is a typical flow for using Develocity Provenance Governor in a software supply chain:
-
A build runs on a Continuous Integration (CI) system and:
-
Produces a software package (for example, a JAR file).
-
Publishes a Build Scan to Develocity (Build Scans capture detailed information about the build process).
-
Uploads the software package to a package repository (for example, JFrog Artifactory).
-
Calls Develocity Provenance Governor to generate and publish attestations for the software package.
-
-
Develocity Provenance Governor generates attestations based on Build Scan data from Develocity that was collected while building the software package.
-
After an attestation is generated, Develocity Provenance Governor signs the attestation using a private key.
-
The signed attestation is published to an attestation store. Develocity Provenance Governor supports publishing attestations to JFrog Artifactory.
-
-
A later CI or Continuous Deployment (CD) job calls Develocity Provenance Governor to perform a policy scan on the software package.
-
Develocity Provenance Governor performs a policy scan to evaluate policies against the software package to determine if it is compliant with organization rules and requirements. Develocity Provenance Governor uses the previously published attestations to evaluate policies.
-
The result of the policy scan is returned to the calling job.
-
Based on the results of the policy scan, the job can decide to proceed or fail.
-
|
Once published, attestations generated by Develocity Provenance Governor can also be used with JFrog Evidence features and functionality. |
2. Getting set up
Develocity Provenance Governor is delivered as a containerized application designed for Kubernetes deployment. While the application is cloud-native by design, proper setup requires careful configuration of prerequisites, credentials, and policies.
2.1. Prerequisites
Before deploying Develocity Provenance Governor, ensure you have the following prerequisites. These are organized in a logical order that matches the typical installation workflow: infrastructure setup, external integrations, and security configuration.
|
Recommended Setup Order: Infrastructure (set up first):
External Integrations (configure before deployment):
Security (prepare before first use):
Planning:
|
2.1.1. Develocity License
Develocity Provenance Governor uses your existing Develocity license - no separate license is required. Any valid Develocity license works; no special entitlements are needed. If you are already a Develocity customer, you can use your existing license. If you do not have a Develocity license, contact Gradle’s sales team and mention that you need access for "Develocity Provenance Governor".
2.1.2. Kubernetes Cluster
Develocity Provenance Governor is designed to run in a Kubernetes environment. You will need to install it into a Kubernetes cluster. The Kubernetes cluster must have an Ingress Controller.
|
Develocity Provenance Governor can be deployed in environments other than Kubernetes. If you need to run Develocity Provenance Governor outside of Kubernetes please open a support ticket and describe your deployment requirements. |
2.1.3. Cluster Resources
Plan for these resource requirements: By default, the combined Kubernetes resource requests are 2 CPUs and 1 GiB memory per replica. These values are configurable via Kubernetes Deployment values, but lower values may impact performance.
2.1.4. JFrog Artifactory with Evidence Management
|
Artifactory Enterprise+ license (version 7.104.2 or later) is required. The Evidence Management feature is mandatory for Develocity Provenance Governor operation. Without Artifactory configured with this feature, Develocity Provenance Governor cannot store or retrieve attestations. |
Develocity Provenance Governor requires Artifactory as the attestation store. All attestations are stored in and retrieved from Artifactory, making it a critical component of your deployment.
Requirements:
-
Artifactory version 7.104.2 or later
-
Enterprise+ license
-
Evidence Management feature enabled with support for external evidence
-
Network access from the Develocity Provenance Governor deployment to the Artifactory instance
-
Artifactory Access Token or Identity Token with the following permissions:
-
Read - to retrieve attestations
-
Annotate - to attach evidence metadata to artifacts
-
2.1.5. Develocity Instance
Develocity Provenance Governor uses Develocity Build Scan data to generate attestations. It requires network access to a Develocity instance.
Develocity Provenance Governor requires a valid Develocity access key for a user with the “Access build data via the API” permission. If the Develocity instance is using Project-level Access Control, Develocity Provenance Governor will only be able to publish attestations for Build Scan data in projects that the user has access to.
The minimum version of Develocity that Develocity Provenance Governor supports is 2023.3. Develocity Provenance Governor supports publishing attestations from Build Scan data from the following build tools:
-
Gradle, with Develocity plugin version 1.16 or later.
-
JVM attestations for Gradle toolchains are only supported for plugin 3.11 or later.
-
-
Maven, with Develocity extension version 1.4 or later.
We recommend using the latest available version of Develocity and its build tool integrations for the best experience.
2.1.6. Signing Key Pair
Develocity Provenance Governor cryptographically signs all attestations to ensure authenticity and prevent tampering. You need to generate a signing key pair before deployment. The private key remains with Develocity Provenance Governor and is used to sign attestations. The public key must be registered in your Artifactory instance to allow verification of signed attestations.
Supported Key Types:
We support Ed25519, RSA, and Elliptic Curve keys. Ed25519 is recommended because it provides strong security (equivalent to 128-bit security) with significantly smaller keys and faster operations than RSA or ECDSA.
Generating a Key Pair:
Develocity Provenance Governor needs to be configured with a signing key pair in PEM format. You can generate an Ed25519 key pair using OpenSSL:
openssl genpkey -algorithm Ed25519 -out ./key-pair.private-pem
openssl pkey -in ./key-pair.private-pem -pubout -out ./key-pair.public-pem
|
Keep the private key secure. You’ll configure it as a Kubernetes secret during deployment. The public key will be registered in Artifactory to verify attestation signatures. |
2.1.7. Hostname
Develocity Provenance Governor needs a hostname. It will be used in its Ingress. Ensure that the hostname’s DNS is configured to work with the Kubernetes cluster’s Ingress Controller.
2.1.8. TLS Certificate
You’ll need a TLS (Transport Layer Security) certificate for your hostname to enable SSL (Secure Sockets Layer). While SSL/TLS is technically optional, it is strongly recommended for production environments.
Options include:
-
Using an existing organizational certificate
-
Generating one with cert-manager and Let’s Encrypt (see deployment section for details)
-
Using a self-signed certificate for testing (not recommended for production)
2.2. Deployment
Develocity Provenance Governor is provided as a container image that runs on a Kubernetes cluster (recommended) or a local Docker/Kubernetes environment. Make sure you have the prerequisites detailed in Prerequisites before proceeding.
2.2.1. Audience
This guide is written for a Platform / Infrastructure Engineer. It assumes:
-
Basic familiarity with Kubernetes (namespaces, secrets, configmaps, deployments, ingress)
-
Ability to obtain a Develocity license file (
develocity.license) -
(Optional) Ability to obtain access tokens/keys for Develocity and Artifactory instances
-
Access to a DNS domain (optional, for public HTTPS ingress)
2.2.2. Quick Start (Local k3d + Basic HTTP Ingress)
If you just want to stand up a working instance locally with k3d and basic auth (no TLS) follow these condensed steps. Full details remain in later sections. Download the manifest.yaml file.
Details
# 1. Create cluster (port 80 exposed)
brew install k3d
k3d cluster create --port "80:80@loadbalancer"
# 2. Namespace
kubectl create namespace develocity-provenance-governor
# 3. Create Docker registry secret for pulling the Develocity Provenance Governor image
# The license is used as authentication (format: 'ge:' prefix + base64-encoded license)
LICENSE=$(echo -n "user:$(cat ./develocity.license)" | base64 | tr -d '\n') # The 'user:' prefix is for clarity and is ignored by the registry.
cat > registry-secret.json <<JSON
{ "auths": { "https://registry.gradle.com": { "auth": "${LICENSE}" } } }
JSON
kubectl create secret docker-registry registry-secret \
--namespace develocity-provenance-governor \
--from-file=.dockerconfigjson=./registry-secret.json
kubectl create secret generic license \
--namespace develocity-provenance-governor \
--from-file=develocity.license=./develocity.license
# 4. Configure Basic HTTP Ingress (nip.io for local resolution)
kubectl --namespace develocity-provenance-governor apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api.127.0.0.1.nip.io
spec:
rules:
- host: api.127.0.0.1.nip.io
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-http
port:
number: 80
EOF
# 5. Deploy core components (set version + digest)
# Download manifest.yaml using the link provided above.
kubectl --namespace develocity-provenance-governor apply -f manifest.yaml
# 6. Verify rollout
kubectl rollout status deployment/api --namespace develocity-provenance-governor --timeout=90s
# 7. Check logs
kubectl logs deployment/api --namespace develocity-provenance-governor | head -n 50
# 8. Test route (expect 401 Unauthorized – no credentials yet)
curl -i api.127.0.0.1.nip.io
|
Quick Start complete? If you successfully deployed the basic instance, see the Deployment Flow Overview below for the recommended order of detailed configuration steps, or jump directly to specific sections to extend functionality with authentication, policies, signing keys, and external integrations. |
2.2.3. Deployment Flow Overview
The recommended order is:
-
Choose / create a Kubernetes cluster (local k3d or existing)
-
Create namespace + required secrets (registry + license)
-
Configure Ingress (HTTP, then HTTPS + cert-manager) - Optional
-
Deploy the core workload (ServiceAccount, Services, Deployment, NetworkPolicy)
-
Create placeholder Secrets/ConfigMaps for dynamic configuration (
secrets,properties,policies) -
Add integrations (Develocity instances, Artifactory instances)
-
Enable authentication (Basic and/or OIDC)
-
Define policies (AccessControl manifests)
-
Roll out config changes as needed
Estimated setup time:
-
Local k3d (Quick Start): 15-30 minutes
-
Production setup with all integrations: 2-4 hours (depending on external service coordination and certificate setup)
2.2.4. Select Kubernetes Distribution
Before proceeding ensure access to a Kubernetes cluster has been provided, otherwise a local k3d cluster is also supported.
k3d
brew install k3d
k3d cluster create --port "80:80@loadbalancer"
2.2.5. Prepare the Kubernetes Namespace and Required Secrets
You must create a Kubernetes namespace and the secrets needed for the application to start.
Create Kubernetes Namespace
kubectl create namespace develocity-provenance-governor
Docker Registry Secret (Required)
Authenticate to registry.gradle.com to pull the product image.
LICENSE=$(echo -n "user:$(cat ./develocity.license)" | base64 | tr -d '\n') # The 'user:' prefix is for clarity and is ignored by the registry.
cat <<EOF >./registry-secret.json
{
"auths": {
"https://registry.gradle.com": {
"auth":"${LICENSE}"
}
}
}
EOF
kubectl create secret docker-registry registry-secret \
--namespace develocity-provenance-governor \
--from-file=.dockerconfigjson=./registry-secret.json
2.2.6. Deploy Core Components
Apply the workload manifests. (See Quick Start)
Download the manifest.yaml file.
kubectl --namespace develocity-provenance-governor apply -f manifest.yaml
kubectl rollout status deployment/api \
--timeout=90s \
--namespace develocity-provenance-governor
Expected output: deployment "api" successfully rolled out.
If rollout fails:
-
Verify
registry-secretcredentials and image digest/tag -
Verify
licensesecret presence and correct file content
kubectl logs deployment/api \
--namespace develocity-provenance-governor | grep -i license
Develocity license enabled, with license [...]
Started ProvenanceGovernor in ...
2.2.7. Ingress (Optional)
No default ingress is provided. Choose what aligns with organizational standards. Below examples cover:
-
Local development (nip.io hostname)
-
HTTPS with cert-manager & Let’s Encrypt
kubectl --namespace develocity-provenance-governor apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api.127.0.0.1.nip.io
spec:
rules:
- host: api.127.0.0.1.nip.io
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-http
port:
number: 80
EOF
kubectl get ingress api.127.0.0.1.nip.io --namespace develocity-provenance-governor
NAME CLASS HOSTS ADDRESS PORTS AGE
http traefik api.127.0.0.1.nip.io 172.18.0.3 80 15s
curl api.127.0.0.1.nip.io -i
HTTP/1.1 401 Unauthorized
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Length: 0
Expires: 0
Pragma: no-cache
Referrer-Policy: no-referrer
Www-Authenticate: Basic realm="Realm"
...
Ingress with cert-manager
Alternative, ingress manifest using cert-manager, nginx, and signed certificate from Let’s Encrypt.
The ingress resource, can be used to expose the api service outside the cluster.
Modify the provided manifest to reflect the domain that you control paying close attention to tls[0].hosts[0] and rules[0].host.
kubectl --namespace develocity-provenance-governor apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: https
spec:
tls:
- hosts:
- api.example.com
secretName: https-api-tls
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-http
port:
number: 80
EOF
At this point, the ingress controller might use a self-signed certificate provided as a default.
kubectl get ingress https --namespace develocity-provenance-governor
NAME CLASS HOSTS ADDRESS PORTS AGE
https traefik api.example.com xxx.xxx.xx.x 80, 443 15s
If not already deployed, install cert-manager
kubectl --namespace develocity-provenance-governor create --edit -f - <<EOF
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: lets-encrypt
spec:
acme:
# The ACME server URL
server: https://acme-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: user@example.com
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: lets-encrypt
# Enable the HTTP-01 challenge provider
solvers:
- http01:
ingress:
ingressClassName: nginx
EOF
| This will create a certificate issuer using the Production Instance of Let’s Encrypt. Edit accordingly based on desired certificate issuer implementation. If using Let’s Encrypt, ensure the ingress is publicly accessible and can handle a http01 challenge provider request to complete certificate issuance. Also, be sure to update the email address. |
kubectl describe issuer lets-encrypt \
--namespace develocity-provenance-governor
Detailed issuer information should be returned along with Status.Status: True and Status.Type: Ready.
Edit the https ingress resource to use the lets-encrypt issuer instead of the self signed certificate issuer.
kubectl edit ingress https \
--namespace develocity-provenance-governor
cert-manager.io/issuer: lets-encrypt to annotations:metadata:
annotations:
cert-manager.io/issuer: lets-encrypt
$ kubectl get certificate \
--namespace develocity-provenance-governor
NAME READY SECRET AGE
https-api-tls True https-api-tls 2m31s
$ curl https://api.example.com -i
curl https://api.example.com -i
HTTP/2 401
content-length: 0
www-authenticate: Basic realm="Realm"
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
x-content-type-options: nosniff
strict-transport-security: max-age=31536000; includeSubDomains
x-frame-options: DENY
x-xss-protection: 0
referrer-policy: no-referrer
No SSL negotiation exceptions should be thrown and a 401 should be returned.
2.2.8. Create Placeholders for Dynamic Configuration (Optional but Recommended)
The Deployment mounts (optionally) these resources:
Resource |
Kind |
Purpose |
Required |
Mounted Path |
license |
Secret |
Develocity product license |
Yes |
/workspace/config/license |
secrets |
Secret |
Sensitive values (access tokens, private keys) |
No |
/workspace/config/secrets |
properties |
ConfigMap |
Non-sensitive configuration (URIs, public keys) |
No |
/workspace/config/properties |
policies |
ConfigMap |
Policy manifests |
No |
/workspace/config/policies |
Create empty resources to prepare for configuration:
kubectl create secret generic secrets --namespace develocity-provenance-governor || true
kubectl create configmap properties --namespace develocity-provenance-governor || true
kubectl create configmap policies --namespace develocity-provenance-governor || true
2.2.9. Configure Develocity Instances (Optional)
Use one or more Develocity instances as attestation data sources.
Key patterns:
-
Secret:
develocity.instances.<NAME>.access-key -
ConfigMap:
develocity.instances.<NAME>.uri
<NAME> must be alphanumeric plus dashes or underscores.
kubectl edit secret secrets --namespace develocity-provenance-governor
Add access key (use stringData: when adding new values):
stringData:
develocity.instances.MY_INSTANCE.access-key: my-access-key-for-develocity
kubectl edit configmap properties --namespace develocity-provenance-governor
Add URI:
data:
develocity.instances.MY_INSTANCE.uri: https://develocity.example.com
Rollout & Verify:
kubectl rollout restart deployment/api --namespace develocity-provenance-governor
kubectl rollout status deployment/api --namespace develocity-provenance-governor --timeout=90s
kubectl logs deployment/api --namespace develocity-provenance-governor | grep -i "Develocity support enabled"
Expected log snippet:
Develocity support enabled, for instance [MY_INSTANCE:https://develocity.example.com]
Troubleshooting: Repeated Retrying [n/10] request … indicates connectivity or credential issues.
2.2.10. Configure Artifactory Instances (Optional)
Used to publish attestation data.
Key patterns:
-
Secret:
artifactory.instances.<NAME>.access-token(token preferred over legacy key) -
ConfigMap:
artifactory.instances.<NAME>.uri
kubectl edit secret secrets --namespace develocity-provenance-governor
Add token:
stringData:
artifactory.instances.MY_ARTIFACTORY.access-token: my-access-token-for-artifactory
kubectl edit configmap properties --namespace develocity-provenance-governor
Add URI:
data:
artifactory.instances.MY_ARTIFACTORY.uri: https://artifactory.example.com
Rollout & Verify:
kubectl rollout restart deployment/api --namespace develocity-provenance-governor
kubectl rollout status deployment/api --namespace develocity-provenance-governor --timeout=90s
kubectl logs deployment/api --namespace develocity-provenance-governor | grep -i "Artifactory support enabled"
Expected log snippet:
Artifactory support enabled, for instance [MY_ARTIFACTORY:https://artifactory.example.com]
2.2.11. Configure Signing Keys (Optional but Recommended for Attestations)
Attestations are wrapped in a Dead Simple Signing Envelope (DSSE) signed by a key pair.
Required key property patterns:
-
Secret (private key, base64 PEM):
signing.key.<FRIENDLY_KEY_NAME>.private-pem -
ConfigMap (public key PEM block):
signing.key.<FRIENDLY_KEY_NAME>.public-pem
Choose a naming convention like <ORG_OR_PRODUCT>_YYYY-MM-DD for rotation clarity.
Create Ed25519 Key Pair
Recommended (128-bit security, small key sizes/signatures).
openssl genpkey \
-algorithm Ed25519 \
-out private-key.pem
openssl pkey \
-in private-key.pem \
-pubout \
-out public-key.pem
kubectl edit secret secrets \
--namespace develocity-provenance-governor
Base64 and add private key (Secret):
base64 -i private-key.pem | pbcopy
kubectl edit secret secrets --namespace develocity-provenance-governor
Paste under data: (Kubernetes will not modify pasted base64):
data:
signing.key.FRIENDLY_KEY_NAME.private-pem: <PASTE_BASE64_PRIVATE_KEY>
cat public-key.pem | sed 's/^/ /' | pbcopy
kubectl edit configmap properties --namespace develocity-provenance-governor
Paste under data::
data:
signing.key.FRIENDLY_KEY_NAME.public-pem: |
# PASTE THE PUBLIC KEY BLOCK HERE FROM CLIPBOARD
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
Rollout & Verify:
kubectl rollout restart deployment/api --namespace develocity-provenance-governor
kubectl logs deployment/api --namespace develocity-provenance-governor | grep -i "Signature support enabled"
Expected log:
Signature support enabled, for key pair [name:FRIENDLY_KEY_NAME:keyid:XXXXXX:signing-algorithm:ed25519]
Key ID is a 6-character abbreviation of the SHA-256 digest of the public key.
Other Supported Algorithms
Elliptic Curve (ECDSA) and RSA are also supported. Substitute commands below; follow the same secret/configmap procedure.
2.2.12. Authentication (Optional)
Supported schemes:
Enable Basic Authentication
Add identities under:
-
Secret key pattern:
basic.identities.<NAME>
<NAME> is a human-friendly identifier.
No default credentials exist.
kubectl edit secret secrets --namespace develocity-provenance-governor
stringData:
basic.identities.some-user: "{noop}some-pass"
Recommendation: Replace {noop} with {bcrypt} and supply a bcrypt-hashed password.
kubectl rollout restart deployment/api \
--namespace develocity-provenance-governor
kubectl logs deployment/api \
--namespace develocity-provenance-governor |
grep -i "Basic Identity support"
Basic Identity support enabled, for identity [some-user]
2.2.13. Policies (Optional)
Policies are YAML documents stored inside the policies ConfigMap.
Each key corresponds to a file-style name ending with .yaml.
Access Control Policies
Define who can do what on which resources.
identityMatchingStrategy supported:
-
withBasicIdentity– must match a key underbasic.identities.<NAME>insecrets -
withOidc– relies on issuer discovery; ensure network accessibility
canPerform supported actions:
* publish-attestations - publish DSSE-wrapped attestations to an Attestation Store (e.g., Artifactory)
* publish-policy-scans - publish policy scan results to an Attestation Store
withResources supported patterns:
-
pkg:pkg-type/pkg-namespace/pkg-name@pkg-vesion- specific Package URL pattern to match or use wildcards:-
pkg:maven/*- all Maven packages -
pkg:oci/*- all OCI packages (container images) -
pkg:maven/org.example/@- all Maven packages inorg.examplenamespace and any version -
pkg:maven/org.example/my-app@1.0.0- specific Maven package -
pkg:oci/image-name@v1*- specific OCI image with wildcard version
-
-
*:REPLACE_WITH_DV_INSTANCE_NAME/*- allow to read build scans from the Develocity Instance to source data for attestations. -
*:REPLACE_WITH_ARTIFACTORY_INSTANCE_NAME/*- allow to publish attestations to the Artifactory Instance. Replace*with the repository name in artifactory to restrict access further. -
*:*/*- allows authenticated identity access to all resources (not recommended for production use)
Basic Identity Policy Example
kubectl edit configmap policies --namespace develocity-provenance-governor
data:
basic-identity-some-user-full-access.yaml: |
apiVersion: policy.gradle.com/v1
kind: AccessControl
metadata:
name: basic-identity-some-user-full-access
spec:
identityMatchingStrategy:
withBasicIdentity:
- withName: "some-user"
canPerform:
- publish-attestations
- publish-policy-scans
withResources:
- "*:*/*"
OIDC Policy Example (GitHub Actions)
kubectl edit configmap policies --namespace develocity-provenance-governor
data:
oidc-github-action-full-access.yaml: |
apiVersion: policy.gradle.com/v1
kind: AccessControl
metadata:
name: oidc-github-action-full-access
spec:
identityMatchingStrategy:
withOidc:
- fromIssuerUri: https://token.actions.githubusercontent.com
withClaims:
job_workflow_ref: org/automation-repo/.github/workflows/build-image.yml@*
canPerform:
- publish-attestations
- publish-policy-scans
withResources:
- "*:*/*"
Public Key Verification Policy Example
kubectl edit configmap policies --namespace develocity-provenance-governor
data:
public-key-verification-policy.yaml: |
apiVersion: policy.gradle.com/v1
kind: TrustedPublicKeys
metadata:
name: public-keys
spec:
resultsLabels: []
description: Public keys trusted for attestations.
remediation: Update the list of trusted public keys to ensure only verified attestations are accepted.
keys:
DEPLOYMENT_KEY_2025_10_01:
pem: |
-----BEGIN PUBLIC KEY-----
....
-----END PUBLIC KEY-----
Replace DEPLOYMENT_KEY_2025_10_01 and the PEM block with your actual trusted public keys, for example the ones used to sign attestations.
kubectl rollout restart deployment/api \
--namespace develocity-provenance-governor
kubectl rollout status deployment/api \
--namespace develocity-provenance-governor \
--timeout=90s
kubectl logs deployment/api \
--namespace develocity-provenance-governor |
grep -i "Loading Policy Resource from file"
Loading Policy Resource from file [/workspace/config/policies/public-key-verification-policy.yaml]
2.2.14. Troubleshooting Deployment Rollouts
Common rollout hang:
Waiting for deployment "api" rollout to finish: 1 old replicas are pending termination...
error: timed out waiting for the condition
Inspect pods:
kubectl get pods --namespace develocity-provenance-governor
Identify failing pod (e.g., CrashLoopBackOff) and fetch logs:
kubectl logs api-<POD_ID> --namespace develocity-provenance-governor
Example configuration error:
***************************
APPLICATION FAILED TO START
***************************
Failed to bind properties under 'artifactory.instances.INSTANCE_NAME_GOES_HERE' ... Reason: java.lang.IllegalArgumentException: uri must not be null
Remediation:
-
Missing URI – edit
propertiesConfigMap keyartifactory.instances.<NAME>.uri -
Missing token – edit
secretskeyartifactory.instances.<NAME>.access-token
After fixing, restart:
kubectl rollout restart deployment/api --namespace develocity-provenance-governor
kubectl rollout status deployment/api --namespace develocity-provenance-governor --timeout=90s
2.2.15. Verification and Next Steps
After completing the deployment, verify that Develocity Provenance Governor is running correctly:
-
Check Pod Status
kubectl get pods -n develocity-provenance-governorYou should see the
apipod inRunningstate with1/1ready. -
Check Application Logs
kubectl logs deployment/api -n develocity-provenance-governor --tail=50Look for messages indicating: License loaded successfully Integrations enabled (e.g., "Develocity support enabled", "Artifactory support enabled") Policies loaded (if configured) No error messages
-
Test API Accessibility
curl -i http://your-dpg-hostnameYou should receive a
401 Unauthorizedresponse, which confirms the application is running and authentication is required.The actuator endpoints (health, readiness, liveness) are exposed on port 9090 and not accessible through the public ingress. Develocity Provenance Governor also exposes
/livezand/readyzendpoints on the main application port for health checking when Kubernetes probes are enabled. -
Verify Integration Configuration
Check that your configured integrations were loaded:
kubectl logs deployment/api -n develocity-provenance-governor | grep "support enabled"
|
Successfully deployed? If all verification steps passed and you’re ready to start using Develocity Provenance Governor, proceed to Section 3: Using Develocity Provenance Governor to learn how to publish attestations and evaluate policies. |
What to Do Next:
-
Configure Your First Integration - If you haven’t already, set up connections to Develocity and Artifactory. See Application Configuration.
-
Set Up Authentication - Configure access control policies to secure your API. See Access Control.
-
Write Your First Policy - Define policies for your software supply chain governance. See Writing Policies.
-
Publish Test Attestations - Try publishing attestations for a test package. See Using Develocity Provenance Governor.
-
Integrate with CI/CD - Add Develocity Provenance Governor to your build and deployment pipelines. See GitHub Actions or Integration with Other CI/CD Systems.
If you encounter issues, consult the Troubleshooting section.
2.2.16. Summary Cheat Sheet
Integration |
Secret Key Pattern |
ConfigMap Key Pattern |
Mandatory |
Notes |
License |
(file) |
n/a |
Yes |
Provided via |
Registry Auth |
|
n/a |
Yes |
JSON auth file for image pulls |
Develocity Instance |
|
|
No |
Multiple supported |
Artifactory Instance |
|
|
No |
Token preferred |
Signing Key (Private) |
|
n/a |
No |
Base64 PEM |
Signing Key (Public) |
n/a |
|
No |
PEM block |
Basic Identity |
|
n/a |
No |
|
Policies |
n/a |
|
No |
AccessControl docs |
2.3. Application Configuration
Configuration for Develocity Provenance Governor uses Kubernetes-native resources that are mounted into the container at startup:
-
ConfigMap 'properties' - Non-sensitive settings (URIs, public keys)
-
Secret 'secrets' - Sensitive data (access keys, tokens, private keys)
-
ConfigMap 'policies' - YAML policy definitions
Changes to these resources take effect after restarting the deployment.
2.3.1. Configuration Overview
Configure Develocity Provenance Governor in the following order:
| Priority | Component | Required |
|---|---|---|
1 |
✓ Required - Without this, no attestations can be generated |
|
2 |
✓ Required - Without this, no attestations can be stored |
|
3 |
✓ Required - Without this, no attestations can be signed |
|
4 |
✓ Required - Without this, the API is not accessible |
|
5 |
✓ Required for Operation - Defines what to check |
Each section below details the configuration for these components.
2.3.2. Develocity
To configure access to a Develocity instance, configure the following application properties:
develocity.instances.<instance-name>.uri=https://develocity.example.com (1)
| 1 | <instance-name> is an identifier you choose (e.g., 'prod-dv' or 'main').
This name is used in access control policies and log messages.
Use alphanumeric characters, dashes, or underscores. |
Provide a Develocity access key in the sensitive application properties:
develocity.instances.<instance-name>.access-key=************ (your access key here)
2.3.3. Artifactory
To configure access to an Artifactory instance, configure the following application properties:
artifactory.instances.<instance-name>.uri=https://artifactory.example.com (1)
| 1 | <instance-name> is a name you choose to identify this Artifactory instance. |
Provide an Artifactory access or ID token in the sensitive application properties:
artifactory.instances.<instance-name>.access-token=************ (your access token here)
Advanced Artifactory Configuration
The following advanced properties can be configured for each Artifactory instance:
artifactory.instances.<instance-name>.path=/artifactory (1)
artifactory.instances.<instance-name>.graphql-path=/onemodel/api/v1/graphql (2)
artifactory.instances.<instance-name>.evidence-path=/evidence/api/v1/subject (3)
artifactory.instances.<instance-name>.retries-writing.attempts=3 (4)
artifactory.instances.<instance-name>.retries-writing.min-backoff=1s (5)
artifactory.instances.<instance-name>.retries-reading.attempts=3 (6)
artifactory.instances.<instance-name>.retries-reading.min-backoff=1s (7)
| 1 | The base path to the Artifactory instance. Defaults to /artifactory. |
| 2 | The path to the Artifactory GraphQL API. Defaults to /onemodel/api/v1/graphql. |
| 3 | The path to the Artifactory Evidence Management API. Defaults to /evidence/api/v1/subject. |
| 4 | The maximum number of retry attempts for write operations to Artifactory. Defaults to 3. |
| 5 | The minimum backoff duration between retry attempts for write operations. Defaults to 1s. |
| 6 | The maximum number of retry attempts for read operations from Artifactory. Defaults to 3. |
| 7 | The minimum backoff duration between retry attempts for read operations. Defaults to 1s. |
2.3.4. S3
To configure access to an Amazon S3 bucket for storing or reading attestations, configure the following application properties:
s3.instances.<instance-name>.region=us-east-1 (1)
s3.instances.<instance-name>.bucket-name=dpg-bucket-01 (2)
| 1 | The AWS region where the bucket is located. |
| 2 | The name of the S3 bucket. |
S3 Authentication
You can configure authentication using either static credentials, IAM roles, or environmental credentials. The application attempts to authenticate in the following order:
-
IAM Role Assumption: Used if
role-arnis configured. -
Static Credentials: Used if
access-key-idandsecret-access-keyare configured. -
Container Credentials: (Default) Uses the environment’s credentials provider chain (e.g., IRSA in EKS, EC2 instance profile).
Static Credentials:
s3.instances.<instance-name>.access-key-id=AKIA...
s3.instances.<instance-name>.secret-access-key=...
IAM Role Assumption:
s3.instances.<instance-name>.role-arn=arn:aws:iam::123456789012:role/MyRole
s3.instances.<instance-name>.role-session-name=provenance-governor
AWS IAM Permissions
The IAM Role used by the application must have the following permissions on the target S3 bucket:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::<bucket-name>",
"arn:aws:s3:::<bucket-name>/*"
]
}
]
}
-
s3:PutObject: Required for publishing attestations. -
s3:GetObject: Required for reading attestations (Fetch by ID, Policy Evaluation). -
s3:ListBucket: Required for discovering attestations during Policy Evaluation.
Associate Service Account Role (EKS)
When running on Amazon EKS, you can associate an IAM role with a Kubernetes Service Account using IRSA (IAM Roles for Service Accounts).
To enable this, the Service Account used by the Develocity Provenance Governor API pod must be annotated with the ARN of the IAM role.
The default deployment uses a Service Account named api in the develocity-provenance-governor namespace.
You can patch the Service Account after applying the manifest:
kubectl annotate serviceaccount api \
-n develocity-provenance-governor \
eks.amazonaws.com/role-arn=arn:aws:iam::123456789012:role/MyAttestationWriterRole
Alternatively, if you are using Kustomize to manage your deployment, you can create a kustomization.yaml to patch the manifest:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- manifest.yaml
patches:
- target:
kind: ServiceAccount
name: api
namespace: develocity-provenance-governor
patch: |-
- op: add
path: /metadata/annotations
value:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/MyAttestationWriterRole
S3 Connection Tuning
The following properties allow tuning of the S3 connection and request handling:
s3.instances.<instance-name>.endpoint=https://s3.us-west-2.amazonaws.com (1)
s3.instances.<instance-name>.path-style-access=false (2)
s3.instances.<instance-name>.max-concurrency=50 (3)
s3.instances.<instance-name>.max-object-size=10MB (4)
| 1 | The S3 endpoint URI. If omitted, the default AWS endpoint for the configured region is used. |
| 2 | Whether to use path-style access (e.g., https://s3.amazonaws.com/bucket/key). Defaults to false (virtual-hosted-style). |
| 3 | The maximum number of concurrent HTTP requests to S3. Defaults to the AWS SDK default. |
| 4 | The maximum allowed size for an attestation object. Defaults to 10MB. |
S3 Timeouts
s3.instances.<instance-name>.connection-timeout=2s (1)
s3.instances.<instance-name>.read-timeout=30s (2)
s3.instances.<instance-name>.write-timeout=30s (3)
| 1 | The timeout for establishing a connection to S3. Defaults to 2s. |
| 2 | The timeout for reading data from S3. Defaults to 30s. |
| 3 | The timeout for writing data to S3. Defaults to 30s. |
2.3.5. Access Control
Access control in Develocity Provenance Governor is configured using Access Control policies, which are YAML documents formatted like the other Develocity Provenance Governor policies.
They assign access rights to identities and resources that the application has been configured with.
Develocity Provenance Governor uses a single YAML file for access control policies, but it can include multiple policies by using YAML’s --- document separator.
apiVersion: policy.gradle.com/v1
kind: AccessControl
metadata:
name: admin
spec:
identityMatchingStrategy: (1)
withBasicIdentity:
- withName: "admin" (2)
withOidc:
- withIssuerUri: https://my-oidc-issuer.example.com (3)
withClaims: (4)
organization: "my-org"
canPerform:
- publish-attestations (5)
- publish-policy-scans (6)
- read-attestations (7)
withResources: (8)
- pkg:maven/* (9)
- dv:my-develocity (10)
- af:my-artifactory/* (11)
- s3:my-s3-store/* (12)
| 1 | Matchers for identities to apply this policy to. May match OIDC (OpenID Connect) identities using HTTP Bearer auth with OIDC access tokens, or basic identities using HTTP Basic auth. |
| 2 | Matches a basic identity with the username admin. |
| 3 | Matches an OIDC identity issued by the specified issuer. |
| 4 | Restricts the OIDC matches to tokens with the specified claims. |
| 5 | Grants the ability to publish attestations. |
| 6 | Grants the ability to perform policy evaluation. |
| 7 | Grants the ability to read attestations (e.g., via Fetch Attestation by ID API). |
| 8 | Resource matchers. |
| 9 | Resource matcher that matches all maven packages. |
| 10 | Resource matcher that matches a Develocity instance named my-develocity. |
| 11 | Resource matcher that matches all repositories in the Artifactory instance named my-artifactory. |
| 12 | Resource matcher that matches all resources in the S3 instance named my-s3-store. |
While Develocity Provenance Governor uses a single YAML file for access control policies, you can include multiple policies in that file using YAML’s --- document separator.
Resources and matches
Access to resources is controlled by using resource matchers in access control policies. Matchers may match one or more resources. They are string matchers that support wildcards. The resources used are:
| Resource Type | Resource specifier |
|---|---|
Packages |
The pURL of the package.
Supports wildcards (e.g., |
Artifactory instance |
|
Develocity instance |
|
S3 instance |
|
<instance-name> matches the name you configured for the instance (e.g. my-artifactory in artifactory.instances.my-artifactory.uri).
The portion after the instance name represents the resource path relative to that instance.
For Artifactory, you can restrict access to specific repositories.
For Develocity and S3, only the wildcard /* is currently supported for the resource path.
Basic identities
Basic identities may be configured by specifying the following sensitive application property:
basic.identities.<username>=<password>
<password> uses Spring Boot’s password format, so you can use {noop}password for plaintext passwords, or use a password encoder such as bcrypt.
Actuator Basic Identities
To secure the Spring Boot Actuator endpoints, you can configure separate basic identities:
actuator.basic.identities.<username>=<password>
<password> uses Spring Boot’s password format, so you can use {noop}password for plaintext passwords, or use a password encoder such as bcrypt.
Password Values
The password value uses Spring Security’s format with a prefix indicating the encoding:
-
{bcrypt}$2a$10$…- Bcrypt-hashed (recommended for production) -
{noop}mypassword- Plain text (only for testing, not recommended)
To generate a bcrypt password, use:
htpasswd -bnBC 10 "" password | tr -d ':\n'
2.3.6. Policies
Develocity Provenance Governor can be configured with policies to evaluate against packages.
See Writing Policies for details on writing policies.
Develocity Provenance Governor uses a single YAML file for policies, but it can include multiple policies by using YAML’s --- document separator.
2.3.7. Signing Keys
The signing key pair must be provided through a Kubernetes secret and mounted into the api container.
|
You should have already generated your signing key pair in the Prerequisites section. This section shows how to configure Develocity Provenance Governor to use those keys. |
Step 1: Organize Your Keys
Create a directory structure for your keys with descriptive names:
mkdir -p keys/
# Copy your generated keys with a timestamp or version identifier
cp key-pair.private-pem keys/signing.key.20250818123202.private-pem
cp key-pair.public-pem keys/signing.key.20250818123202.public-pem
Your directory should look like:
keys/ signing.key.20250818123202.private-pem signing.key.20250818123202.public-pem
|
Include a timestamp or version in key filenames to support key rotation.
The pattern |
Step 2: Create the Kubernetes Secret
Create a secret from your keys directory:
kubectl create secret generic keys \
--from-file="keys/" \
--namespace develocity-provenance-governor \
--dry-run=client -o yaml > keys-secret.yaml
# Review the generated secret
cat keys-secret.yaml
# Apply the secret
kubectl apply -f keys-secret.yaml
3. Using Develocity Provenance Governor
Before using Develocity Provenance Governor, verify your installation is working:
-
Check that all pods are running:
kubectl get pods -n develocity-provenance-governor -
Verify integrations are loaded by checking logs for 'support enabled' messages:
kubectl logs deployment/api -n develocity-provenance-governor | grep "support enabled" -
Confirm authentication is working with a test request (expect 401 Unauthorized if not authenticated, or 200 if authenticated):
curl -i http://your-dpg-hostname
Once verified, you can start publishing attestations and evaluating policies on software packages.
It is recommended to first start by publishing attestations for software packages. This creates the data foundation that policies will evaluate against. Once you are confident that attestations are being published correctly, you can start writing policies and evaluating them against software packages. This approach allows you to verify attestation generation is working before adding policy enforcement, reducing troubleshooting complexity.
3.1. Publishing Attestations
You can publish attestations for a software package by calling the Develocity Provenance Governor API directly, or by using the Develocity Provenance Governor GitHub Action.
In both cases, the following information will be needed:
| Information | Where to Find It | Example |
|---|---|---|
Package type |
Your build system or registry type |
|
Package name |
Artifact name from your build |
|
Package version |
Build version or tag |
|
SHA-256 digest |
Build output or registry metadata |
|
Repository URL |
Artifactory repository where package is stored |
|
Build Scan IDs or Query |
Develocity Build Scan URLs or advanced search |
|
3.1.1. When to Publish Attestations
You can publish attestations for a software package at any time after the package has been built and published to Artifactory.
This flexibility allows you to optimize your workflow:
-
Immediate publishing: Publish right after the build for complete tracking of all artifacts
-
Gated publishing: Only publish for packages that pass quality gates to reduce storage costs
-
Batch publishing: Collect multiple builds and publish attestations together for efficiency
Delaying attestations until it is known the software package will move forward in the software supply chain can help reduce the number of attestations that need to be stored and managed. For example, you may choose to only publish attestations for packages that have passed an initial quality gate.
For details on how to publish attestations using the Develocity Provenance Governor API, see _Appendix A: API Reference.
For details on how to publish attestations using the Develocity Provenance Governor GitHub Action, see GitHub Actions.
3.1.2. Attestation Types
Develocity Provenance Governor generates attestations which are compliant with the in-toto Attestations Framework. Each attestation is a JSON document wrapped in a signing envelope and has a subject and a predicate. The predicate has a type that describes the kind of attestation it is, as well as properties specific to that predicate type.
|
A more detailed description of the attestations published by Develocity Provenance Governor can be found in Appendix: Attestation Reference. |
The table below lists the types of attestations Develocity Provenance Governor can generate:
| Attestation | Predicate Type | Description |
|---|---|---|
Build Tool |
|
Attests to the build tool used to create the package, including its version. |
Java Toolchain |
|
Attests to the Java toolchain used to build the package, including the JDK version and vendor. |
Resolved Dependencies Repositories |
|
Attests to which repository resolved dependencies were fetched from when building the package. |
Resolved Dependencies |
|
Attests to the dependencies that were resolved when building the package. |
3.2. Writing Policies
Develocity Provenance Governor can evaluate policies against software packages to determine if they are compliant with your organization’s requirements. Develocity Provenance Governor uses the published attestations to evaluate policies.
|
Policies evaluate against the attestations generated by Develocity Provenance Governor. If you need to enforce requirements not covered by the current attestation types, contact support to discuss your use case. |
Develocity Provenance Governor uses declarative YAML policies instead of code-based policy languages (like Rego in Open Policy Agent). This makes policies easier to read, write, and version control alongside your deployment configuration.
3.2.1. Policy Architecture Overview
Before writing policies, it’s important to understand how the different components work together:
Example Flow:
# 1. Write a policy with a label
apiVersion: policy.gradle.com/v1
kind: JavaToolchain
metadata:
name: require-java-21
labels:
policy.my-corp.com/gate: production # <-- Label
spec:
matchingStrategy: must-match
toolchains:
- vendor: Eclipse Adoptium
versions: ["21.0.5"]
# 2. Create a PolicyScanDefinition that selects policies by label
---
apiVersion: policy.gradle.com/v1
kind: PolicyScanDefinition
metadata:
name: production-gate
spec:
description: Policies for production deployments
policy-selector:
match-labels:
policy.my-corp.com/gate: production # <-- Selects all policies with this label
# 3. Execute the scan via API (references the PolicyScanDefinition name)
# POST /packages/maven/com.example/my-app/1.0.0/policy-scans/production-gate
This architecture allows you to:
-
Write policies once and reuse them across multiple scans
-
Create different policy sets for different environments (dev, staging, production)
-
Organize policies by team, compliance requirement, or deployment stage
-
Change which policies are enforced without modifying individual policies
3.2.2. Types of policies
The table below lists the kinds of policies you can define in Develocity Provenance Governor:
| Policy | Policy Kind | Description |
|---|---|---|
Build Tool Policy |
|
Checks for the use of a specific build tool and version. Can be used to deny use of specific build tools or versions, or require the use of a specific build tool or version. |
Java Toolchain |
|
Checks for the use of a specific Java toolchain (JDK version and vendor). Can be used to deny use of specific JDK versions or vendors, or require the use of a specific JDK version or vendor. |
Resolved Dependency Repository |
|
Checks for the use of specific repositories to resolve dependencies. Can be used to prevent the use of specific repositories, or require the use of a specific repository. |
Package URL |
|
Checks for the use of specific dependencies. Can be used to prevent the use of specific dependencies, or require the use of a specific version of a dependency. |
Attestations Exist Policy |
|
Checks for the presence of specific types of attestations for a package. Can be used to ensure that required metadata (e.g., build tool info) has been captured. |
Attestation Signature Verification Policy |
|
Checks the signatures on attestations to verify they were signed with a trusted key and that the attestation has not been tampered with. |
The following sections describe each policy type in more detail, along with examples of how to write them.
3.2.3. General policy structure
All policies share a common structure.
Policies are modeled as Kubernetes-style custom resources, with apiVersion, kind, metadata, and spec properties.
|
Policies are not true Kubernetes resources, but the format makes them recognizable to those familiar with Kubernetes. |
The following snippet demonstrates the general structure used by all policies:
apiVersion: policy.gradle.com/v1
kind: TypeOfPolicy (1)
metadata: (2)
name: friendly-name (3)
labels: (4)
policy.my-corp.com/java-21: enforcement-policies
policy.my-corp.com/owner: DevSecOps
spec:
description: Human-readable description of the policy. (5)
remediation: Human-readable remediation steps if the policy is violated. (6)
matchingStrategy: must-match or none-match (7)
resultsLabels: (8)
policy.my-corp.com/cvss: <critical|high|medium|low> # common for vulnerability management
policy.my-corp.com/severity: <minor|sever|warning> # common for compliance and regulatory enforcement / risk assessment
policy.my-corp.com/dry-run: true # domain specific
policy.my-corp.com/severity-level: <SEV-1,SEV-2,SEV-3> # common for incident management
| 1 | The kind property defines the type of policy. This will drive what properties are required in the spec property. |
| 2 | The metadata.* properties are similar to traditional Kubernetes metadata information which includes name, labels, annotations. |
| 3 | The name property is a unique identifier for the policy. |
| 4 | Policies that set metadata.labels can be selected by policy scans for inclusion if the label satisfies the selector defined. A policy can be included in multiple policy scans. See Evaluating Policies for more information. |
| 5 | The spec.description explains in human-readable terms what the policy is attesting to about a software package. |
| 6 | The spec.remediation explains in human-readable terms what will be reported back in the policy scan and what needs done to the software package to make the package compliant. |
| 7 | The spec.matchingStrategy defines how the policy values are evaluated against the attestation data:
|
| 8 | The spec.resultsLabels allows the policy author to define key value pairs to write to the policy results. See Result labels for more information. |
|
Each kind of policy defines policy-specific properties (under the |
|
You can include multiple policies in a single YAML file by separating them with |
Result labels
Result labels are useful when policies are evaluated as part of a policy scan.
The labels can be used to categorize and filter policy scan results based on severity, compliance level, or other criteria relevant to your organization’s governance processes. For example, you might use result labels to indicate the severity of a policy violation (e.g., critical, high, medium, low), the type of compliance issue (e.g., security, license, quality), or the team responsible for addressing the issue (e.g., frontend, backend, devops).
3.2.4. BuildTool
A BuildTool policy enforces which build tools are allowed or disallowed in builds. Specific tools and versions can be required or prohibited.
This policy operates on the https://gradle.com/attestation/build-tool/v1 predicate type.
kind: BuildTool
apiVersion: policy.gradle.com/v1
metadata:
name: example-build-tool-policy
labels:
policy.my-corp.com/gate: build
annotations:
createdBy: admin
spec:
resultsLabels:
policy.my-corp.com/gate: build
description: Require specific build tools and versions for CI pipelines
remediation: Use an approved build tool and version
matchingStrategy: must-match
buildTools:
- toolType: gradle
toolVersions:
- "7.6"
- "8.0"
agentVersions:
- "1.0"
- "2.0"
- toolType: maven
toolVersions:
- "3.8.6"
agentVersions:
- "1.0"
3.2.5. JavaToolchain
A JavaToolchain policy enforces which Java toolchains are allowed or disallowed in builds. Specific vendors and versions can be required or prohibited.
Scenario: Your organization has banned Oracle JDK due to licensing changes and requires all teams to use BellSoft Liberica JDK version 21.0.5 or later.
Solution: Create two policies - one to deny Oracle, one to require BellSoft:
apiVersion: policy.gradle.com/v1
kind: JavaToolchain
metadata:
name: none-match-oracle-java-toolchain
labels:
policy.my-corp.com/gate: build
spec:
resultsLabels:
policy.my-corp.com/gate: build
description: Disallow Oracle Java Toolchains in builds.
remediation: Update the Gradle build to use BellSoft Java toolchain.
matchingStrategy: none-match
toolchains:
- vendor: oracle
apiVersion: policy.gradle.com/v1
kind: JavaToolchain
metadata:
name: must-match-bellsoft-java-toolchain
labels:
policy.my-corp.com/gate: build
spec:
resultsLabels:
policy.my-corp.com/gate: build
description: Builds must use BellSoft Java toolchains in builds.
remediation: Update the build to use BellSoft Java toolchain.
matchingStrategy: must-match
toolchains:
- vendor: BellSoft Liberica
versions:
- 21.0.8
- 21.0.7
- 21.0.6
- 21.0.5
3.2.6. ResolvedDependenciesRepositories
A ResolvedDependenciesRepositories policy controls the dependency repositories which are allowed to be used to resolve and fetch dependencies.
This policy operates on the https://gradle.com/attestation/resolved-dependencies-repositories/v1 predicate type.
apiVersion: policy.gradle.com/v1
kind: ResolvedDependenciesRepositories
metadata:
name: must-match-repositories
labels:
policy.my-corp.com/gate: build
spec:
resultsLabels:
policy.my-corp.com/gate: build
description: Allow resolved dependencies repositories
remediation: Remove the offending repository from build configuration
matchingStrategy: must-match
uris:
- https://artifacts.example.com/maven2
- https://repo.grdev.net/artifactory/enterprise-libs-release-candidates-local/
- https://plugins.gradle.org/m2/
- https://repo.grdev.net/artifactory/public
3.2.7. PackageUrl
A PackageUrl policy controls which dependencies are allowed or disallowed in builds. This can be used to block or require specific libraries.
This policy operates on the https://gradle.com/attestation/resolved-dependencies/v1 predicate type.
Dependencies are identified using Package URLs (pURLs), which provide a standardized way to reference software packages across different ecosystems.
Package URL Pattern Syntax
Package URLs in policies support wildcard matching using the asterisk (*) character:
| Pattern | Matches | Example |
|---|---|---|
|
Exact package, any version (e.g., |
|
|
Exact package and version |
Only |
|
Exact package, wildcard patch version |
|
|
All artifacts in namespace |
|
|
All artifacts in namespace with specific version |
|
|
All Maven artifacts |
Any Maven package |
|
All packages in npm scope |
|
|
Wildcards ( |
apiVersion: policy.gradle.com/v1
kind: PackageUrl
metadata:
name: purls-none-match-lombok
labels:
policy.my-corp.com/gate: build
spec:
resultsLabels:
policy.my-corp.com/gate: build
description: Disallow lombok dependency
remediation: Remove lombok dependency from project
matchingStrategy: none-match
purls:
- pkg:maven/org.projectlombok/lombok
apiVersion: policy.gradle.com/v1
kind: PackageUrl
metadata:
name: none-match-purl-spring-beans-6.2.7
labels:
policy.my-corp.com/gate: build
spec:
resultsLabels:
policy.my-corp.com/gate: build
description: Find usages of spring 6.2.7
remediation: Upgrade dependency to spring 6.2.7
matchingStrategy: none-match
purls:
- pkg:maven/org.springframework/*@6.2.7
apiVersion: policy.gradle.com/v1
kind: PackageUrl
metadata:
name: must-match-approved-spring-versions
labels:
policy.my-corp.com/gate: build
spec:
resultsLabels:
policy.my-corp.com/gate: build
description: Ensure only approved patch versions of Spring Framework are used.
remediation: Upgrade or downgrade to an approved patch version (e.g., 6.0.10, 6.1.5).
matchingStrategy: must-match
purls:
- pkg:maven/org.springframework/spring-core@6.0.10
- pkg:maven/org.springframework/spring-beans@6.0.10
- pkg:maven/org.springframework/spring-web@6.0.10
- pkg:maven/org.springframework/spring-core@6.1.5
- pkg:maven/org.springframework/spring-beans@6.1.5
- pkg:maven/org.springframework/spring-web@6.1.5
3.2.8. AttestationsExist
An AttestationsExist policy operates on the predicateType field of all attestations associated with a software package.
This can be used to ensure that critical metadata, regardless of its origin, has been captured and published.
Develocity Provenance Governor currently generates attestations with the following predicate types:
apiVersion: policy.gradle.com/v1
kind: AttestationsExist
metadata:
name: require-build-tool-attestation
labels:
policy.my-corp.com/gate: build
spec:
resultsLabels:
policy.my-corp.com/gate: build
description: Require Build Tool attestation to be present.
remediation: Ensure the build publishes a Build Tool attestation.
expectedPredicates:
- https://gradle.com/attestation/build-tool/v1
3.2.9. TrustedPublicKeys
A TrustedPublicKeys policy defines which public keys are trusted for verifying attestation signatures. This policy is used to ensure that only attestations signed with approved keys are accepted during policy evaluation.
This policy operates on the attestation envelope and verifies the signature against the payload.
apiVersion: policy.gradle.com/v1
kind: TrustedPublicKeys
metadata:
name: trusted-keys
labels:
policy.my-corp.com/gate: build
spec:
description: For the Java 21 toolchain, validate attestation signatures
remediation: Make sure the attestations are signed with the expected key
keys:
my-signing-key: (1)
pem: |- (2)
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAVzZ+Xr2RqM6RvYcOmxPADdMi7u8pJ7L8Fv6HnQz/xJg=
-----END PUBLIC KEY-----
my-b64-key: (3)
pemBase64: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUNvd0JRWURLMlZ3QXlFQVZ6WitYcjJScU02UnZZY09teFBBRGRNaTd1OHBKN0w4RnY2SG5Rei94Smc9Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=
| 1 | A unique identifier for this key. Use a descriptive name that indicates its purpose or rotation date. |
| 2 | The public key in PEM format. Use this format when embedding the key directly in your policy file. |
| 3 | The public key in Base64-encoded PEM format. Use this format when the key is stored as a single-line string. |
The keys map allows you to specify one or more trusted public keys, either as PEM or Base64-encoded PEM.
This policy is typically referenced by label in a PolicyScanDefinition to enforce signature validation on attestations.
3.3. Evaluating Policies
Develocity Provenance Governor performs "Policy Scans" to evaluate policies against a software package. Policy Scans are defined declaratively in YAML using a PolicyScanDefinition. Policy Scan definitions are stored alongside policies.
Policy Scans execute during your CI/CD pipeline, typically:
-
After building and publishing a package
-
After attestations are published to Artifactory
-
Before promoting to the next environment
-
Before deploying to production
The scan results determine whether the pipeline proceeds or fails based on policy compliance.
When a Policy Scan is executed, Develocity Provenance Governor retrieves the relevant policies based on the label selectors defined in the PolicyScanDefinition. It then evaluates each policy against the attestations associated with the specified software package.
Policy scans help to create reusable, targeted validation strategies by grouping related policies together.
Choose a policy scan organization strategy:
| Strategy | Use When | Example Scans |
|---|---|---|
By Environment |
Different rules per deployment stage |
|
By Package Type |
Different rules per artifact type |
|
By Team |
Different teams have different rules |
|
By Compliance |
Regulatory requirements vary |
|
Most organizations combine strategies, for example: prod-backend-scan or staging-frontend-scan.
3.3.1. PolicyScanDefinition
A PolicyScanDefinition defines a PolicyScan and selects which policies to apply using label selectors. This allows grouping and targeting of policies for specific enforcement scenarios.
apiVersion: policy.gradle.com/v1
kind: PolicyScanDefinition
metadata:
name: build-gate
labels:
policy.my-corp.com/gate: build
spec:
description: Policy scan definition for the build gate
policy-selector:
match-labels:
policy.my-corp.com/gate: build
The above policy scan will include all policies labeled with policy.my-corp.com/gate: build.
3.4. Example: Build Gate Scenario
This example demonstrates a "Build Gate" scenario, where a set of policies are applied to an artifact. The goal is to ensure that any artifact passing this gate meets specific criteria, such as being built with a trusted build tool and having the necessary attestations.
Ensure all relevant policies are labeled with policy.my-corp.com/gate: build.
For example:
apiVersion: policy.gradle.com/v1
kind: BuildTool
metadata:
name: require-gradle
labels:
policy.my-corp.com/gate: build
spec:
# ... policy details ...
Create a PolicyScanDefinition that selects these policies.
apiVersion: policy.gradle.com/v1
kind: PolicyScanDefinition
metadata:
name: build-gate
spec:
description: Validate build standards for the build gate
policy-selector:
match-labels:
policy.my-corp.com/gate: build
Trigger the policy scan via the API to validate the package against the build gate requirements:
curl --request POST \
--url http://localhost:8080/packages/maven/com.example/my-app/1.0.0/policy-scans/build-gate \
--header 'authorization: Basic ...' \
--header 'content-type: application/json' \
--data '{ ... }'
3.5. Understanding Policy Scan Results
Policy scan results provide detailed information about which policies passed or failed for a package.
The format of the response depends on the Accept header provided in the request:
-
application/json: The server collects all results and returns them as a single JSON array. This is the default behavior. -
application/x-ndjson: The server streams individual results as Newline Delimited JSON objects as soon as they are available. This is recommended for CI/CD pipelines to provide immediate feedback and prevent timeouts.
3.5.1. HTTP Status Codes
| Status Code | Meaning |
|---|---|
|
Policy scan completed. Check |
|
Invalid request (missing required fields, invalid package URL format, etc.). |
|
Authentication credentials missing or invalid. |
|
Authenticated but not authorized to scan this package. |
|
Policy scan definition not found, or package/attestations not found. |
|
Server error during policy evaluation. |
3.5.2. Interpreting Results
Successful Scan (All Policies Pass):
When all policies pass, the API will return an array where each PolicyScan.Result object has a status of satisfied or not-applicable.
[
{
"labels": {
"policy.my-corp.com/gate": "build"
},
"status": "satisfied",
"policyUri": "/policies/BuildTool/only-gradle-builds",
"policyDescription": "Ensure only Gradle is used as the build tool.",
"policyRemediation": "Use Gradle as the build tool. Consult migration guides if necessary.",
"attestationStoreInstance": "jfrog-artifactory:prod",
"attestationStoreUri": "https://jfrog.example.com/artifactory/maven-releases/.evidence/maven-app-1.0.0-build-env.json",
"sourcedFromUri": "https://develocity.example.com/s/abcdef1234567",
"details": {
"buildTool": "Gradle",
"predicateType": "https://gradle.com/attestation/build-tool/v1"
}
},
{
"labels": {
"policy.my-corp.com/gate": "build"
},
"status": "satisfied",
"policyUri": "/policies/TrustedPublicKeys/release-pipeline-signer",
"policyDescription": "Verify all attestations are signed by the release pipeline's trusted key.",
"policyRemediation": "Ensure the attestation is signed by a trusted key from the release pipeline. Re-run the build if necessary.",
"attestationStoreInstance": "jfrog-artifactory:prod",
"attestationStoreUri": "https://jfrog.example.com/artifactory/maven-releases/.evidence/maven-app-1.0.0-release.json",
"sourcedFromUri": null,
"details": {
"signerKeyId": "abcd1234",
"predicateType": "https://in-toto.io/Statement/v0.1"
}
}
]
Failed Scan (One or More Policies Fail):
When one or more policies fail, the API will return an array that includes PolicyScan.Result objects with a status of unsatisfied.
[
{
"labels": {
"policy.my-corp.com/gate": "build"
},
"status": "unsatisfied",
"policyUri": "/policies/ResolvedDependenciesRepository/disallow-untrusted-repos",
"policyDescription": "Disallow untrusted Maven repositories.",
"policyRemediation": "Remove 'https://untrusted.repo.com/maven2' from your build's dependency repositories.",
"attestationStoreInstance": "jfrog-artifactory:prod",
"attestationStoreUri": "https://jfrog.example.com/artifactory/maven-releases/.evidence/maven-app-1.0.0-build.json",
"sourcedFromUri": "https://develocity.example.com/s/abcdef1234567",
"details": {
"untrustedRepository": "https://untrusted.repo.com/maven2",
"predicateType": "https://gradle.com/attestation/resolved-dependencies/v1"
}
},
{
"labels": {
"policy.my-corp.com/gate": "build"
},
"status": "satisfied",
"policyUri": "/policies/BuildTool/only-gradle-builds",
"policyDescription": "Ensure only Gradle is used as the build tool.",
"policyRemediation": "Use Gradle as the build tool. Consult migration guides if necessary.",
"attestationStoreInstance": "jfrog-artifactory:prod",
"attestationStoreUri": "https://jfrog.example.com/artifactory/maven-releases/.evidence/maven-app-1.0.0-build-env.json",
"sourcedFromUri": "https://develocity.example.com/s/abcdef1234567",
"details": {
"buildTool": "Gradle",
"predicateType": "https://gradle.com/attestation/build-tool/v1"
}
}
]
3.5.3. Using Results in CI/CD
In a CI/CD pipeline, you can use application/x-ndjson to stream the API response directly into jq. This allows you to filter for any policies with an unsatisfied status as they are returned, without waiting for the entire scan to complete.
#!/bin/bash
# Stream results and filter for unsatisfied policies
# We use curl with 'Accept: application/x-ndjson' to fetch the results as a stream.
# jq filters the stream for any object where .status == "unsatisfied"
FAILED_POLICIES=$(curl -s --request POST \
--url "http://dpg.example.com/packages/maven/com.example/my-app/1.0.0/policy-scans/build-gate" \
--header "authorization: Basic ${AUTH_TOKEN}" \
--header "Accept: application/x-ndjson" \
--header "content-type: application/x-www-form-urlencoded" \
--data-urlencode "sha256=..." \
--data-urlencode "repositoryUrl=..." \
| jq -c 'select(.status == "unsatisfied")')
if [ -n "$FAILED_POLICIES" ]; then
echo "Policy scan FAILED. The following policies were unsatisfied:"
echo "$FAILED_POLICIES" | jq .
exit 1
else
echo "Policy scan PASSED."
fi
|
Use the |
3.6. Integrating with Continuous Integration and Continuous Deployment systems
3.6.1. GitHub Actions
We provide GitHub Actions for easy integration with GitHub workflows.
The gradle/develocity-provenance-governor-actions/publish calls the publishing API to publish attestations for a package, and the gradle/develocity-provenance-governor-actions/enforce enforces that a policy scan passes for a package.
Both actions require a GitHub token to authenticate with the Develocity Provenance Governor API. You can base your access control policy on this token. For example:
apiVersion: policy.gradle.com/v1
kind: AccessControl
metadata:
name: example
spec:
identityMatchingStrategy:
withOidc:
- withIssuerUri: "https://token.actions.githubusercontent.com"
withClaims:
repository_owner: example-org
Example Publishing
uses: gradle/develocity-provenance-governor-actions/publish@main
with:
attestation-publisher-url: 'https://develocity-provenance-governor.example.com'
build-scan-ids: eo5xxyg3drtoc
build-scan-queries: 'value:"CI run=${{ github.run_id }}"'
subject-type: oci
subject-name: java-payment-calculator
subject-version: 1.2.3
subject-digest: 1a6b2bf83435f2a9ccd33519ad3e817bf79aee6af1c7a15d26d8a256bfa9cc94
subject-repository-url: develocitytia.jfrog.io/docker-trial
Example Enforcement
uses: gradle/develocity-provenance-governor-actions/enforce@main
with:
policy-evaluator-url: 'https://develocity-provenance-governor.example.com'
subject-type: oci
subject-name: java-payment-calculator
subject-version: 1.2.3
subject-digest: 1a6b2bf83435f2a9ccd33519ad3e817bf79aee6af1c7a15d26d8a256bfa9cc94
subject-repository-url: develocitytia.jfrog.io/docker-example-repo
policy-scan: ci-enforcement
3.6.2. Integration with Other CI/CD Systems
Develocity Provenance Governor exposes a REST API that can be used to publish attestations and evaluate policies. You can integrate with any CI/CD system that can make HTTP requests.
For example, you can use curl to publish attestations and evaluate policies from a shell script:
# Set up credentials
export DPG_URL="https://develocity-provenance-governor.example.com"
export DPG_AUTH=$(echo -n 'username:password' | base64)
# Publish attestations
curl -X POST "$DPG_URL/packages/maven/com.example/my-app/1.0.0/attestations" \
-H "Authorization: Basic $DPG_AUTH" \
-H "Content-Type: application/json" \
-d '{
"sha256": "73f482a2296dcc654c380979aae3916a1925ff906b1c43a0659f97fd56e44497",
"repositoryUrl": "artifactory.example.com/maven-repo-local",
"buildScan": {
"ids": ["bjvognekiphus"]
}
}'
See Appendix A: API Reference for complete details on the API endpoints and request/response formats, as well as example curl commands.
4. Troubleshooting
4.1. Operations
4.1.1. Updating Develocity Provenance Governor
To update to a new version:
-
Review the release notes for breaking changes and new features.
-
Update the container image in your deployment:
# Edit the deployment to use the new image version kubectl set image deployment/api api-http=registry.gradle.com/develocity/provenance-governor:NEW_VERSION@sha256:NEW_DIGEST \ -n develocity-provenance-governor # Watch the rollout kubectl rollout status deployment/api -n develocity-provenance-governor -
Verify the update:
# Check pod is running with new image kubectl get pods -n develocity-provenance-governor -o jsonpath='{.items[0].spec.containers[0].image}' # Check logs for successful startup kubectl logs deployment/api -n develocity-provenance-governor --tail=50
|
Before updating production, test the new version in a non-production environment with your existing configurations. |
4.1.2. Rolling Back to a Previous Version
If you encounter issues after an update:
-
Rollback the deployment:
kubectl rollout undo deployment/api -n develocity-provenance-governor -
Verify rollback succeeded:
kubectl rollout status deployment/api -n develocity-provenance-governor kubectl logs deployment/api -n develocity-provenance-governor --tail=50 -
Check rollout history:
kubectl rollout history deployment/api -n develocity-provenance-governor
4.1.3. Updating Configuration Without Downtime
Configuration changes (ConfigMaps, Secrets) require a pod restart to take effect:
-
Update the ConfigMap or Secret:
# For policies kubectl create configmap policies \ --from-file=policies/ \ --dry-run=client -o yaml | kubectl apply -f - # For application properties kubectl create configmap properties \ --from-file=application.properties \ --dry-run=client -o yaml | kubectl apply -f - # For secrets kubectl create secret generic secrets \ --from-file=secrets/ \ --dry-run=client -o yaml | kubectl apply -f - -
Restart the deployment:
kubectl rollout restart deployment/api -n develocity-provenance-governor kubectl rollout status deployment/api -n develocity-provenance-governor -
Verify configuration loaded:
kubectl logs deployment/api -n develocity-provenance-governor | grep "support enabled"
4.1.4. Managing Policies
Adding a new policy:
-
Add the policy YAML to your policies directory.
-
Update the policies ConfigMap.
-
Restart the deployment.
-
Verify the policy is loaded in the logs.
Disabling a policy temporarily:
Remove the labels that PolicyScanDefinitions use to select it. The policy will remain in the ConfigMap but won’t be evaluated.
Testing policy changes:
-
Create a new PolicyScanDefinition with a unique name for testing.
-
Use labels to select only the new/modified policies.
-
Test against known packages.
-
Once verified, update your production PolicyScanDefinition.
4.1.5. Backup and Restore
Backup your configuration:
# Export all configurations
kubectl get configmap policies -n develocity-provenance-governor -o yaml > policies-backup.yaml
kubectl get configmap properties -n develocity-provenance-governor -o yaml > properties-backup.yaml
kubectl get secret secrets -n develocity-provenance-governor -o yaml > secrets-backup.yaml
kubectl get secret license -n develocity-provenance-governor -o yaml > license-backup.yaml
Restore from backup:
kubectl apply -f policies-backup.yaml
kubectl apply -f properties-backup.yaml
kubectl apply -f secrets-backup.yaml
kubectl apply -f license-backup.yaml
kubectl rollout restart deployment/api -n develocity-provenance-governor
4.2. Common Issues
The following are some possible common issues, how they are recognized, and what might be needed to resolve them:
4.2.1. Invalid or missing Develocity license
The startup error clearly describes the action needed:
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to bind properties under 'develocity' to com.gradle.cavendish.dv.DevelocityProperties:
Reason: java.lang.IllegalArgumentException: No develocity.license provided in configuration.
Action:
Update your application's configuration
Ensure that the Develocity Provenance Governor deployment is configured with a valid Develocity license.
4.2.2. Missing Configuration properties
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to bind properties under 'develocity' to com.gradle.cavendish.dv.DevelocityProperties:
Reason: java.lang.IllegalArgumentException: Develocity Access Key `develocity.instances.grdev.access-key` must have text
Action:
Update your application's configuration
Double check to see that the property mentioned is spelled correctly and in the configuration.
4.2.3. Access control issues (403 or 401 responses)
Access control issues can be troublesome to track down as the system errors on the side of keeping sensitive information secure.
Follow these troubleshooting steps:
-
Verify your authentication is working:
kubectl logs deployment/api -n develocity-provenance-governor | grep 'Identity support enabled' -
Check that access control policies are loaded:
kubectl logs deployment/api -n develocity-provenance-governor | grep 'AccessControl' -
Verify resource matchers include trailing wildcards where needed:
-
Correct:
dv:my-instance/* -
Incorrect:
dv:my-instance(missing wildcard)
-
-
Start with a permissive policy for testing:
withResources: - "*:*/*" # Allows access to all resourcesOnce authentication is working, tighten the policy to specific resources.
-
Enable debug logging temporarily to see policy evaluation details (consult support for instructions).
4.2.4. Invalid access keys for external integrations (e.g. Develocity and Artifactory)
2025-10-16T17:51:58.967-06:00 ERROR 70284 --- [ctor-http-nio-2] c.g.c.dv.DevelocityAccessTokenManager : Error refreshing access tokens org.springframework.web.reactive.function.client.WebClientResponseException$Unauthorized: 401 Unauthorized from POST https://develocity.grdev.net/api/auth/token ...
Again, the logging should be fairly clear in this respect. The above, for example, illustrates Develocity Provenance Governor cannot communicate with Develocity. The corrective action here would be to examine the access key in the configuration that is defined for accessing Develocity and make sure it is correct.
4.2.5. Policies don’t seem to be used
2025-10-16T17:51:58.098-06:00 INFO 70284 --- [ restartedMain] c.g.c.p.PolicyScanDefinitionEvaluator : Policy Scan support enabled for [java-21-toolchain] including policy [none-match-oracle-java-toolchain] 2025-10-16T17:51:58.098-06:00 INFO 70284 --- [ restartedMain] c.g.c.p.PolicyScanDefinitionEvaluator : Policy Scan support enabled for [java-21-toolchain] including policy [must-match-bellsoft-java-toolchain] 2025-10-16T17:51:58.099-06:00 INFO 70284 --- [ restartedMain] c.g.c.p.PolicyScanDefinitionEvaluator : Policy Scan support enabled for [java-21-toolchain] including policy [purls-none-match-lombok] 2025-10-16T17:51:58.099-06:00 INFO 70284 --- [ restartedMain] c.g.c.p.PolicyScanDefinitionEvaluator : Policy Scan support enabled for [java-21-toolchain] including policy [none-match-purl-spring-beans-6.2.7] 2025-10-16T17:51:58.100-06:00 INFO 70284 --- [ restartedMain] c.g.c.p.PolicyScanDefinitionEvaluator : Policy Scan support enabled for [java-21-toolchain] including policy [must-match-repositories]
When evaluating policies, check startup logs to see which policies loaded. Each enabled policy scan logs its included policies. If your expected policy is missing:
-
Check the policy name matches exactly (case-sensitive)
-
Verify policy labels match the scan’s label selector
-
Confirm the policy YAML is in the 'policies' ConfigMap:
kubectl get configmap policies -n develocity-provenance-governor -o yaml -
Look for parsing errors in earlier log messages:
kubectl logs deployment/api -n develocity-provenance-governor | grep -i error
4.2.6. Policy Writing and Debugging
My policy doesn’t match anything
Symptoms: Policy scan shows PASSED for a none-match policy even though the forbidden item exists, or FAILED for a must-match policy even though the required item exists.
Common Causes:
-
Incorrect pURL format - Check the exact format of the dependency in attestations:
# View attestations to see exact pURL format curl -H "Authorization: Basic ..." \ "http://dpg.example.com/packages/maven/com.example/my-app/1.0.0/attestations" -
Case sensitivity - Package names and vendors are case-sensitive:
-
Wrong:
pkg:maven/Org.Example/artifact -
Right:
pkg:maven/org.example/artifact
-
-
Version mismatch - If you specify a version in the pURL, it must match exactly:
-
pkg:maven/org.example/artifact@1.0.0does NOT match1.0.0-SNAPSHOT -
Use wildcards if you want to match all versions:
pkg:maven/org.example/artifact
-
-
Namespace separator - For Maven, use
/not.:-
Wrong:
pkg:maven/org.example.subpackage/artifact -
Right:
pkg:maven/org.example/subpackage-artifact
-
Understanding why a policy failed
To debug policy failures:
-
Check the policy scan results - The failure message includes which attestation values caused the failure:
{ "policyName": "must-match-java-21", "status": "FAILED", "message": "Found Java vendor: Oracle Corporation, version: 17.0.8", "remediation": "Update to Java 21" } -
Review attestation data - Compare what the policy expects vs. what’s in the attestations:
# Get all attestations for the package curl -H "Authorization: Basic ..." \ "http://dpg.example.com/packages/maven/com.example/my-app/1.0.0/attestations" | jq . -
Check predicate types - Ensure your policy operates on the correct predicate type:
-
BuildTool→https://gradle.com/attestation/build-tool/v1 -
JavaToolchain→https://gradle.com/attestation/java-toolchains/v1 -
PackageUrl→https://gradle.com/attestation/resolved-dependencies/v1
-
Policy is not being evaluated
Symptoms: Policy exists but doesn’t appear in scan results.
Troubleshooting steps:
-
Verify policy is loaded:
kubectl logs deployment/api -n develocity-provenance-governor | grep "Policy Scan support enabled" -
Check label selectors match:
Your policy must have labels that match the PolicyScanDefinition selector:
Policy:
metadata: name: my-policy labels: policy.my-corp.com/gate: buildPolicyScanDefinition:
spec: policy-selector: match-labels: policy.my-corp.com/gate: build # Must match! -
Verify YAML syntax:
# Check for YAML parsing errors kubectl get configmap policies -n develocity-provenance-governor -o yaml
Testing policies before deployment
Before deploying policies to production:
-
Start with
none-matchpolicies - These are easier to debug because they fail when unexpected items are found. -
Test against known packages - Run scans against packages you’ve already built and know the contents of.
-
Use descriptive names - Name policies clearly:
block-oracle-jdkis better thanjava-policy-1. -
Add detailed remediation - Include specific steps in the
remediationfield to help developers fix issues. -
Test both pass and fail cases - Verify the policy fails when it should and passes when it should.
4.3. Additional Help
If you experience issues with Develocity Provenance Governor or related components, please submit a support ticket at support.gradle.com, including details of the issue and an attached support bundle from Develocity, if applicable.
Appendix A: API Reference
A.1. Publish
Creates attestations from build information and published them to the attestation store.
A.1.1. Path
-
/packages/{type}/{namespace}/{name}/{version}/attestations -
/packages/{type}/{name}/{version}/attestations(when namespace is absent)
A.1.3. Produces
-
application/json(Buffered: Returns a JSON array containing all results) -
application/x-ndjson(Streaming: Returns Newline Delimited JSON objects as they become available)
A.1.4. Request Body
The request body must be a JSON object (for application/json) or form fields (for form-encoded/multipart) matching the following structure:
{
"sha256": "73f482a2296dcc654c380979aae3916a1925ff906b1c43a0659f97fd56e44497", (1)
"repositoryUrl": "artifactory.example.com/maven-repo-local", (2)
"buildScan": {
"ids": [ (3)
"1234567890aaaa",
"22222bbb"
],
"queries": [ (4)
"project:my-app value:\"CI Provider=GitHub Actions\" tag:CI tag:3.4.x"
]
}
}
| 1 | The SHA-256 hash of the package. |
| 2 | The repository the package is located in. |
| 3 | A list of Build Scan IDs to retrieve information from. Optional. |
| 4 | A list of queries to retrieve Build Scan data, using the Develocity advanced search syntax. Optional. |
A.1.5. Responses
-
200 OK: ReturnsPolicyScan.Resultobjects.-
If
Accept: application/json(default): Returns a JSON array of results. -
If
Accept: application/x-ndjson: Returns a stream of newline-delimited JSON objects.
-
-
400 Bad Request: The request content or criteria is invalid. -
401 Unauthorized: The credential information presented (if any) is not allowed access to this endpoint. -
403 Forbidden: The endpoint is accessible (authorized) but access to the resource requested is disallowed. -
500 Internal Server Error: An error occurred during attestation publishing.
A.1.6. Example Request
curl --verbose --request POST "http://localhost:8080/packages/oci/testifact/1.0/attestations" \
--header "Content-Type: application/json" \
--header "Accept: application/json, application/problem+json" \
--header "Authorization: Basic $(echo -n 'admin:password' | base64)" \
--data '
{
"sha256": "df9b564df8de07153224faa58da743ca6bc281610a6dbbec772535de19bd06e2",
"repositoryUrl": "develocitytia.jfrog.io/docker-trial",
"buildScan": {
"ids": [
"bjvognekiphus"
],
"queries": [
]
}
}
curl --verbose --request POST "http://localhost:8080/packages/oci/testifact/1.0/attestations" \
--header "Content-Type: application/x-www-form-urlencoded" \
--header "Accept: application/json, application/problem+json" \
--header "Authorization: Basic $(echo -n 'admin:password' | base64)" \
--data-urlencode "sha256=df9b564df8de07153224faa58da743ca6bc281610a6dbbec772535de19bd06e2" \
--data-urlencode "repositoryUrl=develocitytia.jfrog.io/docker-trial" \
--data-urlencode "buildScan.ids=bjvognekiphus"
curl --verbose --request POST "http://localhost:8080/packages/oci/testifact/1.0/attestations" \
--header "Accept: application/json, application/problem+json" \
--header "Authorization: Basic $(echo -n 'admin:password' | base64)" \
--form "sha256=df9b564df8de07153224faa58da743ca6bc281610a6dbbec772535de19bd06e2" \
--form "repositoryUrl=develocitytia.jfrog.io/docker-trial" \
--form "buildScan.ids=bjvognekiphus"
curl --verbose --request POST "http://localhost:8080/packages/maven/com.example/my-maven-app/1.0.0/attestations" \
--header "Content-Type: application/json" \
--header "Accept: application/json, application/problem+json" \
--header "Authorization: Basic $(echo -n 'admin:password' | base64)" \
--data '
{
"sha256": "df9b564df8de07153224faa58da743ca6bc281610a6dbbec772535de19bd06e2",
"repositoryUrl": "https://repo.maven.apache.org/maven2",
"buildScan": {
"ids": [
"bjvognekiphus"
],
"queries": [
]
}
}
curl --verbose --request POST "http://localhost:8080/packages/maven/com.example/my-maven-app/1.0.0/attestations" \
--header "Content-Type: application/x-www-form-urlencoded" \
--header "Accept: application/json, application/problem+json" \
--header "Authorization: Basic $(echo -n 'admin:password' | base64)" \
--data-urlencode "sha256=df9b564df8de07153224faa58da743ca6bc281610a6dbbec772535de19bd06e2" \
--data-urlencode "repositoryUrl=https://repo.maven.apache.org/maven2" \
--data-urlencode "buildScan.ids=bjvognekiphus"
curl --verbose --request POST "http://localhost:8080/packages/maven/com.example/my-maven-app/1.0.0/attestations" \
--header "Accept: application/json, application/problem+json" \
--header "Authorization: Basic $(echo -n 'admin:password' | base64)" \
--form "sha256=df9b564df8de07153224faa58da743ca6bc281610a6dbbec772535de19bd06e2" \
--form "repositoryUrl=https://repo.maven.apache.org/maven2" \
--form "buildScan.ids=bjvognekiphus"
A.2. Evaluate
A.2.1. Path
-
/packages/{type}/{namespace}/{name}/{version}/policy-scans/{policy-scan-name} -
/packages/{type}/{name}/{version}/policy-scans/{policy-scan-name}(when namespace is absent)
A.2.3. Produces
-
application/json(Buffered: Returns a JSON array containing all results) -
application/x-ndjson(Streaming: Returns Newline Delimited JSON objects as they become available)
A.2.4. Request Body
The request body must be a JSON object matching the following structure:
{
"sha256": "string", (1)
"repositoryUrl": "string" (2)
}
| 1 | The SHA-256 hash of the package. |
| 2 | The repository the package is located in. |
A.2.5. Responses
-
200 OK: ReturnsPolicyScan.Resultobjects.-
If
Accept: application/json(default): Returns a JSON array of results. -
If
Accept: application/x-ndjson: Returns a stream of newline-delimited JSON objects.
-
-
400 Bad Request: The request content or criteria is invalid. -
401 Unauthorized: Authentication not allowed. -
403 Forbidden: Access to specific resource disallowed. -
404 Not Found: The specified resource was not found. -
429 Too Many Requests: The service is overloaded. -
500 Internal Server Error: An error occurred communicating with an external service.
A.2.6. Example Request
curl --request POST \
--url http://localhost:8080/packages/oci/testifact/2.0/policy-scans/java-21-toolchain \
--header 'authorization: Basic ***********=' \
--header 'content-type: application/json' \
--data '{
"sha256": "3f6cabfb527d740e79e0f49e2f4e564279d8c74a0bfc4481fc3a44e6b085fe91",
"repositoryUrl": "example.com/docker-trial"
}'
curl --request POST \
--url http://localhost:8080/packages/oci/testifact/2.0/policy-scans/java-21-toolchain \
--header 'authorization: Basic ***********=' \
--header 'content-type: application/x-www-form-urlencoded' \
--data-urlencode "sha256=3f6cabfb527d740e79e0f49e2f4e564279d8c74a0bfc4481fc3a44e6b085fe91" \
--data-urlencode "repositoryUrl=example.com/docker-trial"
curl --request POST \
--url http://localhost:8080/packages/oci/testifact/2.0/policy-scans/java-21-toolchain \
--header 'authorization: Basic ***********=' \
--form "sha256=3f6cabfb527d740e79e0f49e2f4e564279d8c74a0bfc4481fc3a44e6b085fe91" \
--form "repositoryUrl=example.com/docker-trial"
curl --request POST \
--url http://localhost:8080/packages/maven/com.example/my-maven-app/1.0.0/policy-scans/java-21-toolchain \
--header 'authorization: Basic ***********=' \
--header 'content-type: application/json' \
--data '{
"sha256": "3f6cabfb527d740e79e0f49e2f4e564279d8c74a0bfc4481fc3a44e6b085fe91",
"repositoryUrl": "https://repo.maven.apache.org/maven2"
}'
curl --request POST \
--url http://localhost:8080/packages/maven/com.example/my-maven-app/1.0.0/policy-scans/java-21-toolchain \
--header 'authorization: Basic ***********=' \
--header 'content-type: application/x-www-form-urlencoded' \
--data-urlencode "sha256=3f6cabfb527d740e79e0f49e2f4e564279d8c74a0bfc4481fc3a44e6b085fe91" \
--data-urlencode "repositoryUrl=https://repo.maven.apache.org/maven2"
curl --request POST \
--url http://localhost:8080/packages/maven/com.example/my-maven-app/1.0.0/policy-scans/java-21-toolchain \
--header 'authorization: Basic ***********=' \
--form "sha256=3f6cabfb527d740e79e0f49e2f4e564279d8c74a0bfc4481fc3a44e6b085fe91" \
--form "repositoryUrl=https://repo.maven.apache.org/maven2"
A.2.7. Example Response
[
{
"labels": {
"policy.my-corp.com/gate": "build"
},
"status": "unsatisfied",
"policyUri": "/policies/ResolvedDependenciesRepository/disallow-untrusted-repos",
"policyDescription": "Disallow untrusted Maven repositories.",
"policyRemediation": "Remove 'https://untrusted.repo.com/maven2' from your build's dependency repositories.",
"attestationStoreInstance": "jfrog-artifactory:prod",
"attestationStoreUri": "https://jfrog.example.com/artifactory/maven-releases/.evidence/maven-app-1.0.0-build.json",
"sourcedFromUri": "https://develocity.example.com/s/abcdef1234567",
"details": {
"untrustedRepository": "https://untrusted.repo.com/maven2",
"predicateType": "https://gradle.com/attestation/resolved-dependencies/v1"
}
},
{
"labels": {
"policy.my-corp.com/gate": "build"
},
"status": "unsatisfied",
"policyUri": "/policies/PackageUrl/disallow-vulnerable-dependency",
"policyDescription": "Disallow usage of specific vulnerable package versions.",
"policyRemediation": "Upgrade 'pkg:maven/org.example/bad-lib@1.0.0' to a non-vulnerable version (e.g., 1.0.1 or higher).",
"attestationStoreInstance": "jfrog-artifactory:prod",
"attestationStoreUri": "https://jfrog.example.com/artifactory/maven-releases/.evidence/maven-app-1.0.0-sbom.json",
"sourcedFromUri": "https://develocity.example.com/s/abcdef1234567",
"details": {
"vulnerablePackage": "pkg:maven/org.example/bad-lib@1.0.0",
"predicateType": "https://cyclonedx.org/schema/sbom/v1.4"
}
},
{
"labels": {
"policy.my-corp.com/gate": "build"
},
"status": "satisfied",
"policyUri": "/policies/TrustedPublicKeys/release-pipeline-signer",
"policyDescription": "Verify all attestations are signed by the release pipeline's trusted key.",
"policyRemediation": "Ensure the attestation is signed by a trusted key from the release pipeline. Re-run the build if necessary.",
"attestationStoreInstance": "jfrog-artifactory:prod",
"attestationStoreUri": "https://jfrog.example.com/artifactory/maven-releases/.evidence/maven-app-1.0.0-release.json",
"sourcedFromUri": null,
"details": {
"signerKeyId": "abcd1234",
"predicateType": "https://in-toto.io/Statement/v0.1"
}
},
{
"labels": {
"policy.my-corp.com/gate": "build"
},
"status": "not-applicable",
"policyUri": "/policies/JavaToolchains/disallow-jdk8",
"policyDescription": "Disallow Java 8 toolchains in new builds.",
"policyRemediation": "Update build configuration to use Java 11 or higher.",
"attestationStoreInstance": "jfrog-artifactory:prod",
"attestationStoreUri": "https://jfrog.example.com/artifactory/maven-releases/.evidence/maven-app-1.0.0-build-env.json",
"sourcedFromUri": "https://develocity.example.com/s/abcdef1234567",
"details": {
"message": "Attestation does not contain 'https://gradle.com/attestation/java-toolchains/v1' predicate. Policy is not applicable."
}
},
{
"labels": {
"policy.my-corp.com/gate": "build"
},
"status": "satisfied",
"policyUri": "/policies/BuildTool/only-gradle-builds",
"policyDescription": "Ensure only Gradle is used as the build tool.",
"policyRemediation": "Use Gradle as the build tool. Consult migration guides if necessary.",
"attestationStoreInstance": "jfrog-artifactory:prod",
"attestationStoreUri": "https://jfrog.example.com/artifactory/maven-releases/.evidence/maven-app-1.0.0-build-env.json",
"sourcedFromUri": "https://develocity.example.com/s/abcdef1234567",
"details": {
"buildTool": "Gradle",
"predicateType": "https://gradle.com/attestation/build-tool/v1"
}
}
]
A.3. Fetch Attestation by ID
Retrieves a specific attestation by its unique identifier.
A.3.1. Path
-
/packages/{type}/{namespace}/{name}/{version}/{digestType}:{digest}/attestations/{instance}/{id} -
/packages/{type}/{name}/{version}/{digestType}:{digest}/attestations/{instance}/{id}(when namespace is absent)
Path Variables
-
{type}: The package type (e.g.,oci,maven) -
{namespace}: The package namespace or organization (may be omitted) -
{name}: The package name -
{version}: The package version -
{digestType}: The digest algorithm (e.g.,sha256) -
{digest}: The digest value -
{instance}: The storage instance identifier (e.g.,s3:prod-bucket) -
{id}: The unique identifier of the attestation
A.3.2. Produces
-
application/json(Buffered: Returns a JSON array containing the attestation) -
application/x-ndjson(Streaming: Returns Newline Delimited JSON objects)
A.3.3. Responses
-
200 OK: Returns the attestation envelope. -
400 Bad Request: The request parameters are invalid. -
401 Unauthorized: Authentication not allowed. -
403 Forbidden: Access to the resource is disallowed. -
404 Not Found: The attestation was not found.
Appendix B: Attestation Reference
Develocity Provenance Governor publishes several attestations to Artifactory’s evidence store. All attestations are in-toto attestations.
The following predicate types are published, when the source Build Scan contains the relevant data:
Each section below describes the predicate payload (the JSON object found in the predicate field of an in-toto Statement whose predicateType matches the given URI).
The examples show only the predicate object (not the full in-toto Statement wrapper) for brevity.
B.1. Build Tool Predicate
Predicate Type URI: https://gradle.com/attestation/build-tool/v1
Describes the build tool that ran the build.
Fields:
-
buildId(string) - The build ID, used to identify the build scan. -
buildScanUri(URI string) - Link to the Develocity Build Scan that produced this attestation. -
toolType(string) - Build tool type. Typical values:gradle,maven(future tools may appear). -
toolVersion(string) - Version of the build tool (e.g.8.10,3.9.6, etc.). -
agentVersion(string) - Version of the Develocity build agent / plugin that captured data.
{
"buildId": "g4b7d8c1m2",
"buildScanUri": "https://develocity.example.com/s/g4b7d8c1m2",
"toolType": "gradle",
"toolVersion": "8.10",
"agentVersion": "3.17"
}
B.2. Java Toolchains Predicate
Predicate Type URI: https://gradle.com/attestation/java-toolchains/v1
Captures the Java toolchains used during the build.
Fields:
-
buildScanUri(URI string) - Link to the Build Scan. -
toolchains(array of objects) - List of observed toolchains.-
vendor(string) - Vendor identifier/name (e.g.Eclipse Adoptium,Oracle Corporation). -
versions(array of strings) - List of Java runtime versions used (e.g.17.0.8).
-
{
"buildScanUri": "https://develocity.example.com/s/g4b7d8c1m2",
"toolchains": [
{
"vendor": "Eclipse Adoptium",
"versions": [
"17.0.8"
]
}
]
}
B.3. Resolved Dependencies Repositories Predicate
Predicate Type URI: https://gradle.com/attestation/resolved-dependencies-repositories/v1
Enumerates a repository consulted for dependency resolution. A single attestation will be produced that contains multiple Uniform Resource Identifiers (URIs) for all of the repositories used in the same build.
Fields:
-
buildScanUri(URI string) - Link to the Build Scan. -
uris(List of URI string) - Repository base URIs from which dependencies were resolved.
{
"buildScanUri": "https://develocity.example.com/s/g4b7d8c1m2",
"uris": [
"https://repo1.maven.org/maven2/",
"https://repo.gradle.org/gradle/libs-releases-local/",
"https://jitpack.io/"
]
}
B.4. Resolved Dependencies Predicate
Predicate Type URI: https://gradle.com/attestation/resolved-dependencies/v1
Contains the set of resolved third-party dependencies as pURLs.
Fields:
-
buildScanUri(URI string) - Link to the Build Scan for correlation. -
purls(array of strings) - Distinct, sorted list of pURLs. May be empty.
Example (truncated list):
{
"buildScanUri": "https://develocity.example.com/s/g4b7d8c1m2",
"purls": [
"pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.17.1",
"pkg:maven/org.apache.commons/commons-lang3@3.14.0",
"pkg:npm/react@18.3.1"
]
}