[Hackathon] varuni7: delegatable capability tokens with cascading, partition-tolerant revocation#52
Open
varuni7 wants to merge 1 commit into
Open
Conversation
…rtition-tolerant revocation Problem 4 (auth). Adds `delegatable`, a macaroon-style auth plugin: a token holder can mint a strictly narrower, shorter-lived sub-token for another agent offline (no issuer round-trip), and revoking a token severs its whole subtree. Revocation state is a grow-only-set CRDT (RevocationSet): merges are the set union — commutative, associative, idempotent — so replicas converge by gossip regardless of order, duplication, or loss. Safety is monotone (a seal once observed revoked is never accepted again), and a verifier that has not yet heard a revocation still accepts until it merges one. Cascade is by construction: a child seal is HMAC(parent_seal, caveat), so verify() recomputes the chain and a revoked ancestor seal severs every descendant with no per-child bookkeeping. Determinism: no uuid, no wall-clock. Token identity is the seal (a pure function of content + secret); all time comes from an injected clock. Same seed -> byte-identical trace. Ships: - auth/delegatable.py plugin (fully typed, pyright strict clean), registered in both plugins._BUILTINS and a nest.plugins.auth pyproject entry point. - delegated_auth scenario (coordinator + 3 intermediaries + 12 leaves) and 4 adversarial validators (tree_built, cascade, attacks_blocked, convergence) that PASS on delegatable and FAIL on jwt. - 3 attack classes defeated: scope escalation, stale/revoked parent, audience confusion. - 51 tests (example + hypothesis property-based + adversarial); full suite 592 passed. `make ci-local`: all 5 checks pass. - docs/layers/auth.md updated. Tests written by Claude. Co-Authored-By: Claude Opus 4.8 (1M context) <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] varuni7: delegatable capability tokens with cascading, partition-tolerant revocation
Problem 4 (auth). The default
jwtplugin cannot delegate a subset of acapability, and revokes by exact token string with no parent/child relationship.
This ships
delegatable: macaroon-style tokens where a holder mints a strictlynarrower, shorter-lived sub-token for another agent offline (no issuer
round-trip), and revoking a token severs its entire subtree.
Design
HMAC(parent_seal, caveat), so aholder can only add restrictions (subset scopes, shorter TTL, bound
audience) — never widen them.
delegate()also checks the scope subset andclamps child TTL ≤ parent.
string.
verify()recomputes the chain, and a revoked ancestor seal seversevery descendant — no per-child revocation list.
(G-Set CRDT).
mergeis the set union — commutative, associative,idempotent — so replicas converge by gossip regardless of order, duplication,
or loss. Safety is monotone (a seal once observed revoked is never accepted
again), and a verifier that has not yet heard a revocation still accepts until
it merges one.
uuid, notime.time(). Token identity is the seal (apure function of content + secret); all time comes from an injected clock.
Same seed → byte-identical trace.
Attacks defeated (adversarial validator)
The
delegated_authscenario + validators PASS ondelegatableand FAIL onjwt. Three attack classes are blocked, each a typed error:ScopeEscalationError.RevokedAncestorError.AudienceMismatchError.Plus a convergence check: after the revoked seal is gossiped, the revoked
intermediary can no longer mint tokens while its siblings still can — proof the
revocation propagated via CRDT merge, not just on the coordinator.
Verify it
Tests
hypothesisproperty tests (scope-subset,cascade over random chains, CRDT merge laws, determinism) + the 3 attacks +
partition convergence, for both the plugin and the validators.
make ci-local: all 5 checks pass(
ruff,ruff format,pyrightstrict,pytest).Files
packages/nest-plugins-reference/nest_plugins_reference/auth/delegatable.py— pluginplugins.py_BUILTINSandnest-plugins-reference/pyproject.tomlentry pointpackages/nest-core/nest_core/scenarios_builtin/delegated_auth.py+scenarios/delegated_auth.yamlnest_core/validators.pydocs/layers/auth.mdtest_delegatable_auth.py,test_delegated_auth_scenario.py— with Claude Code