[Hackathon] fayner-delegatable-auth: delegatable capability tokens with cascading revocation#70
Open
faynercosta wants to merge 1 commit into
Open
Conversation
…nanda#4) Macaroon-style (Birgisson et al., 2014) auth plugin: HMAC-chained tokens with offline attenuation, cascading revocation by chain-prefix hash, and defense-in- depth verify (scope-subset, nested TTL, audience binding). Ships the mandatory adversarial validator (scope-escalation, stale-parent, audience-confusion) that FAILS the reference jwt plugin and PASSES this one, plus a 17-agent delegation- tree scenario. Deterministic: same seed -> byte-identical trace. 20 targeted tests (13 unit + 4 Hypothesis property + 3 end-to-end scenario); full repo CI green (ruff, ruff format, pyright strict, pytest). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
[Hackathon] fayner-delegatable-auth: delegatable capability tokens with cascading revocation
Problem: #4 — Delegatable capability tokens with cascading revocation (auth layer).
Persona: authorization-infrastructure engineer. The risk model, the test
emphasis (adversarial subset/revocation/audience invariants), and the
macaroon idioms below are the submission, not a label on a generic plugin.
Motivation
The default
jwtplugin can mint and revoke flat tokens, but it cannot modelthe single most common multi-agent authorization pattern: an orchestrator
holding a long-lived root capability, minting narrowly-scoped, short-lived
sub-capabilities for worker agents without going back to the issuer, and
having a single
revoke(parent)cascade to every descendant.JwtAuth._revokedis a set of exact token strings with no parent-child relationship, so revoking a
parent leaves its delegated children valid — the exact gap the problem calls
out, and the gap that makes "an LLM agent sub-renting a tool and cleanly
withdrawing it" impossible to express today.
Design
DelegatableAuthis a macaroon (Birgisson et al., NDSS 2014): a token is achain of links, each an attenuating caveat, secured by an HMAC chain
sig_i = HMAC(sig_{i-1}, link_i)anchored at the verifier's root secret. Threeproperties follow:
attenuate,or the verifier-checked
delegate) because extending the chain only needsthe parent's signature, not the root secret. This is real delegation, not
central re-issuance (the named anti-pattern).
revokerecords the hash of achain prefix; every descendant embeds that prefix, so all of them fail
verifyfrom the next call on — no per-child revocation list.holder from writing broader scopes or a longer TTL into a link (they can
compute a valid signature for anything they append). So
verifyindependently re-walks the chain and rejects any link that escalates scope,
outlives its parent, breaks the delegator→audience linkage, or is expired
against the injected logical clock.
Typed errors (
ScopeEscalationError,RevokedAncestorError,AudienceMismatchError,ExpiredTokenError,TtlViolationError,InvalidTokenError) subclassValueErrorso existingAuthcallers thatexcept ValueErrorkeep working.Determinism: no wall-clock time anywhere; the logical clock is injected via
set_clock(ctx.time), exactly like the rotating-identity reference plugin.Same seed → byte-identical trace (asserted in a test).
Adversarial validators (mandatory)
validators/delegation_validators.pyships three attack probes plus atrace-level replay validator. Each probe runs against both plugins via small
adapter callables, so the "adversarial" property is demonstrated, not asserted:
delegatablejwtThe
delegated_authscenario (1 coordinator + 1 gatekeeper + 3 intermediaries× 4 workers) runs all three live. Result on the shipped trace:
The validator FAILS against
jwtand PASSES againstdelegatable— thecharter's bar.
Tests
test_delegatable.py— 13 unit tests: happy path, each typed rejection,two-level cascade, sibling isolation under revocation, tampered-signature and
hand-forged-broadening rejection, offline-attenuate equivalence, and the
cross-plugin probe property.
test_delegatable_properties.py— 4 Hypothesis properties: subset invariantholds at every chain depth, revocation cascades monotonically (depth ≥ k
revoked, depth < k spared), token construction is deterministic, escalation
is always rejected.
test_delegated_auth_scenario.py— end-to-end: delegatable denies everyattack, jwt admits them, trace is byte-deterministic.
20 tests, all green locally under the CI command sequence.
Runnable verification
Tradeoffs / scope
multi-parent delegation is out of scope.
membership checks per verify, no background GC. Fine for simulation; a
production deployment would bound the set with token expiry.
all explicitly out of scope for the problem.