Skip to content

The evidence chain

How "who built this, from what, and what does it touch?" stays answerable — for every app, at any time, without an engineer in the review chain. This is Principle III, one of the two non-negotiables: every artifact carries a verifiable attestation chain from prompt to production, and unattested artifacts are refused at admission everywhere, including sandbox.

From prompt to admitted workload

sequenceDiagram
    autonumber
    participant U as User
    participant B as Builder (via adapter)
    participant G as Gitea (app repo)
    participant T as Tekton pipeline
    participant C as Tekton Chains + cosign
    participant H as Harbor
    participant K as Kyverno (admission)
    participant O as Operator

    U->>B: describe / iterate on the app
    B->>G: commit to sandbox branch<br/>trailers: prompt-hash: sha256:…, model: …
    G->>T: webhook (EventListener)
    T->>T: fetch-source — extract the trailers
    T->>T: build-image — buildah, every FROM must be<br/>harbor.tend-system.svc/…@sha256:…<br/>(registries.conf blocks every external registry;<br/>the build task rejects a FROM that is not digest-pinned)
    T->>C: sign-and-attest
    C->>H: push image + cosign-signed SLSA v1 attestation<br/>(co-located OCI artifacts)
    O-->>T: watches PipelineRun results<br/>→ status.sandboxArtifact
    Note over K: deployment attempted (Argo CD sync)
    K->>H: verifyImages: signature + SLSA predicate
    alt verifiable
        K-->>O: admitted — app runs
    else unattested / unverifiable
        K-->>O: REFUSED (fail closed, every tendtech.io/app namespace)
    end

What the chain is made of

Provenance starts at the commit. The builder adapter records prompt-hash (sha256 of the prompt) and model (the resolved model identity/version returned by the inference gateway) as git commit trailers. The pipeline extracts these — it never invents them — and sign-and-attest fails if either is empty. An artifact without provenance can't exist, rather than existing and being caught later.

The attestation is an in-toto/SLSA v1 predicate with Tend's custom fields under buildDefinition.externalParameters.tend: appRef, promptHash, model, builder, sourceCommit. Tekton Chains signs it with a cosign x509 key (signing-secrets, in-cluster) and stores it in Harbor beside the image as an OCI artifact — evidence travels with the artifact it describes, not in a side database. Transparency log and Fulcio are deliberately disabled: sigstore's public infrastructure is off-boundary (ADR-0005).

Admission is where the chain gets teeth. Kyverno's verify-attestation ClusterPolicy checks the cosign signature and the SLSA predicate on every image entering a tendtech.io/app namespace — sandbox included, because a governance gate that only guards production teaches everyone to live in sandbox. It runs Enforce with failurePolicy: Fail: if the gate can't decide, nothing runs.

CI demonstrates the refusal live, but on a narrower trigger than "every pull request": the policy workflow is path-filtered, so it runs only when a change touches one of the directories it lists. That filter has been wrong three times, and twice a policy change merged green having run no policy job at all. Read the gate as enforced at admission — which it is, unconditionally, at runtime — and the CI demonstration as covering the changes the filter happens to catch.

Evidence keeps accumulating after the build

The chain doesn't stop at admission — later events append to the same per-app record:

Event Evidence captured Where it lives
Iteration new artifact + attestation per build Harbor + status.attestationRefs
Promotion request declared fields snapshot, purpose incl. decisions-about-people, evidence ref App CR status.promotion + the PR
Approval / rejection approver, time, scopes / reason git history + App CR
Data access every gateway decision, allow and deny gateway log store (lifetime)
Cost enforcement each threshold crossing, suspension, resume Events + status.costStatus timestamps on the App CR (there is no cost category in the evidence record)

One query, honestly answered

The evidence index assembles the full record on demand from the authoritative sources — App CR, Gitea history, Harbor attestations, gateway logs, Argo CD — in a parallel fan-out (ADR-0016). Two design points keep it honest:

  • Provenance per category: the response names which source served each category (prompts: harbor-attestations, dataAccess: data-gateway, …). Hand-entered evidence is not a thing that can exist here.
  • Explicit degradation: if a source is unreachable, its categories are marked partial/unavailable rather than silently missing — a partial answer that admits it is partial is evidence; a partial answer presented as complete is a lie the auditor will eventually catch.

The purpose classification captured at promotion — including the decisions-about-people flag — rides this same record and surfaces as the AI Act triage marker in the estate view. Tend records and surfaces; the conformance judgment stays with the customer's compliance function.

Why this is the load-bearing principle

Contract-not-code (governance model) removes the engineer from the review chain; the evidence chain is what makes that removal defensible. When nobody read the code, the system must be able to prove what the code is, where it came from, and everything it touched. The chain is cryptographic where it can be (signatures, digests, hashes) and append-only where it can't (access logs, git history) — and the refusal path is tested live, because an attestation gate that silently stopped gating would be worse than none.

A chain can be verified by hand with cosign verify and cosign verify-attestation against the image digest, using the platform's own verification settings.