feat: ACT/ECT strategy, package restructure, draft -01/-02 prep
Strategic work for IETF submission of draft-nennemann-act-01 and
draft-nennemann-wimse-ect-02:
Package restructure:
- move ACT and ECT refimpls to workspace/packages/{act,ect}/
- ietf-act and ietf-ect distribution names (sibling packages)
- cross-spec interop test plan (INTEROP-TEST-PLAN.md)
ACT draft -01 revisions:
- rename 'par' claim to 'pred' (align with ECT)
- rename 'Agent Compact Token' to 'Agent Context Token' (semantic
alignment with ECT family)
- add Applicability section (MCP, OpenAI, LangGraph, A2A, CrewAI)
- add DAG vs Linear Delegation Chains section (differentiator vs
txn-tokens-for-agents actchain, Agentic JWT, AIP/IBCTs)
- add Related Work: AIP, SentinelAgent, Agentic JWT, txn-tokens-for-agents,
HDP, SCITT-AI-agent-execution
- pin SCITT arch to -22, note AUTH48 status
Outreach drafts:
- Emirdag liaison email (SCITT-AI coordination)
- OAuth ML response on txn-tokens-for-agents-06
Strategy document:
- STRATEGY.md with phased action plan, risk register, timeline
Submodule:
- update workspace/drafts/ietf-wimse-ect pointer to -02 commit
This commit is contained in:
113
workspace/packages/INTEROP-TEST-PLAN.md
Normal file
113
workspace/packages/INTEROP-TEST-PLAN.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# ACT / ECT Cross-Spec Interop Test Plan
|
||||
|
||||
**Status**: Draft (Task C4 preparation — planning only, not yet implemented)
|
||||
**Scope**: Python refimpls `ietf-act` (Phase 1/2, 103 tests) and `ietf-ect` (single-phase, 56 tests)
|
||||
**Deliverable**: `packages/interop/tests/test_interop.py` + compatibility matrix docs
|
||||
|
||||
## 1. Goals and Non-Goals
|
||||
|
||||
### Goals
|
||||
- Empirically document which shared claims round-trip cleanly between refimpls.
|
||||
- Surface real format-level incompatibilities (hash encoding, typ header, algorithm support) rather than assume the spec-level claim overlap implies wire interop.
|
||||
- Produce a user-facing **compatibility matrix** that implementers can rely on when building bridges between Phase 2 ACT Records and ECT payloads.
|
||||
- Provide executable regression tests so future changes to either refimpl cannot silently break the documented interop level without CI noticing.
|
||||
|
||||
### Non-Goals
|
||||
- Propose spec unification or new shared claim registries.
|
||||
- Build a lossy translator/bridge between ACT Records and ECT payloads.
|
||||
- Test `typ` cross-acceptance — `act+jwt` vs `exec+jwt` MUST remain distinct token types.
|
||||
- Forge one token type as the other.
|
||||
- Add new crypto backends (e.g., Ed25519 support) to ECT as part of this work.
|
||||
|
||||
## 2. Known Shape of the Problem
|
||||
|
||||
Shared claims (by name): `jti`, `wid`, `iat`, `exp`, `aud`, `exec_act`, `pred`, `inp_hash`, `out_hash`.
|
||||
|
||||
Confirmed divergences discovered while reading the code:
|
||||
- **Hash encoding mismatch**: ACT `b64url_sha256()` emits plain base64url (e.g. `n4bQgYhMfWWaL-qgxVrQFaO_TxsrC4Is0V1sFbDwCgg`). ECT `validate_hash_format()` requires `alg:base64url` form (e.g. `sha-256:...`) and raises on plain b64url. The briefing says this was "recently fixed to match ACT's plain base64url format" but the ECT validator still requires the prefix — plan must include a reproducer.
|
||||
- **Algorithm**: ACT supports `EdDSA` + `ES256`; ECT hard-codes `ES256` (see `ect/verify.py`, line 59, `"ect: expected ES256"`).
|
||||
- **Typ header**: ACT requires `act+jwt`; ECT requires `exec+jwt` (with legacy `wimse-exec+jwt`). Neither accepts the other — and per anti-goals, neither should.
|
||||
- **aud shape**: ACT stores `aud` as `str | list[str]`; ECT normalises to `list[str]` via `_audience_deserialize`.
|
||||
- **Claims unique to ACT**: `sub`, `iss` (required string), `task`, `cap`, `del`, `oversight`, `exec_ts`, `status`, `err`.
|
||||
- **Claims unique to ECT**: `ect_ext`, `inp_classification`, and policy claims inside `ect_ext` (`pol`, `pol_decision`, `compensation_required`).
|
||||
|
||||
## 3. Test Categories
|
||||
|
||||
### 3.1 Shared claim consistency (`TestSharedClaims`)
|
||||
- `test_jti_format_roundtrips`: UUID-v4 jti accepted by both refimpls; non-UUID jti accepted by ACT (no UUID check) but only by ECT when `validate_uuids=False` (document the asymmetry).
|
||||
- `test_wid_shared_semantics`: same wid value on an ACT Record and an ECT payload — both accept.
|
||||
- `test_iat_exp_numericdate`: identical integer NumericDate accepted by both (ACT uses strict `> 0`, ECT uses `int(claims["iat"])`).
|
||||
- `test_aud_string_vs_list`: string `aud` preserved by ACT, coerced to list by ECT; list form is lossless on both.
|
||||
- `test_exec_act_string_both_sides`: same `exec_act` value (e.g. `read.data`) serialises identically; ACT additionally validates ABNF grammar — test that ECT accepts an ACT-grammar-legal value unchanged.
|
||||
- `test_pred_array_shape`: `pred=[]`, `pred=[jti1]`, `pred=[jti1, jti2]` — both refimpls serialise/deserialise identically.
|
||||
- `test_inp_hash_format_divergence` (**expected xfail/documented**): feed ACT's plain b64url output into ECT validator — expect `ValueError("ect: inp_hash/out_hash must be algorithm:base64url...")`. This pins the incompatibility so a future fix flips the test green.
|
||||
- `test_inp_hash_prefixed_form`: `sha-256:<b64url>` value accepted by ECT; ACT treats it as opaque string (no validation), roundtrips without error.
|
||||
- `test_out_hash_same_as_inp`: mirror the above for `out_hash`.
|
||||
|
||||
### 3.2 Algorithm compatibility (`TestAlgorithmMatrix`)
|
||||
- `test_es256_act_record_signature_verifies_with_ect_key_resolver`: build a Phase 2 ACTRecord, sign with ES256 P-256 key. Feed the compact JWS bytes *and an ECT-shaped resolver* through `ect.verify`. Expect `ValueError("ect: invalid typ parameter")` because typ is `act+jwt`. Document: JWS/ES256 signature layer is compatible, but typ gate prevents verifier reuse as-is.
|
||||
- `test_eddsa_act_record_rejected_by_ect`: Phase 2 ACTRecord signed EdDSA. ECT must reject at alg gate (`"ect: expected ES256"`). Documents the ES256-only limitation.
|
||||
- `test_ect_payload_signature_verifies_with_act_crypto`: sign an ECT payload (ES256), strip to raw JWS, feed signature bytes through `act.crypto.verify` with the ECT public key. Expect success — proves the ES256 primitive is wire-compatible at the raw-sig level.
|
||||
|
||||
### 3.3 DAG cross-reference (`TestDagInterop`)
|
||||
- `test_pred_array_referenceable_both_ways`: construct ACT Record with `pred=[ect_jti]` and an ECT payload with `pred=[act_jti]`. Both refimpls accept the arrays structurally (they're opaque strings).
|
||||
- `test_mixed_dag_is_out_of_scope`: document and assert that `ACTStore` only stores ACT records and `ECTStore` only stores ECT payloads; neither is designed to resolve a `pred` jti from the other type. A bridging verifier would have to walk both stores — out of scope for refimpls.
|
||||
- `test_jti_collision_across_types`: the same UUID used as `jti` in an ACT Record and an unrelated ECT payload — both refimpls accept independently; document that jti uniqueness is scoped per-token-type in the refimpls.
|
||||
|
||||
### 3.4 Semantic divergence (`TestClaimDivergence`)
|
||||
- `test_ect_ignores_act_only_claims`: ECT `Payload.from_claims` is called on a dict that includes `sub`, `task`, `cap`, `oversight`, `exec_ts`, `status`. Expect: silently ignored (no error, no retention). Document as "ECT is lenient on unknown top-level claims".
|
||||
- `test_act_ignores_ect_only_claims`: feed `ACTRecord.from_claims` a claim dict with `ect_ext`, `inp_classification`. Expect: silently ignored and not retained.
|
||||
- `test_exec_act_not_validated_against_cap_in_ect`: ACT Record with `exec_act="read.data"` and `cap=[{"action":"write.result"}]` → ACT verifier raises `ACTCapabilityError`. Same `exec_act` in an ECT payload with no `cap` → ECT accepts. Documents the cap-validation asymmetry; guards against anyone accidentally copy-pasting cap logic into ECT.
|
||||
- `test_act_requires_status_ect_does_not`: ACTRecord without `status` → `ACTValidationError`. ECT without `status` → accepted.
|
||||
|
||||
### 3.5 Anti-goals (encoded as negative tests)
|
||||
- `test_act_jwt_typ_rejected_by_ect`: ACT compact with `typ=act+jwt` fed to `ect.verify` → MUST raise "invalid typ parameter".
|
||||
- `test_exec_jwt_typ_rejected_by_act`: ECT compact with `typ=exec+jwt` fed to `act.decode_jws` → MUST raise `ACTValidationError` on typ check.
|
||||
- `test_no_forgery_as_other_type`: explicit comment-only placeholder asserting we do not re-encode one type as the other; kept as a doc anchor.
|
||||
|
||||
## 4. Expected Compatibility Matrix (user-facing)
|
||||
|
||||
| Layer | Direction | Status | Notes |
|
||||
|---|---|---|---|
|
||||
| ES256 raw signature | ACT ↔ ECT | Compatible | Same JWS/ES256 primitive |
|
||||
| EdDSA signature | ACT → ECT | Incompatible | ECT is ES256-only |
|
||||
| `typ` header | ACT ↔ ECT | Strictly separated | By design |
|
||||
| `jti`, `wid`, `iat`, `exp`, `aud`, `exec_act`, `pred` | Shared | Compatible | Identical wire shapes |
|
||||
| `inp_hash`/`out_hash` | ACT → ECT | **Incompatible today** | ACT emits plain b64url, ECT requires `sha-256:<b64url>` |
|
||||
| `inp_hash`/`out_hash` | ECT → ACT | Compatible | ACT treats as opaque string |
|
||||
| `cap` / `exec_act` coupling | ACT-only | N/A | ECT does not enforce |
|
||||
| DAG `pred` traversal | Separate stores | Manual bridging required | Refimpls do not cross-resolve |
|
||||
|
||||
## 5. Dependencies and Structure
|
||||
|
||||
Both packages must be importable in a single venv:
|
||||
|
||||
```
|
||||
pip install -e packages/act packages/ect packages/interop[dev]
|
||||
```
|
||||
|
||||
Proposed layout:
|
||||
|
||||
```
|
||||
packages/
|
||||
act/ …
|
||||
ect/ …
|
||||
interop/
|
||||
pyproject.toml # declares ietf-act, ietf-ect as deps
|
||||
tests/
|
||||
__init__.py
|
||||
conftest.py # shared ES256 keypair + resolver fixtures
|
||||
test_interop.py # classes Test{SharedClaims,AlgorithmMatrix,DagInterop,ClaimDivergence,AntiGoals}
|
||||
README.md # published compatibility matrix
|
||||
```
|
||||
|
||||
`conftest.py` exposes fixtures: `es256_keypair`, `act_record_builder`, `ect_payload_builder`, `dual_resolver` (one kid → same ES256 pubkey for both refimpls).
|
||||
|
||||
## 6. What the Compatibility Matrix Docs Should Tell Users
|
||||
|
||||
- **Do** reuse ES256 key material across ACT and ECT deployments — the signing primitive is identical.
|
||||
- **Do not** feed ACT compact tokens to an ECT verifier or vice versa; `typ` gates are deliberate.
|
||||
- **Do** treat `jti`, `wid`, `pred`, `exec_act` as semantically aligned when building cross-type audit logs.
|
||||
- **Do not** rely on `inp_hash`/`out_hash` being portable today — raise a spec issue if portability matters for your deployment.
|
||||
- **Do not** expect ECT to enforce ACT's `cap`/`exec_act` coupling — authorization remains an ACT concern.
|
||||
- **Open question for spec editors**: align hash encoding (plain b64url vs prefixed), and decide whether Ed25519 should be optional-to-support for ECT.
|
||||
Reference in New Issue
Block a user