What it does
Every call toGET /api/v1/decisions/:id/provenance returns a
canonicalised provenance bundle (decision trace + model snapshots +
audit chain + SLSA attestation + fairness slice). When the deployment
is configured for sigstore signing, the bundle is also signed with
cosign sign-blob and the detached signature lands in the response
header:
Configuration
Env vars on the runtime container:
Optional sigstore-transparency env vars (Rekor upload, Fulcio OIDC) are
honoured but not required — the V1 wire ships offline-key signing
only. Keyless / Fulcio / OIDC support is a follow-up.
How the signing works
lib/supply-chain/cosign-sign-blob.ts invokes:
--output-signature (all three
variants of it fail in the container: - becomes a literal filename
on a read-only cwd, and /dev/stdout / /proc/self/fd/1 raise ENXIO
under Alpine/Go). --tlog-upload=false skips the Sigstore Rekor
transparency log: the V1 wire is offline-key signing; operators who
want tlog can enable it out-of-band via cosign’s own env vars.
Three discipline rules are enforced by code:
spawnonly — neverexec. No shell is invoked, so there is no command-injection surface. The full argv is a fixed array of constants.- The signing key never appears on argv. Cosign reads the key
bytes from the env var named by the
env://COSIGN_KEYURI; only the URI string lives in argv. - The blob payload arrives via stdin. No tempfile, no argv leak. The canonical-JSON bundle is piped to the child’s stdin and the stream is closed immediately.
env://COSIGN_KEY),
never through arguments.
Failure modes (fail-soft to “unsigned”)
Stderr from cosign is captured (capped at 8 KB) and logged to the
application logs (the first 256 chars, via the structured logger) for
operator debugging. The audit row records the typed
signatureFailureCode only — cosign diagnostics never reach the
audit log, the response body, or the response headers.
Image layout
The runtime stage ofplatform/Dockerfile installs cosign:
cosign verify-blob against the sigstore root after image build, as a
separate CI step. That bootstrap is operator-authorised follow-up.
Installing cosign signing
Bundles are returned unsigned until you provide a signing key. There are exactly two supported install paths — pick whichever matches your deployment topology. Both paths set the same two env vars (COSIGN_KEY, COSIGN_PASSWORD) on the runtime container; what differs is where those values live at rest.Path A — Cloud (AWS Secrets Manager)
Use this on App Runner / ECS / EKS / any AWS-resident deployment. Keys live encrypted in Secrets Manager and the runtime resolves them at container start. Steps:cosign generate-key-pair(one time) →cosign.key+cosign.pub.aws secretsmanager create-secret --name <prefix>/cosign-key --secret-string file://cosign.keyaws secretsmanager create-secret --name <prefix>/cosign-password --secret-string '<passphrase>'- Grant the runtime instance role
secretsmanager:GetSecretValueon those two ARNs (least-privilege; one inline policy is enough). - Wire the ARNs as runtime secrets (App Runner runtime-env-secrets
block, ECS task-definition secrets list, or EKS
valueFrom.secretKeyRef). - Commit
cosign.pubtokaireonai-docs/security/cosign.pubso downstream verifiers can validate signatures offline.
Path B — Self-host / local (key file on disk)
Use this for VM, on-prem, Docker Compose, or single-host installs.cosign generate-key-pair(one time) →cosign.key+cosign.pub.- Mount
cosign.keyinto the container (read-only,0400):Or read at boot: - Restart the container.
- Distribute
cosign.pubto every party that needs to verify bundles (CI, audit reviewers, downstream consumers).
Required. Production deployments must install cosign signing
via Path A or Path B. Running without it leaves every provenance
bundle marked X-Provenance-Signature: unsigned, which downstream
verifiers reject. The fail-soft is intentional (a missing key never
breaks the response) but is not a substitute for installation.
Verifying signing is live
- Hit
/api/v1/decisions/:id/provenanceand confirm the response includesX-Provenance-Signature: <base64>(notunsigned). - Verify offline:
- Audit-log spot-check:
auditLog.changes.signedshould betruefor every successful bundle. AsignatureFailureCodefield means the wire intended to sign but couldn’t — page whoever owns the image / key-management layer.
Roadmap
- Keyless signing (Fulcio + OIDC + Rekor upload) so the deployment does not need a long-lived private key.
- Image-time
cosign verify-blobof the cosign release artefact itself (closes the bootstrap-trust gap noted above). - Reproducible-build verification step that re-canonicalises the bundle and re-signs against a CI-only key, comparing digests.