You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The interop test vectors ship inside the package as attenu_guard.vectors
(VECTOR_NAMES, load_vector, load_vectors, read_vector_bytes, read through importlib.resources). The Internet-Draft promises a chain that MUST verify and six that
MUST each be rejected for a named reason, so that an implementation written in ANY language
from the draft alone can score its own offline verifier; shipping them means doing that needs pip install attenu-guard and no clone. tests/vectors/generate.py is the single writer for
both copies — it serialises each vector once and writes those bytes to tests/vectors/ and src/attenu_guard/vectors/ — and tests/test_wire.py asserts the two are byte-identical, so
they cannot diverge. A CI step verifies they survive an install, not just a checkout.
A2A adapter (attenu_guard.adapters.a2a, extra a2a, tested against a2a-sdk 1.1.2): carries the attenuated
delegation chain across an Agent2Agent hop, so a remote agent in another process runs with permissions bounded by the
calling agent's. Two halves on public seams — client side, a DelegationInterceptor (ClientCallInterceptor.before)
mints the child with parent.delegate(...) and puts the signed Delegation Chain (attenu_guard.wire) on the outgoing
message as an A2A extension (Message.extensions + Message.metadata[<uri>], spec §4.6.2, with the A2A-Extensions header §4.6.1); server side, GuardedAgentExecutor wraps the deployment's AgentExecutor.execute,
verifies the chain offline (wire.load: signatures, parent-hash linkage, depth, child ⊆ parent at every hop, expiry)
and mints the served Guard from the verified leaf, narrowed again by what the remote task needs. A missing, forged,
spliced, widened or expired chain — or any exception raised while deciding — refuses the request before the remote
agent's own logic starts, returning the denial contract in the extension's metadata slot. guarded_tool(fn, scope=…)
checks before each tool body; require_guard() refuses a tool reached outside the executor. verify_hop(tokens, signer, client_bundle=…, server_bundle=…) checks the caller's ledger, the remote ledger and the tokens that bind them
from those inputs alone, and reports an unsupplied bundle as "not checked" rather than as passing. This answers A2A
§7.6.4, which states that the protocol defines no scope, validity or revocation semantics for an in-task authorization
decision. Cross-process revocation propagation remains open: an expired chain is refused and revocation_check= is the
seam for a status list, both documented as limits. Example (offline demo plus a live_smoke.py verified over a real
Starlette/uvicorn HTTP hop) and 35 offline tests; seventeenth entry in docs/INTEGRATIONS.md.