- Rename `par` to `pred` (predecessor) in types, serialization, tests - Remove `pol`, `pol_decision` from core payload; move to `ect_ext` - Remove `sub` from payload (not part of ECT spec) - Update `typ` from `wimse-exec+jwt` to `exec+jwt` (accept both) - Rename MaxParLength to MaxPredLength everywhere - Update testdata, demos, READMEs with migration table - All Go tests pass, all 56 Python tests pass (90% coverage)
3.5 KiB
WIMSE ECT — Python Reference Implementation
Python reference implementation of Execution Context Tokens (ECTs) for WIMSE. Implements ECT creation (ES256), verification (Section 7), DAG validation (Section 6), and an in-memory audit ledger (Section 9).
Layout
python/
├── pyproject.toml
├── ect/ # library
│ ├── __init__.py
│ ├── types.py # Payload, constants
│ ├── create.py # create(), generate_key()
│ ├── verify.py # parse(), verify(), VerifyOptions
│ ├── dag.py # validate_dag(), ECTStore, DAGConfig
│ ├── ledger.py # Ledger, MemoryLedger
│ ├── config.py # Config, load_config_from_env()
│ ├── jti_cache.py # JTICache for replay protection
│ └── validate.py # validate_ext, valid_uuid, validate_hash_format
├── tests/
│ ├── test_create.py
│ └── test_dag.py
├── testdata/
│ └── valid_root_ect_payload.json
└── demo.py # two-agent workflow demo
Install
cd refimpl/python && pip install -e .
Usage
from ect import (
Payload,
create,
generate_key,
CreateOptions,
verify,
VerifyOptions,
MemoryLedger,
)
cfg = load_config_from_env()
key = generate_key()
payload = Payload(
iss="spiffe://example.com/agent/a",
aud=["spiffe://example.com/agent/b"],
iat=int(time.time()),
exp=int(time.time()) + 600,
jti="550e8400-e29b-41d4-a716-446655440000",
exec_act="review_spec",
pred=[],
ext={
"pol": "policy_v1",
"pol_decision": "approved",
},
)
compact = create(payload, key, cfg.create_options("agent-a-key"))
store = MemoryLedger()
opts = cfg.verify_options()
opts.verifier_id = "spiffe://example.com/agent/b"
opts.resolve_key = lambda kid: key.public_key() if kid == "agent-a-key" else None
opts.store = store
parsed = verify(compact, opts)
store.append(compact, parsed.payload)
Demo
cd refimpl/python && python3 demo.py
Tests
cd refimpl/python && python3 -m pytest tests/ -v
Unit tests require 90% coverage minimum (pytest is configured with --cov-fail-under=90 in pyproject.toml). Install dev deps: pip install -e ".[dev]". Uncovered lines are mainly abstract base methods and a few verify branches that need manually built tokens.
draft-01 claim changes
| -00 (previous) | -01 (current) | Notes |
|---|---|---|
par |
pred |
Predecessor task IDs |
pol, pol_decision |
removed (use ect_ext) |
Policy claims moved to extension object |
sub |
not defined | Standard JWT claim, not part of ECT spec |
typ: wimse-exec+jwt |
typ: exec+jwt (preferred) |
Both accepted for backward compat |
max_par_length |
max_pred_length |
Renamed to match pred claim |
Production configuration (environment)
Same env vars as the Go refimpl: ECT_IAT_MAX_AGE_MINUTES, ECT_IAT_MAX_FUTURE_SEC, ECT_DEFAULT_EXPIRY_MIN, ECT_JTI_REPLAY_CACHE_SIZE, ECT_JTI_REPLAY_TTL_MIN.
Replay cache (multi-instance)
The provided JTI cache is in-memory only. For multiple verifier instances, use a shared store (Redis, DB) and pass a jti_seen callable that checks/records JTIs there. See refimpl/README for an overview.
Dependencies
- PyJWT, cryptography (ES256).
License
Same as the Internet-Draft (IETF Trust). Code under Revised BSD per BCP 78/79.