Files
ietf-draft-analyzer/workspace/act/act-implementation-master-prompt.md
Christian Nennemann 2506b6325a
Some checks failed
CI / test (3.11) (push) Failing after 1m37s
CI / test (3.12) (push) Failing after 57s
feat: add draft data, gap analysis report, and workspace config
2026-04-06 18:47:15 +02:00

12 KiB

Master Prompt: ACT Reference Implementation

Context

You are implementing the reference implementation for the Agent Compact Token (ACT), as defined in draft-nennemann-act-00. The full draft is attached or provided in context. Your implementation must be a clean, well-documented Python package that serves as the normative reference for the specification — meaning it is the ground truth for interoperability testing.

The ACS (Agent Compliance Seal) reference implementation (~1,900 lines, Python) exists as a prior art reference for code style and structure. ACT follows the same philosophy: minimal dependencies, sub-millisecond performance for the hot path, Ed25519 as the primary algorithm.


Deliverables

Produce the following files in a single act/ package directory:

act/
├── __init__.py          # Public API exports
├── token.py             # ACTMandate and ACTRecord dataclasses + serialization
├── crypto.py            # Key management: Tier 1 (pre-shared), Tier 2 (PKI),
│                        #   Tier 3 (DID:key, DID:web); sign/verify primitives
├── lifecycle.py         # Phase 1 → Phase 2 transition logic (re-signing)
├── delegation.py        # Delegation chain construction and verification
├── dag.py               # DAG validation (uniqueness, parent existence,
│                        #   temporal ordering, acyclicity, capability
│                        #   consistency)
├── ledger.py            # In-memory append-only audit ledger (for testing;
│                        #   interface suitable for external backends)
├── verify.py            # Unified verification entry point (Phase 1 + Phase 2)
├── errors.py            # All ACT-specific exception types
└── vectors.py           # Generates and validates all Appendix B test vectors
tests/
├── test_token.py
├── test_crypto.py
├── test_lifecycle.py
├── test_delegation.py
├── test_dag.py
├── test_ledger.py
├── test_verify.py
└── test_vectors.py      # Must pass all vectors defined in vectors.py

Specification Summary

Token Structure

An ACT is a JWT (JWS Compact Serialization). It has two phases:

Phase 1 — Authorization Mandate (signed by issuing agent):

JOSE Header:

{
  "alg": "EdDSA",       // Ed25519; also support ES256
  "typ": "act+jwt",
  "kid": "<key-id>"
  // optional: "x5c" (Tier 2), "did" (Tier 3)
}

Required JWT claims:

  • iss — issuer agent identifier (opaque string / X.509 DN / DID)
  • sub — target agent identifier (same format as iss)
  • aud — intended recipient(s); string or array
  • iat — issuance time (NumericDate)
  • exp — expiration time (NumericDate); SHOULD be iat + ≤900s for automated flows
  • jti — UUID v4; doubles as task identifier for DAG par references

Optional:

  • wid — workflow UUID grouping related ACTs

Required ACT-specific claims:

  • task — object: { purpose (str, REQUIRED), data_sensitivity (str, OPTIONAL), created_by (str, OPTIONAL), expires_at (NumericDate, OPTIONAL) }
  • cap — array of { action (str, REQUIRED), constraints (object, OPTIONAL) } action names conform to ABNF: component *("." component) component = ALPHA *(ALPHA / DIGIT / "-" / "_")
  • del — object: { depth (int), max_depth (int), chain (array) } chain entries: { delegator (str), jti (str), sig (base64url str) } chain is ordered root → immediate parent (chain[0] = root authority) If del is absent: treat as root mandate, depth=0, delegation forbidden

Optional:

  • oversight — { requires_approval_for (array of action strings), approval_ref (str, OPTIONAL) }

Phase 2 — Execution Record (re-signed by executing agent, i.e. sub):

All Phase 1 claims are preserved unchanged. Additional required claims:

  • exec_act — string; MUST match one of the cap[].action values
  • par — array of jti strings (parent task IDs in DAG); [] for root tasks
  • exec_ts — NumericDate; actual execution time; MUST be >= iat; SHOULD be <= exp (if exec_ts > exp: log warning, do NOT reject)
  • status — one of: "completed", "failed", "partial"

