mergedhumanPR #155Auth
Hackathon/parsa noah mesh revocable auth
delegatable (#138) solved delegation, but its revocation set is one in-process
Author
@parsaayoubi
github profile →- Status
- Merged
- Merged on
- Jul 10
- Branch
- hackathon/parsa-noah-mesh-revocable-auth
Description
The pitch.
# auth: `mesh_revocable` — revocation that survives a network partition ## Problem `delegatable` (#138) solved delegation, but its revocation set is one in-process `set[str]`, and `delegated_auth` hands **a single plugin instance to all sixteen agents** — so the cascade is global for free. It's one Python object. Give each agent its own replica, the only shape a real network permits, and the guarantee vanishes: a revocation at the issuer is invisible to every other verifier, forever. ## Solution `MeshRevocableAuth` subclasses `DelegatableAuth` — token format, HMAC chain, attenuation, exceptions all inherited verbatim, zero overrides — and reinterprets the inherited revocation set as a replica of a **grow-only set CRDT**. `export_revocations()` / `merge_revocations()` are the gossip channel. Union is commutative, associative and idempotent, so replicas converge under any delivery order (Hypothesis enforces the laws). Malformed gossip raises `RevocationStateError` and leaves the replica untouched. 150 lines. ## Evidence `nest run delegated_auth_partition` — 10 agents, seed 42, byte-identical across runs. Split at t=10; revoke the far subtree's grant at t=20 while it's unreachable; heal at t=55; gossip every 5 ticks. Far-side gateway on that lineage: ``` t= 3..51 accept (split open from t=10 — it cannot know) t=57..87 DENY (healed t=55; converged within one gossip round) ``` Same scenario, swapped plugin: | Wiring | liveness | converges | |---|---|---| | `delegatable`, one shared instance | ✗ denies impossibly fast | ✓ vacuously | | `delegatable`, one replica per agent | ✓ | **✗ stale forever** | | `mesh_revocable`, one replica per agent | ✓ | ✓ | That middle row is the gap. An end-to-end test drives the real simulator under both plugins and locks the flip into CI. Two validators encode both halves of the CAP position, so neither can rot: `check_partition_liveness` (an isolated verifier **must** keep accepting — denying instantly claims knowledge the network never delivered) and `check_revocation_converges` (after heal + a gossip bound nobody accepts, **and** a verifier other than the revoker explicitly denied — no vacuous pass). `check_no_stale_ancestor_use` is deliberately not asserted: unattainable under partition. The merged escalation and audience validators run green here. ## Supporting core change Partitions activated at the first event, so no scenario could establish cross-boundary state before the split. Adds `partition_start_at_time` / `partition_heal_at_time`, gating on **simulation time** — a gossiping plugin burns more events per tick, so `t=55` is event 702 under `mesh_revocable` and 339 under `delegatable`; an event-count window would put the two under *different* partitions and void the comparison. `partition_heal_at_tick` keeps its meaning and its test; `None` preserves today's behavior. ## Verification `make ci-local` green: ruff + format clean, pyright strict **0 errors**, **1192 passed** (`main` collects 1151; +42 here). `nest doctor` 7/7. `nest run delegated_auth` unchanged. Registered in `_BUILTINS` and as a `nest.plugins.auth` entry point. Docs: [`docs/layers/mesh_revocable_auth.md`](docs/layers/mesh_revocable_auth.md). ## Limits - **Revocation window** = partition duration + up to one gossip interval. The CAP price, bounded and explicit. - **Grow-only**, matching the base plugin's no-unrevoke semantics. - **Gossip is the deployment's job** — the plugin exposes the channel, not a transport. - **Symmetric secret, inherited** — any holder can also mint. - **Couples to the base plugin's private `_revoked`** — regression tests make an upstream refactor fail loudly, not silently.
Try it
Open PR on GitHubView diffCheckout locally
git fetch origin pull/155/head:pr-155
git checkout pr-155