# 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: ```json { "alg": "EdDSA", // Ed25519; also support ES256 "typ": "act+jwt", "kid": "" // 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`: ```python 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."