Files
Christian Nennemann 37859beef6 feat: interop test package + session handoff doc
Cross-spec interop validation between ietf-act and ietf-ect:
- new packages/interop/ sibling package (ietf-act-ect-interop)
- 32 tests pass: shared claims, algorithm matrix, DAG structure,
  divergence handling, anti-goals
- documents ES256 raw signature wire-compatibility
- documents airtight typ separation (act+jwt vs exec+jwt)

Hazards surfaced:
- ACTLedger.append() silently accepts ECT Payload via duck-typing
  (both have .jti) — documented in interop README as a production
  hazard requiring external isinstance checks

Session handoff:
- SESSION-2026-04-12.md — snapshot of decisions, artifacts, open
  actions, and next-session starting points

Also: session-end commit of hash-format fix propagation to
packages/ect/ (the fix was applied to the old refimpl location
but did not propagate through the parallel package-move agent).
2026-04-12 07:39:41 +02:00

4.5 KiB
Raw Permalink Blame History

ietf-act-ect-interop

Cross-spec interop tests between the ietf-act (Agent Context Token, draft-nennemann-act-01) and ietf-ect (Execution Context Tokens, draft-nennemann-wimse-ect-01) Python reference implementations.

The purpose of this package is not to ship runtime code — it is to document empirically which shared claims, algorithms, and structures round-trip cleanly between the two refimpls, so implementers building bridges or shared tooling know what they can rely on.

Compatibility matrix

Observed as of the commit that produced these 32 passing tests:

Layer Direction Status Evidence
ES256 raw JWS signature ACT ↔ ECT Compatible test_es256_primitive_is_wire_compatible_at_raw_sig_level — ACT's crypto.verify accepts an ECT-signed compact's r||s bytes
EdDSA signature ACT → ECT Incompatible ECT verify refuses EdDSA at the alg gate (ES256-only)
typ header ACT ↔ ECT Strictly separated (by design) act+jwt vs exec+jwt; each verifier rejects the other
jti format Shared Compatible Same UUID string accepted by both
wid Shared Compatible Preserved on both sides
iat / exp (NumericDate) Shared Compatible Integer seconds on both
aud (string form) ACT → ECT Compatible (lossy round-trip) ACT stores str | list[str]; ECT coerces to list[str] via _audience_deserialize
exec_act Shared Compatible ACT ABNF-legal values pass through ECT unchanged
pred array Shared Compatible Same topology, same wire shape
inp_hash / out_hash Shared Compatible now Both specs use plain base64url (ECT was aligned — the prefixed sha-256: form is now rejected by ECT)
capexec_act coupling ACT-only Divergent ACT verifier raises ACTCapabilityError; ECT does not enforce
status claim ACT-only Divergent Required in ACT Phase 2; absent in ECT
sub, task, iss required ACT-only Divergent ECT Payload.from_claims silently drops them
ect_ext, inp_classification ECT-only Divergent ACT ACTRecord.from_claims silently drops them
DAG cross-resolution Separate stores Not supported ECTStore is keyed on Payload; ACTLedger on ACTRecord; no refimpl bridges the two

Hazard flag (found while writing these tests)

ACTLedger.append() does not perform a runtime isinstance check — it relies on duck typing. An ECT Payload object has a .jti attribute, and will therefore be silently accepted by the ACT ledger. This is an implementation hazard, not a spec-level guarantee: production bridges must enforce explicit type checks outside both refimpls. Pinned in test_act_ledger_does_not_type_check_ect_payload.

Do / Do not

Do

  • Reuse ES256 (P-256) key material across ACT and ECT deployments — the signing primitive is byte-identical.
  • Treat jti, wid, pred, exec_act as semantically aligned when building cross-type audit views.
  • Rely on inp_hash / out_hash being byte-portable between refimpls today.

Do not

  • Feed an ACT compact token to an ECT verifier or vice versa. The typ gates are deliberate and permanent.
  • Forge one token type as the other. This is a first-class anti-goal.
  • Expect ECT to enforce ACT's cap / exec_act coupling. Authorization stays in ACT.
  • Use EdDSA-signed ACT tokens in an ECT-only deployment. ECT is ES256-only.
  • Cross-insert objects between ACTLedger and ECTStore / MemoryLedger. Python's duck typing will let some of these through; that's a bug waiting to happen.

Install and run

From the workspace root:

pip install -e packages/act
pip install -e packages/ect
pip install -e packages/interop

cd packages/interop
python -m pytest tests/ -v

Expected: 32 passed.

Test file map

File Focus
tests/test_shared_claims.py pred, jti/wid, hash format, exec_act string shape
tests/test_algorithm_matrix.py ES256 ↔ EdDSA × verifier compatibility
tests/test_dag_structure.py pred-array topology equivalence; store separation
tests/test_divergence.py claims each parser ignores; typ separation; cap coupling; status requirement
tests/test_anti_goals.py cross-type forgery rejection; cross-type store hazard

Open questions for spec editors

  • Should ECT optionally accept Ed25519? Today it is ES256-only.
  • Should the refimpls enforce type-level rejection of cross-type objects passed into their stores/ledgers, or is that outside scope?