> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kaireonai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Decision Provenance Bundle

> GET /api/v1/decisions/:id/provenance returns a canonicalized JSON bundle of trace + model snapshots + cascade trace + audit chain + build identity, with a digest header and a real cosign signature when COSIGN_KEY is configured.

## Endpoint

`GET /api/v1/decisions/:id/provenance` — admin-gated.

## Response

```jsonc theme={null}
// X-Provenance-Schema: kaireon.provenance.v1
// X-Provenance-Digest: <sha256 hex>
// X-Provenance-Signature: <base64 | "unsigned">
{
  "schemaVersion": "kaireon.provenance.v1",
  "generatedAt": "...",
  "decisionTrace": { /* PII-redacted DecisionTrace */ },
  "modelSnapshots": [
    { "modelId": "...", "modelType": "gradient_boosted", "modelStateHashSha256": "..." }
  ],
  "effectiveRulesByOffer": {
    "OFFER_X": { /* lib/effective-rules.ts result with explainCascade trace */ }
  },
  "auditLog": [
    { "id": "...", "action": "...", "prevHash": "...", "integrityHash": "...", "timestamp": "..." }
  ],
  "buildIdentity": {
    "gitSha": "...",
    "gitRepo": "...",
    "imageName": "...",
    "imageDigest": "...",
    "sbomDigestSha256": "...",
    "slsaAttestation": { /* in-toto v1 statement when env vars set */ }
  },
  "fairnessSlice": { /* latest fairness_evaluate / fairness_report row */ }
}
```

## Why a separate endpoint

The bundle is a **point-in-time snapshot** of every artifact a regulator
or auditor needs to reconstruct one decision: the trace itself, the
model versions referenced, the rule cascade that produced the candidate
set, the audit chain, the build identity (so they can verify the code
that ran), and the fairness slice in effect at the time.

Returning these via the existing
`/decision-traces/:id` route would either bloat the trace endpoint or
require N+1 fetches. The bundle endpoint runs all the joins server-side
and emits a stable, hash-friendly document.

## Reproducible digest

Output is canonicalized — JSON with sorted object keys — so the
`X-Provenance-Digest` is deterministic. The same trace re-emitted
from any pod produces the same digest. Verifiers can re-fetch the
bundle days later and confirm bit-identity.

## Cosign signing

When the **COSIGN\_KEY** env var is set, the canonical bundle bytes are
signed by spawning the real `cosign sign-blob` binary (installed in the
platform Docker image) and the detached base64 signature lands in
`X-Provenance-Signature`. The earlier deterministic placeholder
(hash-of-hash) has been removed. Full mechanics — argv, failure modes,
timeouts, key handling — are on
[Provenance signing with cosign](/governance-security/provenance-cosign).

The header value `"unsigned"` indicates the bundle was generated
without a usable signing path (no **COSIGN\_KEY**, missing binary, or a
signing failure — the audit row records the typed reason). Consumers
can use this signal to refuse unsigned bundles in compliance
workflows.

## Build identity inputs

The route reads these env vars and includes them in `buildIdentity`:

| Env                                      | Default if unset           | Purpose                                   |
| ---------------------------------------- | -------------------------- | ----------------------------------------- |
| `GIT_SHA`                                | `null`                     | Commit SHA the running pod was built from |
| `GIT_REPO`                               | `null`                     | Repository URL                            |
| `IMAGE_NAME`                             | `null`                     | OCI image reference                       |
| `IMAGE_DIGEST`                           | `null`                     | OCI image digest (sha256:...)             |
| `SBOM_DIGEST_SHA256`                     | `null`                     | SBOM hex digest from W3 release pipeline  |
| `BUILDER_ID`                             | `kaireon.platform.runtime` | SLSA builder.id                           |
| `BUILDER_VERSION`                        | `unknown`                  | SLSA builder.version                      |
| `BUILD_STARTED_ON` / `BUILD_FINISHED_ON` | now()                      | SLSA buildStartedOn/Finished              |
| `GITHUB_RUN_ID`                          | trace id                   | SLSA invocationId                         |

When all of `GIT_SHA / GIT_REPO / IMAGE_NAME / IMAGE_DIGEST` are set,
`buildIdentity.slsaAttestation` is populated by the supply-chain
SLSA-provenance builder.

## Audit trail

Every bundle emission writes one audit-log row with
`action: "decision_provenance_emit"`, `entityId: <traceId>`, and
`changes: { digestSha256, signed, modelCount, auditLogCount }`.
DSAR exports cite these rows.

## Honest limits

* Bundle includes per-modelType snapshots (the hash of each
  `modelState`), not the full state. Auditors who need bit-exact
  model reproduction request the model record separately via the
  registry endpoint.
* Signing is fail-soft by design: a broken signing path degrades to
  an `"unsigned"` bundle rather than failing the request — see
  "Cosign signing" above.
* Effective-rules cascade is computed at bundle-emission time, not
  at decision time. Rule edits between the decision and the bundle
  request will surface in the cascade trace; the trace itself
  records what rules fired at decision time via
  `qualificationResults` / `contactPolicyResults`.
* PII redaction runs over the trace before signing. The redactor
  is `lib/explanations/pii-redact.ts`; bugs there could leak PII
  into provenance bundles. Treat unsigned bundles as untrusted.
