The Evidence Server¶
The Evidence Server lets places the operator cannot watch, such as CI runners or edge sites, send signed reports about a service into your cluster. Pacto checks each report against the contract and stores it in your registry. Evidence here means a record of what a tool saw in a running system.
It is off by default (evidence.enabled: false). Turning it on needs three
things the chart cannot guess. Settle the first before you publish anything: it
decides which registry the contract itself has to live in.
Check the registry serves the native Referrers API¶
Each report is stored next to the contract version it is about, in the same
registry repository, as an OCI 1.1 referrer (an artifact that points at
another artifact). Pacto does not fall back to the older tag-based scheme, so
the registry holding the contract must support Referrers lookup. GHCR does not qualify — which matters here more than anywhere
else on this site, because every other page publishes to ghcr.io — and neither
does CNCF distribution (registry:2, registry:3); see
Evidence in OCI. Publishing the
contract to a conformant registry is a decision to make before pacto push, not
after helm install.
Create the trust store¶
pacto evidence keygen mints an Ed25519 pair and
names the public key after the trust binding the server reads —
<producerId>__<keyId>.pub, or a bare <keyId>.pub when there is a single
producer:
The Secret is mounted whole and read-only at /etc/pacto/trust, so each
Secret key has to be the public-key filename — which is exactly what
--from-file gives you. One --from-file per trusted producer:
kubectl create secret generic pacto-evidence-trust \
--namespace pacto-operator-system \
--from-file=keys/acme-ci__release-2026.pub
The private .key stays with the producer that signs; the cluster never needs
it. Evidence security covers rotation and
multi-producer trust.
Get the subject digest¶
A subject is one immutable contract revision, and
pacto push prints the digest of the revision it just published:
Pushed payments-api@2.1.0 -> registry.example.com/your-org/your-service-pacto:2.1.0
Digest: sha256:<64 hex characters>
Then install:
helm install pacto-operator \
oci://ghcr.io/trianalab/pacto/charts/pacto-operator \
--namespace pacto-operator-system --create-namespace \
--set evidence.enabled=true \
--set 'evidence.registry.subjects[0]=oci://registry.example.com/your-org/your-service-pacto@sha256:<digest>' \
--set evidence.trust.existingSecret=pacto-evidence-trust
- At least one subject.
evidence.registry.subjectslists the exact, immutable contract revisions evidence may be reported against, each anoci://<repo>@sha256:<digest>reference. The chart's schema rejects an empty list, sohelm installfails before anything reaches the cluster:at '/evidence/registry/subjects': minItems: got 0, want 1. It rejects a short or tag-shaped reference the same way — the digest has to be all 64 hex characters. - A trust store.
evidence.trust.existingSecretnames the Secret you created above. The chart does not check this one, so an install without it succeeds and the operator then exits at startup withevidence enabled but no trust secret set: signature verification is mandatory. Verification is never optional. - A registry that serves the native Referrers API, as above. Nothing checks it at install time: the chart installs, the operator starts and the failure surfaces later as an Evidence Server that never becomes ready.
If that registry is private, there is a fourth thing you create yourself: a
kubernetes.io/dockerconfigjson Secret named by
evidence.registry.credentialsSecret, mounted read-only as a DOCKER_CONFIG
directory so the server authenticates the way pacto pull does. Leave the value
empty for an anonymous or in-cluster registry.
See the Helm reference for the full value list and Operator configuration for the controller flags each value maps to.
Verifying the install¶
evidence.enabled=true adds a third Deployment, pacto-evidence, created by
the controller the same way the dashboard is:
NAME READY UP-TO-DATE AVAILABLE AGE
pacto-dashboard 1/1 1 1 16s
pacto-evidence 1/1 1 1 14s
pacto-operator 1/1 1 1 21s
1/1 means more than "the process started". Readiness is
GET /api/evidence/v1/ready, which answers 503 until every subject
resolves in the registry and answers native Referrers discovery. A
pacto-evidence stuck at 0/1 is nearly always a subject the cluster cannot
pull or a registry without the Referrers API — read its own log, not the
controller's:
The Service is pacto-evidence on port 8686. Producers inside the cluster
POST signed envelopes to it:
A pacto fleet outside the cluster consumes the same server's read-only
contribution by base URL — --evidence-url appends
/api/evidence/v1/targets itself, so do not include it:
kubectl port-forward -n pacto-operator-system svc/pacto-evidence 8686:8686 &
pacto fleet search --evidence-url http://127.0.0.1:8686
Nothing durable lives in the cluster: no PersistentVolumeClaim, no data volume, because the registry is the store. Delete and recreate the Deployment and the accepted evidence is still there. Evidence in OCI covers what is written and where; the ingestion API lists all five endpoints.