313 lines
12 KiB
Markdown
313 lines
12 KiB
Markdown
# 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": "<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`:
|
|
```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."
|