Commit graph

2 commits

Author SHA256 Message Date
CTO (Paperclip)
9b8f520afc testagent-transform: align with dummy-sink contract + static dev tokens (DEV-613)
The sink client in DEV-610 was built against the architecture-doc shape
(POST canonical bytes to `/api/v1/handover` with headers) but DEV-611's
dummy-sink speaks a different contract: `POST /api/v1/sink/dummy/deliver`
with a JSON wrapper `{decision_id, payload_hash, payload, approval_ref,
transform_ref}`. The two subtasks therefore never actually talked to each
other.

Reconcile on the dummy-sink's wire format because the sink's extra
fields (decision_id, approval_ref, transform_ref) are mandatory for the
verification-service replay guard and chain checks:

- Switch `sink.Client.Deliver` to take a `DeliverRequest` carrying all
  five fields, and parse the handover_ref / log_index out of the sink's
  response for the caller.
- Pull `decision_id` from the handover payload's top-level field inside
  `transform.Runner.Execute` and plumb it through to the sink, surface
  `SinkHandoverRef` on the result.
- Update the mock sink inside `runner_test.go` to accept the new wire
  format and assert every wrapper field survives round-trip.

Additionally add `TRANSFORM_DEV_POCKET_ID_TOKEN` /
`TRANSFORM_DEV_WORKLOAD_TOKEN` env vars so the Phase-1 smoke harness can
drive the agent without a Pocket-ID server or a projected SA token
mount; both short-circuit directly to a `StaticToken`.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 20:58:14 +00:00
CTO (Paperclip)
ba1b88d490 testagent-transform: Phase-1 trust-chain demo agent (DEV-610)
Some checks failed
build-images / signing-service image (push) Failing after 16s
validate / JSON Schema (ajv) (push) Successful in 19s
validate / Go (vet + test) (push) Failing after 4s
validate / Rego (opa fmt + opa test) (push) Successful in 13s
validate / DB (migrate up/down dry-run) (push) Failing after 21s
Minimal CLI agent process that end-to-end demonstrates the Transform step
of the ProofedMesh trust chain:

  1. Fetches an approval attestation from the transparency-log by
     attestation_ref (`sha256:<hex>`).
  2. DSSE-verifies the fetched envelope against a pinned approval-signer
     ed25519 keyring; stops before any signing call on failure.
  3. Builds an in-toto v1 Statement with predicateType
     `basicstack.de/attestations/transform/v1`, operation
     `expense.map_to_sink`, materials[0].attestation_ref = approval_ref,
     and subject[0].digest.sha256 = sha256(canonical(handover_payload)).
  4. Calls signing-service /api/v1/sign/dsse with BOTH identities:
     Authorization: Bearer <pocket-id client_credentials token> AND
     X-Workload-Token: <projected SA token from
     /var/run/secrets/tokens/trust-api>.
  5. Submits the returned DSSE envelope + content_hash to the
     transparency-log /api/v1/log/entries.
  6. POSTs the canonical handover_payload bytes to the dummy-sink
     (schema sink.dummy.v1), carrying the transform content_hash on the
     X-Attestation-Ref header.

Tests use httptest for all four backends:

  - Happy path (verifies the bytes sent to the sink match the signed
    subject digest and that the TLog recorded the same content_hash the
    agent returned).
  - Invalid approval signature -> stops before sign-call; TLog and sink
    untouched.
  - Signing-service returns 403 signing_denied -> surfaced as
    signer.ErrSigningDenied; TLog and sink untouched.
  - Missing Pocket-ID token -> fails before any network call.

Includes PocketID client_credentials TokenSource (OIDC discovery + cache)
and ProjectedSATokenSource (reads kubelet-managed Volume) mirroring
admin-api/approval-service.

Non-scope: no read-side attestations (Phase 2), no K8s/CI image build
wiring (follow-up ticket).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 19:55:02 +00:00