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.3. Flow
Below is a typical flow for using Develocity Provenance Governor in a software supply chain:
-
A build runs on a Continuous Integration System and:
-
Produces a software package (for example, a JAR file).
-
Publishes a Build Scan to Develocity.
-
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 Continuous Integration or Continuous Deployment 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 with Kubernetes and Helm deployment options, making it effortless to integrate into modern cloud-native environments.
2.1. Prerequisites
2.1.1. Develocity License
Using Develocity Provenance Governor requires a valid Develocity license. 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”.
You do not need any particular license entitlements to use Develocity Provenance Governor, any valid Develocity license will work.
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 Provenance Governor outside of Kubernetes please open a support ticket and describe your deployment requirements. |
2.1.3. Cluster Resources
Ensure the Kubernetes cluster has sufficient resources to run Develocity Provenance Governor. By default, the combined Kubernetes resource requests are 2 CPUs and 6 GiB memory; limits are 6 CPUs and 6 GiB memory. These values are configurable via Helm values.
2.1.4. Artifactory with Evidence Management
Develocity Provenance Governor relies on Artifactory to store and manage provenance data. It requires network access to an Artifactory instance.
This Artifactory instance must have the Evidence Management feature enabled, with support for external evidence. This requires an Artifactory version of 7.104.2 or later, and an Enterprise+ Artifactory license.
Develocity Provenance Governor requires an Artifactory Access Token or Identity Token. It must have Read and Annotate permissions for the repository you wish to use Develocity Provenance Governor with.
2.1.5. Develocity Instance
Develocity Provence 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
Signing keys are used to sign attestations when publishing them, and to validate their signatures when using them to validate policies. Develocity Provenance Governor needs to be configured with a signing key pair in PEM format. The public key of this pair must be configured in the Artifactory instance attestations will be published to.
We support Ed25519, RSA, and Elliptic Curve keys, but recommend using Ed25519 for its better security and performance.
A key pair can be generated using OpenSSL:
openssl genpkey -algorithm Ed25519 -out ./key-pair.private-pem
openssl pkey -in ./key-pair.private-pem -pubout -out ./key-pair.public-pem
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
To enable SSL, you need to have a TLS certificate for Develocity Provenance Governor’s hostname. While SSL is technically optional, it is strongly recommended.
2.2. Application Configuration
Configuration for Develocity Provenance Governor is provided via its Helm values.yaml file. There are several sources of configuration:
-
Application properties - a properties file with various application settings.
-
Sensitive application properties - a properties file with sensitive settings, such as access tokens or keys.
-
Access control policies - YAML documents defining access control policies.
-
Policies - YAML documents defining policies to enforce on packages.
2.2.1. 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 a name you choose to identify this Develocity instance. |
Provide a Develocity access key in the sensitive application properties:
develocity.instances.<instance-name>.access-key=************ (your access key here)
2.2.2. 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)
2.2.3. 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.
oapiVersion: 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)
withClaim: (4)
organization: "my-org"
canPerform:
- publish-attestations (5)
- publish-policy-scans (6)
withResources: (7)
- pkg:maven/* (8)
- dv:my-develocity (9)
- af:my-artifactory/* (10)
| 1 | Matchers for identities to apply this policy to. May match OIDC 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 | Resource matchers. |
| 8 | Resource matcher that matches all maven packages. |
| 9 | Resource matcher that matches a Develocity instance named my-develocity. |
| 10 | Resource matcher that matches all repositories in the Artifactory instance named my-artifactory. |
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 resource. They are string matchers that support wildcards. The resources used are:
| Resource Type | Resource specifier |
|---|---|
|
Packages |
The pURL of the package. |
|
Artifactory instance |
|
|
Develocity instance |
|
<instance-name/instance-uri> means that resource matchers may match either the name you configured for the instance, or the URI of the instance.
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.
2.2.4. 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.2.5. Signing Keys
The signing key pair is provided directly as Helm values.
2.3. 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.3.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.3.2. Deployment Flow Overview
The recommended order is:
-
Choose / create a Kubernetes cluster (local k3d or existing)
-
Create namespace + required secrets (registry + license)
-
Deploy the core workload (ServiceAccount, Services, Deployment, NetworkPolicy)
-
Verify rollout & logs
-
(Optional) Configure Ingress (HTTP, then HTTPS + cert-manager)
-
Create placeholder Secrets/ConfigMaps for dynamic configuration (
secrets,properties,policies) -
Add integrations (Develocity instances, Artifactory instances)
-
Add signing keys
-
Enable authentication (Basic and/or OIDC)
-
Define policies (AccessControl manifests)
-
Roll out config changes as needed
-
Troubleshoot rollout issues
2.3.3. 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.
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. Registry + License secrets
LICENSE=$(echo -n "ge:$(cat ./develocity.license)" | base64 | tr -d '\n')
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. Deploy core components (set version + digest)
kubectl --namespace develocity-provenance-governor apply -f - <<EOF
apiVersion: v1
kind: Namespace
metadata:
labels:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
app.kubernetes.io/part-of: develocity-provenance-governor
app.kubernetes.io/version: 1.0.1
name: develocity-provenance-governor
---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
app.kubernetes.io/part-of: develocity-provenance-governor
app.kubernetes.io/version: 1.0.1
name: api
namespace: develocity-provenance-governor
---
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
app.kubernetes.io/part-of: develocity-provenance-governor
app.kubernetes.io/version: 1.0.1
name: api-http
namespace: develocity-provenance-governor
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: http
selector:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
---
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
app.kubernetes.io/part-of: develocity-provenance-governor
app.kubernetes.io/version: 1.0.1
name: api-http-monitoring
namespace: develocity-provenance-governor
spec:
ports:
- name: http-monitoring
port: 9090
protocol: TCP
targetPort: http-monitoring
selector:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
app.kubernetes.io/part-of: develocity-provenance-governor
app.kubernetes.io/version: 1.0.1
name: api
namespace: develocity-provenance-governor
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
template:
metadata:
labels:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
spec:
containers:
- image: registry.gradle.com/develocity/provenance-governor:1.0.1@sha256:d50cc7c50a3d02d723f065b3f192274210764ff96fcee6626a0481208bd3d3fa
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 9090
name: api-http
ports:
- containerPort: 8080
name: http
- containerPort: 9090
name: http-monitoring
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 9090
resources:
limits:
cpu: 2000m
memory: 1Gi
requests:
cpu: 2000m
memory: 1Gi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
volumeMounts:
- mountPath: /workspace/config/license
name: license
readOnly: true
- mountPath: /workspace/config/secrets
name: secrets
readOnly: true
- mountPath: /workspace/config/policies
name: policies
readOnly: true
- mountPath: /workspace/config/properties
name: properties
readOnly: true
- mountPath: /tmp
name: tmp
readOnly: false
imagePullSecrets:
- name: registry-secret
securityContext:
fsGroup: 65534
fsGroupChangePolicy: Always
runAsGroup: 65534
runAsNonRoot: true
runAsUser: 65534
seccompProfile:
type: RuntimeDefault
serviceAccountName: api
volumes:
- configMap:
name: policies
optional: true
name: policies
- configMap:
name: properties
optional: true
name: properties
- name: secrets
secret:
optional: true
secretName: secrets
- name: license
secret:
optional: true
secretName: license
- emptyDir: {}
name: tmp
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
labels:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
app.kubernetes.io/part-of: develocity-provenance-governor
app.kubernetes.io/version: 1.0.1
name: api
namespace: develocity-provenance-governor
spec:
ingress:
- from:
- namespaceSelector: {}
podSelector: {}
ports:
- port: 8080
protocol: TCP
podSelector:
matchLabels:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
policyTypes:
- Ingress
EOF
# 5. Verify rollout
kubectl rollout status deployment/api --namespace develocity-provenance-governor --timeout=90s
# 6. Check logs
kubectl logs deployment/api --namespace develocity-provenance-governor | head -n 50
# 7. (Optional) 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
# 8. Test route (expect 401 Unauthorized – no credentials yet)
curl -i api.127.0.0.1.nip.io
Move on to the detailed sections below to extend functionality (authentication, policies, signing, external integrations).
2.3.4. Select Kubernetes Distribution
Before proceeding ensure access to a Kubernetes cluster has been provided, otherwise a local k3d cluster is also supported.
2.3.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 "ge:$(cat ./develocity.license)" | base64 | tr -d '\n')
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
Develocity License Secret (Required)
A Develocity license is required for the deployment to be successful.
kubectl create secret generic license \
--namespace develocity-provenance-governor \
--from-file=develocity.license=./develocity.license
2.3.6. Deploy Core Components
Apply the workload manifests. (See Quick Start)
Details
kubectl --namespace develocity-provenance-governor apply -f - <<EOF
apiVersion: v1
kind: Namespace
metadata:
labels:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
app.kubernetes.io/part-of: develocity-provenance-governor
app.kubernetes.io/version: 1.0.1
name: develocity-provenance-governor
---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
app.kubernetes.io/part-of: develocity-provenance-governor
app.kubernetes.io/version: 1.0.1
name: api
namespace: develocity-provenance-governor
---
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
app.kubernetes.io/part-of: develocity-provenance-governor
app.kubernetes.io/version: 1.0.1
name: api-http
namespace: develocity-provenance-governor
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: http
selector:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
---
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
app.kubernetes.io/part-of: develocity-provenance-governor
app.kubernetes.io/version: 1.0.1
name: api-http-monitoring
namespace: develocity-provenance-governor
spec:
ports:
- name: http-monitoring
port: 9090
protocol: TCP
targetPort: http-monitoring
selector:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
app.kubernetes.io/part-of: develocity-provenance-governor
app.kubernetes.io/version: 1.0.1
name: api
namespace: develocity-provenance-governor
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
template:
metadata:
labels:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
spec:
containers:
- image: registry.gradle.com/develocity/provenance-governor:1.0.1@sha256:d50cc7c50a3d02d723f065b3f192274210764ff96fcee6626a0481208bd3d3fa
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 9090
name: api-http
ports:
- containerPort: 8080
name: http
- containerPort: 9090
name: http-monitoring
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 9090
resources:
limits:
cpu: 2000m
memory: 1Gi
requests:
cpu: 2000m
memory: 1Gi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
volumeMounts:
- mountPath: /workspace/config/license
name: license
readOnly: true
- mountPath: /workspace/config/secrets
name: secrets
readOnly: true
- mountPath: /workspace/config/policies
name: policies
readOnly: true
- mountPath: /workspace/config/properties
name: properties
readOnly: true
- mountPath: /tmp
name: tmp
readOnly: false
imagePullSecrets:
- name: registry-secret
securityContext:
fsGroup: 65534
fsGroupChangePolicy: Always
runAsGroup: 65534
runAsNonRoot: true
runAsUser: 65534
seccompProfile:
type: RuntimeDefault
serviceAccountName: api
volumes:
- configMap:
name: policies
optional: true
name: policies
- configMap:
name: properties
optional: true
name: properties
- name: secrets
secret:
optional: true
secretName: secrets
- name: license
secret:
optional: true
secretName: license
- emptyDir: {}
name: tmp
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
labels:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
app.kubernetes.io/part-of: develocity-provenance-governor
app.kubernetes.io/version: 1.0.1
name: api
namespace: develocity-provenance-governor
spec:
ingress:
- from:
- namespaceSelector: {}
podSelector: {}
ports:
- port: 8080
protocol: TCP
podSelector:
matchLabels:
app.kubernetes.io/component: backend
app.kubernetes.io/name: api
policyTypes:
- Ingress
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.3.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.3.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.3.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.3.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 (fixing prior typo artfiactory):
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.3.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.
Elliptic Curve (prime256v1)
openssl ecparam -name prime256v1 -genkey -noout -out ec_private.key
openssl pkcs8 -topk8 -inform PEM -in ec_private.key -outform PEM -nocrypt -out private-key.pem
openssl ec -in ec_private.key -pubout -out public-key.pem
Expected log algorithm: SHA256withECDSA (previous version incorrectly stated RSA).
RSA (2048)
openssl genrsa -out private-key.pem 2048
openssl rsa -in private-key.pem -pubout -out public-key.pem
Expected log algorithm: SHA256withRSA (previous version incorrectly stated ECDSA).
2.3.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]
Enable OIDC Authentication
OIDC Providers are dynamically discovered based on token issuers specified in policies. See the Policies section for examples.
2.3.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.3.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.3.15. 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 |
Next steps: Integrate with CI/CD to rotate credentials, implement key rotation schedule, and enforce least-privilege policies.
3. Using Develocity Provenance Governor
Once Develocity Provenance Governor is installed and configured, you can start using it to publish attestations and evaluate policies on software packages.
It is recommended to first start by publishing attestations for software packages. Once you are confident that attestations are being published correctly, you can start writing policies and evaluating them against software packages.
3.1. Publishing Attestations
You can publish attestations for a software package by calling the Provenance Governor API directly, or by using the Provenance Governor GitHub Action.
In both cases, the following information will be needed:
-
Identifiers for the software package (which is the "subject" of the attestations):
-
The type of the software package (for example,
ocifor an OCI image, ormavenfor a Maven package). -
The name of the software package
-
The version of the software package
-
The SHA-256 digest of the software package
-
The Artifactory repository URL where the software package was published
-
-
A list of Build Scan identifiers from Develocity that were used to build the software package, or an advanced query to find the associated build in formation in Develocity.
|
You can publish attestations for a software package at any time after the package has been built and published to Artifactory. This allows you delay publishing attestations until it is known the software package will move forward in the software supply chain. For example, you may choose to only publish attestations for packages that have passed an initial quality gate. Doing so can help reduce the number of attestations that need to be stored and managed. |
For details on how to publish attestations using the Provenance Governor API, see _Appendix A: API Reference.
For details on how to publish attestations using the Provenance Governor GitHub Action, see GitHub Actions.
3.1.1. 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 Dependency Repository |
|
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 can only be applied to the attestation types generated by Develocity Provenance Governor. |
Unlike other policy engines, Develocity Provenance Governor policies are defined declaratively using YAML and are loaded into the system via configuration. This makes it easy to read and write policies, and to version control them alongside your deployment code.
3.2.1. 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. |
|
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.2. General policy structure
All policies share a common structure. Policies are modeled as Kubernetes-style custom resources, with apiVersion, kind, metadata, and spec properties.
|
Polices 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
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)
cvss: <critical|high|medium|low> # common for vulnerability management
severity: <minor|sever|warning> # common for compliance and regulatory enforcement / risk assessment
dry-run: true # domain specific
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.3. BuildTool
A BuildTool policy enforces which build tools are allowed or disallowed in builds. Specific tools and versions can be required or prohibited.
kind: BuildTool
apiVersion: policy.gradle.com/v1
metadata:
name: example-build-tool-policy
labels:
environment: ci
team: backend
annotations:
createdBy: admin
spec:
resultsLabels:
severity: high
category: 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.4. JavaToolchain
A JavaToolchain policy enforces which Java toolchains are allowed or disallowed in builds. Specific vendors and versions can be required or prohibited.
apiVersion: policy.gradle.com/v1
kind: JavaToolchain
metadata:
name: none-match-oracle-java-toolchain
labels:
java-21: toolchain
java-17: toolchain
owner: DevSecOps
spec:
labels:
devsecops.gradle.com/compliance/severity: critical
description: Disallow Oracle Java Toolchains in builds.
remediation: Update the Gradle build to use BellSoft Java toolchain.
matching-strategy: none-match
toolchains:
- vendor: oracle
apiVersion: policy.gradle.com/v1
kind: JavaToolchain
metadata:
name: must-match-bellsoft-java-toolchain
labels:
java-21: toolchain
java-17: toolchain
owner: DevSecOps
spec:
labels:
devsecops.gradle.com/compliance/severity: critical
description: Builds must use BellSoft Java toolchains in builds.
remediation: Update the build to use BellSoft Java toolchain.
matching-strategy: must-match
toolchains:
- vendor: BellSoft Liberica
versions:
- 21.0.8
- 21.0.7
- 21.0.6
- 21.0.5
3.2.5. ResolvedDependenciesRepositories
A ResolvedDependenciesRepositories policy controls the dependency repositories which are allowed to be used to resolve and fetch dependencies.
apiVersion: policy.gradle.com/v1
kind: ResolvedDependenciesRepositories
metadata:
name: must-match-repositories
labels:
java-21: ""
java-17: deny-purl
owner: DevSecOps
spec:
labels:
devsecops.gradle.com/compliance/severity: critical
dry-run: true
description: Allow resolved dependencies repositories
remediation: Remove the offending repository from build configuration
matching-strategy: 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.6. PackageUrl
A PackageUrl policy controls which dependencies are allowed or disallowed in builds. This can be used to block or require specific libraries.
Dependencies are identified using Package URLs (pURLs), which provide a standardized way to reference software packages across different ecosystems.
apiVersion: policy.gradle.com/v1
kind: PackageUrl
metadata:
name: purls-none-match-lombok
labels:
java-21: toolchain
java-17: toolchain
owner: DevSecOps
spec:
labels:
devsecops.gradle.com/compliance/severity: critical
dry-run: true
description: Disallow lombok dependency
remediation: Remove lombok dependency from project
matching-strategy: 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:
java-21: toolchain
java-17: toolchain
owner: DevSecOps
spec:
labels:
devsecops.gradle.com/compliance/severity: critical
dry-run: true
description: Find usages of spring 6.2.7
remediation: Upgrade dependency to spring 6.2.7
matching-strategy: none-match
purls:
- pkg:maven/org.springframework/*@6.2.7
3.2.7. 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.
apiVersion: policy.gradle.com/v1
kind: TrustedPublicKeys
metadata:
name: trusted-keys
labels:
java-21: toolchain
owner: DevSecOps
spec:
description: For the Java 21 toolchain, validate attestation signatures
remediation: Make sure the attestations are signed with the expected key
keys:
my-test-key:
pem: |-
-----BEGIN PUBLIC KEY-----
REPLACE THIS LINE WITH THE INTERNALS OFF THE PUBLIC KEY
-----END PUBLIC KEY-----
my-testb64-key:
pemBase64:
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 stored alongside policies.
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 enforcement strategies by grouping related policies together. There are many different ways to organize policy scans. For example, you might create scans based on:
-
The type of software package (for example,
OCIorMaven). -
The environment the package is intended to be deployed to (for example,
Development,Staging, orProduction). -
The organizational unit or team responsible for the package (for example,
Frontend, orBackend). -
The department or business function the package supports (for example,
Finance,HR, orMarketing).
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: java-21-toolchain
labels:
java-21: toolchain
owner: DevSecOps
spec:
description: Policy scan definition for Java 21 toolchain
policy-selector:
match-labels:
java-21: toolchain
The above policy scan will include all policies labeled with java-21: toolchain.
3.4. Integrating with Continuous Integration and Continuous Deployment systems
3.4.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 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.4.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.
See Appendix A: _API Reference for details on the API endpoints and request/response formats, as well as example curl commands.
4. Troubleshooting
4.1. Common Issues
The following are some possible common issues, how they are recognized, and what might be needed to resolve them:
4.1.1. Invalid or missing Develocity license
The startup error clearly describe 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.1.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.1.3. Access control issues (403 or 401 responses)
These types of issues can be troublesome to track down as the system errs on the side of keeping sensitive information secure. One strategy to mitigate these sorts of problems is to being with a permissive access control policy and being to lock it down after other integration issues are working. Also, make sure to double check that the trailing wildcards are not missing in the resource identifiers.
4.1.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.1.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, if expected behaviors from policies are not seen, make sure the expected policies are loaded. By looking at the startup logs, the policies loaded will be logged. If the one in question is not in that list, that would be a problem.
4.2. 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)
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
A.1.2. Consumes
-
application/json -
application/x-www-form-urlencoded -
multipart/form-data
A.1.3. Produces
-
application/json
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)
"develocity": {
"buildScanIds": [ (3)
"1234567890aaaa",
"22222bbb"
],
"buildScanQueries": [ (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: Returns aPublishAttestation.SuccessfulResponseobject in JSON format. -
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 -v -X POST "http://localhost:8080/packages/oci/testifact/1.0/attestations" \
-H "Content-Type: application/json" \
-H "Accept: application/json, application/problem+json" \
-H "Authorization: Basic $(echo -n 'admin:password' | base64)" \
-d '
{
"sha256": "df9b564df8de07153224faa58da743ca6bc281610a6dbbec772535de19bd06e2",
"repositoryUrl": "develocitytia.jfrog.io/docker-trial",
"buildScan": {
"ids": [
"bjvognekiphus"
],
"queries": [
]
}
}
A.1.7. Example Response
{
"status": "SUCCESS",
"attestationUri": "/attestations/789",
"details": {
"store": "attestation-store-1"
}
}
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)
Path Variables
-
{type}: The package type (e.g.,oci,maven) -
{namespace}: The package namespace or organization (may be omitted for some package types) -
{name}: The package name -
{version}: The package version -
{policy-scan-name}: The name of the policy scan to execute
A.2.2. Consumes
-
application/json
A.2.3. Produces
-
application/json
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: Returns a stream (Flux) ofPolicyScan.Resultobjects in JSON format. -
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"
}'
A.2.7. Example Response
[
{
"status": "unsatisfied",
"policyUri": "/policies/ResolvedDependenciesRepository/must-match-repositories",
"policyDescription": "Allow resolved dependencies repositories",
"policyRemediation": "Remove the offending repository from build configuration",
"attestationStoreInstance": "af:tia",
"attestationStoreUri": "https://develocitytia.jfrog.io/artifactory/docker-trial/.evidence/c35e1a4fecb35e4f7a829b2ae3d4bea93ad66247a062dda4b3301df79d73f5f8/3f6cabfb527d740e79e0f49e2f4e564279d8c74a0bfc4481fc3a44e6b085fe91/gradle-resolved-dependencies-repository-1759517425992-af494c3b.json",
"sourcedFromUri": "http://localhost:24300/s/wyqqaj64qu73k",
"details": {
"repositoryUri": "https://repo.maven.apache.org/maven2"
}
},
{
"status": "not-applicable",
"policyUri": "/policies/PackageUrl/purls-none-match-lombok",
"policyDescription": "Disallow lombok dependency",
"attestationStoreInstance": "af:tia",
"attestationStoreUri": "https://develocitytia.jfrog.io/artifactory/docker-trial/.evidence/c35e1a4fecb35e4f7a829b2ae3d4bea93ad66247a062dda4b3301df79d73f5f8/3f6cabfb527d740e79e0f49e2f4e564279d8c74a0bfc4481fc3a44e6b085fe91/gradle-build-tool-1759517425991-dd3a6aab.json",
"sourcedFromUri": "http://localhost:24300/s/wyqqaj64qu73k",
"details": {
"message": "Envelope does not contain a [https://gradle.com/attestation/resolved-dependencies/v1] predicate type."
}
},
{
"status":"satisfied",
"policyUri":"/policies/JavaToolchains/none-match-oracle-java-toolchain",
"policyDescription":"Disallow Oracle Java Toolchains in builds.",
"attestationStoreInstance":"af:tia",
"attestationStoreUri":"https://develocitytia.jfrog.io/artifactory/docker-trial/.evidence/fab0e045e6304acb00818728589b9350deb3b3f9d8787b0af984ae1062d35126/3f6cabfb527d740e79e0f49e2f4e564279d8c74a0bfc4481fc3a44e6b085fe91/gradle-java-toolchains-1762553480138-c74a20cb.json",
"sourcedFromUri": "http://localhost:24300/s/wyqqaj64qu73k",
"details":{
}
}
]
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/attestations/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 Toolchain Predicate
Predicate Type URI: https://gradle.com/attestations/java-toolchain/v1
Captures the Java runtime used during the build. Multiple attestations of this predicate type may be produced (one per distinct Java runtime) for the same build.
Fields:
-
buildScanUri(URI string) - Link to the Build Scan. -
version(string) - Java runtime version string (e.g.17.0.8). May be empty if unavailable. -
vendor(string) - Vendor identifier/name (e.g.Eclipse Adoptium,Oracle Corporation). May be empty.
{
"buildScanUri": "https://develocity.example.com/s/g4b7d8c1m2",
"version": "17.0.8",
"vendor": "Eclipse Adoptium"
}
B.3. Resolved Dependencies Repositories Predicate
Predicate Type URI: https://gradle.com/attestations/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/attestations/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"
]
}
Appendix C: Release history
1.0.1
-
[NEW] Dependency updates
1.0.0
-
Initial release of Develocity Provenance Governor.