Skip to main content

Policy Portability

Anti-lock-in is a trust moat, not a weakness. If you can take your Adjudon policies and re-run them anywhere, you trust Adjudon more — exactly because you could leave.

Live status (2026-05-11)

Live today: Policy bundle export (.tar.gz), SHA-256 content-hash integrity, ADL + JSON v1 + Cedar policy artefacts, transcript records with policyVersionHash + cedarRequestHash, adjudon-replay Node CLI for offline verification against cedar-wasm.

Gated on founder action (architecture wired, signatures currently mock):

  • D-Trust QTSP RFC 3161 qualified timestamp — services/eidasTimestampService.js returns deterministic mock until EIDAS_TSA_PROD env-var is set with the D-Trust endpoint (commercial contract pending).
  • Sigstore Cosign keyless OIDC + Rekor v2 inclusion proof — services/policyBundleSigner.js returns structured stub until the GitHub Actions OIDC pipeline is wired (FOUNDER_ACTION_CHECKLIST gate 3.1).
  • Pre-built signed standalone replay binaries — backend/scripts/build-replay-binary.sh exists; binaries publish in Phase 3.1.

Until each gate fires, the bundle's signature blocks contain mock tokens that preserve chain integrity but do not carry the eIDAS Art. 41(2) statutory presumption. We document this honestly so procurement does not arrive expecting a live qualified timestamp that does not exist yet.

What you get

When you export a Policy bundle from /dashboard/policies/export, you receive a single adjudon-policy-bundle.tar.gz containing:

manifest.json                                  ← bundle index + SHA-256 fileHashes
policies/
policy_<id>.json ← Policy doc (current state)
policy_<id>_v<n>.meta.json ← PolicyVersion metadata + canonicalContentHash
policy_<id>_v<n>.adl.json ← ADL representation (per LD-2)
policy_<id>_v<n>.cedar ← Cedar source (post production-cutover)
transcripts/
transcripts_<from>_<to>.jsonl ← per-evaluation records (PII-scrubbed)
verify/
verify.sh ← POSIX hash-verification script
README.md
signatures (inside manifest.json):
sigstoreCosign ← keyless cert + Rekor v2 inclusion proof
qtspTimestamp ← D-Trust GmbH RFC 3161 token

The dual-anchor

Two independent layers attest to bundle integrity:

Sigstore Cosign + Rekor v2 (software-supply-chain transparency)

Adjudon's GitHub Actions workflow issues an OIDC token to Fulcio → ephemeral cert → keyless DSSE signature → Rekor v2 inclusion proof. The Rekor entry is publicly searchable and tamper-evident — Adjudon cannot retroactively change the bundle without breaking the Rekor leaf.

What this protects against: Adjudon-side tampering with the bundle after issuance.

What it does NOT do alone: Rekor is a transparency log operator (Linux Foundation), NOT a Qualified Trust Service Provider under eIDAS. A Rekor timestamp alone does not get the §371a Abs. 3 ZPO + eIDAS Art. 41(2) statutory presumption.

D-Trust GmbH RFC 3161 qualified timestamp (eIDAS Art. 41)

D-Trust is Bundesnetzagentur-supervised, on the EU Trusted List, TÜViT-confirmed eIDAS conformity. The bundle's SHA-256 hash is submitted via RFC 3161 → qualified TimeStampToken → stored in manifest.signatures.qtspTimestamp.

What this unlocks: Under §371a Abs. 3 ZPO, a qualified electronic timestamp is treated as an öffentliches elektronisches Dokument with statutory presumption of accuracy + integrity in German civil court. eIDAS Art. 41(2) extends this presumption across the EU.

Failover: GlobalSign nv-sa (Belgian FPS Economy) is the secondary QTSP.

Why both layers

  • Rekor alone: software-supply-chain transparency without statutory evidentiary value.
  • QTSP alone: German court-admissible but without independent-transparency-log redundancy (D-Trust is one entity; can it be coerced? With a single QTSP, you trust D-Trust).
  • Both: even if Adjudon AND D-Trust were both coerced, the Rekor log shows the bundle was published with the QTSP token at a specific time. The two anchors are operated by independent legal entities in independent jurisdictions.

How to verify a bundle offline

  1. Receive adjudon-policy-bundle.tar.gz (e.g., via email, USB stick, or a regulator hand-over).
  2. Extract:
    tar -xzf adjudon-policy-bundle.tar.gz
  3. Verify hashes:
    cd <extracted-dir>
    sh verify/verify.sh
    This checks SHA-256 of every file against manifest.fileHashes.
  4. Verify Sigstore signature against the published Adjudon Fulcio identity (Phase 3.1 — wraps the verify script):
    # Step 1: extract the signature + cert from the manifest JSON
    jq -r '.signatures.sigstoreCosign.signature' manifest.json > bundle.sig
    jq -r '.signatures.sigstoreCosign.certificate' manifest.json > bundle.pem

    # Step 2: verify with cosign (signature + certificate as file paths)
    cosign verify-blob \
    --certificate-identity-regexp "^https://github.com/adjudon/.*$" \
    --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
    --signature bundle.sig \
    --certificate bundle.pem \
    adjudon-policy-bundle.tar.gz
  5. Verify QTSP token:
    • Extract the DER-encoded TimeStampToken from manifest.signatures.qtspTimestamp.token
    • Verify the CMS signature against the D-Trust public certificate
    • Cross-check the D-Trust signing cert against the Bundesnetzagentur Trusted List at tl.bundesnetzagentur.de and the EU LOTL Browser at eidas.ec.europa.eu/efda/tl-browser/
    • Assert TSTInfo.messageImprint.hashedMessage equals the bundle hash
  6. Replay every recorded decision via the bundled adjudon-replay CLI:
    node adjudon-replay.js adjudon-policy-bundle.tar.gz
    This re-runs every transcript against the bundled policy versions using Cedar WASM, asserts byte-for-byte agreement with the stored decision.

Cardinal Rules applied

  • Rule #1 (org isolation): the bundle contains data for a single org; export endpoint filters organizationId from the authenticated user, never from the request body.
  • Rule #3 (EU residency): D-Trust + GlobalSign are EU Trusted List QTSPs. Sigstore Fulcio + Rekor v2 are global but only the SHA-256 hash leaves EU per EDPB Guidelines 02/2025.
  • Rule #4 (PII scrub): transcripts in the bundle inherit the scrubbed form from write-time (per the Track D policyTranscriptService.emit scrub stage); no raw PII surfaces in the bundle.
  • Rule #5 (audit immutability): the bundle is a snapshot — Policy and PolicyVersion + PolicyTranscript records are append-only on the live database, and the bundle is a frozen view of that chain at a specific moment.

What's NOT in the bundle (and why)

  • Customer identity beyond organizationId: no user emails, no API keys, no IP addresses.
  • Trace input payloads: only PII-scrubbed traceValue strings from the firedConditions list. The raw input that the customer's agent processed is NOT in the bundle.
  • The Cedar WASM runtime binary: the adjudon-replay CLI bundles cedar-wasm internally; the customer doesn't need to install Cedar separately.

Phase 3.1 deliverables (founder action gated)

  • Sigstore Cosign integration requires GitHub Actions OIDC setup (one-time DevOps; see FOUNDER_ACTION_CHECKLIST 3.1).
  • D-Trust QTSP requires commercial procurement (see FOUNDER_ACTION_CHECKLIST 2.1).
  • Pre-signed S3 download URL for the bundle (vs the current in-memory generation).
  • Encrypted-at-rest bundle option with customer-held key (per Synthesis §8 caveat 9 — adversarial-evasion mitigation).