Additional optional claims:

  • inp_hash — base64url(SHA-256(raw input bytes)), no padding
  • out_hash — base64url(SHA-256(raw output bytes)), no padding
  • err — { code (str), detail (str) }; present when status != "completed"

Critical: In Phase 2, the JOSE header kid MUST reference the sub agent's key (not the iss agent's key). The re-signature is produced by the executing agent over the complete Phase 2 payload (all Phase 1 claims

  • execution claims combined).

Trust Tiers

Tier 1 — Pre-Shared Keys (mandatory-to-implement)

  • Keys: Ed25519 (primary) or P-256
  • kid: opaque string agreed out-of-band
  • Key registry: a dict mapping kid → public key bytes, configured at init time
  • No external resolution needed

Tier 2 — PKI / X.509

  • kid: SHA-256 thumbprint of DER-encoded certificate
  • x5c JOSE header MAY carry the certificate chain
  • Verification: standard X.509 chain validation against trusted CA store

Tier 3 — DID

  • Support did:key (self-contained, no resolution needed)
  • Support did:web (requires HTTP resolution; cache with configurable TTL)
  • kid: DID key fragment (e.g. did:key:z6Mk...#key-1)
  • did JOSE header MAY carry the full DID for resolution

Delegation Chain

When issuing a delegated ACT (Agent A → Agent B):

  1. del.depth = parent ACT's del.depth + 1
  2. del.max_depth ≤ parent ACT's del.max_depth
  3. cap must be a subset of parent ACT's cap with constraints at least as restrictive
  4. Each chain entry sig = Sign(A.private_key, SHA-256(parent_act_compact_bytes)) where parent_act_compact_bytes is the raw bytes of the parent ACT's JWS Compact Serialization (UTF-8 encoded)

Verification of chain entry:

  • Retrieve public key for entry.delegator
  • Recompute SHA-256(parent_act_compact_bytes)
  • Verify entry.sig against that hash using entry.delegator's public key

Rejection conditions:

  • del.depth > del.max_depth
  • del.chain length != del.depth
  • Any chain entry sig fails verification
  • cap contains actions not in parent ACT's cap
  • Any constraint in cap is less restrictive than in parent ACT

DAG Validation (Phase 2)

The ACT ledger (or set of received parent ACTs) is the ECT store.

Required checks on receiving a Phase 2 ACT:

  1. jti uniqueness within wid scope (or globally if wid absent)
  2. Every jti in par exists in the ledger/store as a verified Phase 2 ACT
  3. For each parent: parent.exec_ts < child.exec_ts + 30s (clock skew tolerance)
  4. No cycle: following par references must not return to current jti — enforce max traversal limit of 10,000 nodes
  5. exec_act matches one of the cap[].action values in the Phase 1 claims

Verification Procedure

Phase 1 verification (ACTVerifier.verify_mandate):

  1. Parse JWS Compact Serialization
  2. Check typ == "act+jwt"
  3. Check alg in allowlist (must include EdDSA/Ed25519, ES256; MUST NOT include "none" or any HS* algorithm)
  4. Resolve public key for kid per trust tier
  5. Verify JWS signature
  6. Check exp not passed (clock skew tolerance: ≤300s)
  7. Check iat not unreasonably future (≤30s ahead)
  8. Check aud contains verifier's own identifier
  9. Check iss is trusted per local policy
  10. Check sub matches verifier's own identifier (when verifier is the target)
  11. Check all required claims present and well-formed
  12. If del.chain non-empty: verify delegation chain

Phase 2 verification (ACTVerifier.verify_record): All Phase 1 steps, plus: 13. Check exec_act present and matches a cap[].action 14. Check par present; perform DAG validation 15. Check exec_ts present and >= iat; if > exp log warning but do NOT reject 16. Check status present and valid 17. Check re-signature was produced by sub agent's key (kid in Phase 2 header must correspond to sub's public key, not iss's key) 18. Optionally verify inp_hash/out_hash against provided data


Audit Ledger Interface

ACTLedger (in-memory reference implementation):

  • append(act_record: ACTRecord) -> int — returns sequence number
  • get(jti: str) -> ACTRecord | None
  • list(wid: str | None) -> list[ACTRecord]
  • verify_integrity() -> bool — verifies no records have been tampered with (hash-chain over sequence-ordered records)

The ledger must enforce append-only semantics: once appended, a record cannot be modified or deleted. Raise ACTLedgerImmutabilityError on any attempt.


Error Types

Define in errors.py:

ACTError                        # base
ACTValidationError              # malformed token structure
ACTSignatureError               # signature verification failed
ACTExpiredError                 # token expired
ACTAudienceMismatchError        # aud does not contain verifier identity
ACTCapabilityError              # no matching capability / capability escalation
ACTDelegationError              # delegation chain invalid
ACTDAGError                     # DAG validation failed (cycle, missing parent, etc.)
ACTPhaseError                   # wrong phase for operation (e.g. mandate used as record)
ACTKeyResolutionError           # cannot resolve kid to public key
ACTLedgerImmutabilityError      # attempt to modify ledger
ACTPrivilegeEscalationError     # delegated cap exceeds parent cap

Test Vectors (Appendix B)

vectors.py must generate and validate all of the following. Each vector must include: description, input parameters, expected output (encoded token or expected exception class).

Valid vectors:

  • B.1: Phase 1 ACT — root mandate, Tier 1 (Ed25519 pre-shared key), no delegation
  • B.2: Phase 2 ACT — completed execution, transition from B.1 mandate
  • B.3: Phase 2 ACT — fan-in, two parent jti values from parallel branches
  • B.4: Phase 1 ACT — delegated mandate (depth=1), chain entry with sig
  • B.5: Phase 2 ACT — delegated execution record

Invalid vectors (must raise specified exception):

  • B.6: del.depth > del.max_depth → ACTDelegationError
  • B.7: cap escalation in delegated ACT → ACTPrivilegeEscalationError
  • B.8: exec_act not in cap → ACTCapabilityError
  • B.9: DAG cycle (par references own jti) → ACTDAGError
  • B.10: Missing parent jti in DAG → ACTDAGError
  • B.11: Tampered payload (bit flip in claims) → ACTSignatureError
  • B.12: Expired token → ACTExpiredError
  • B.13: Wrong audience → ACTAudienceMismatchError
  • B.14: Phase 2 re-signed by iss key instead of sub → ACTSignatureError
  • B.15: Algorithm "none" → ACTValidationError

Implementation Constraints

Dependencies: use only the Python standard library plus:

  • cryptography (for Ed25519, P-256, X.509)
  • pyjwt OR manual JWS implementation (prefer manual for spec fidelity)
  • pytest (test runner only)

Performance target: Phase 1 creation ≤ 500µs mean on modern hardware. Benchmark in a bench/ directory.

Code style:

  • Type-annotated throughout (Python 3.11+)
  • Dataclasses for token structures
  • No global mutable state
  • All public API functions documented with docstrings referencing the relevant draft section (e.g. # ACT §8.1)

Security constraints:

  • MUST NOT use symmetric algorithms (HS256 etc.) anywhere
  • MUST NOT implement "alg: none" path
  • Ed25519 signing MUST use bound key-pair APIs (private key object that carries the public key) — never pass raw private key bytes
  • All secret key material must be zeroed on deletion where the cryptography library supports it

What NOT to implement:

  • DID:web resolution with live HTTP calls in the reference implementation (stub it with a configurable resolver callback instead)
  • Token revocation infrastructure
  • Persistence (ledger is in-memory only)

Output Format

Produce each file completely, in order. After all files, produce a README.md for the act/ package that includes:

  • Installation instructions
  • Quick-start example (Phase 1 mandate → Phase 2 record → verify)
  • Running the test suite
  • Running the test vectors
  • Performance benchmark instructions

At the end, confirm: "All Appendix B test vectors pass."