in reviewhumanPR #67Registry
vladimirputkov: verified_registry — signature-verified registration with typed rejections and a Sybil scenario
Branch: hackathon/vladimirputkov-registry-integrity
Author
@VladimirPutkov
github profile →- Status
- In review
- Opened on
- Jul 5
- Branch
- hackathon/vladimirputkov-registry-integrity
Description
The pitch.
# [Hackathon] vladimirputkov: verified_registry — signature-verified registration with typed rejections and a Sybil scenario **Branch:** `hackathon/vladimirputkov-registry-integrity` **Base:** `main` **Layer:** registry **Scope:** self-scoped registry improvement because Problem 06 was already completed by #24 ## Problem The default `in_memory` registry trusts every call to `register(card)`. An attacker can therefore: - register an `AgentCard` under another agent’s `agent_id`; - silently overwrite the authentic card; - advertise capabilities it has not proved it owns; - mutate a valid card after signing it. The identity layer already provides signing plugins such as `did_key` and `ed25519_rotating`, but the default registration path does not use them. This PR adds an authenticated registration boundary to the registry layer. ## Implementation ### `registry: verified` `VerifiedRegistry` is a drop-in, non-replicated `Registry` implementation registered through the `nest.plugins.registry` entry-point group. A card is admitted only if it carries a valid identity signature from the same agent named by its `agent_id`. The signature covers canonical card bytes containing: - `agent_id`; - `name`; - sorted, order-independent `capabilities`; - `endpoint`. `metadata` is deliberately excluded because it carries the signature and optional identity fields such as `key_id` and `signed_at`. Cryptographic verification remains delegated to the configured `Identity` plugin. The registry itself implements no cryptography. ### Typed rejection reasons Rejected registrations raise `RegistrationRejectedError` with a stable reason: | Attack or failure | Reason | |---|---| | Missing or malformed signature | `missing_signature` | | Signature created by an agent other than the claimed `agent_id` | `signer_mismatch` | | Forged signature, unknown signer, or post-signing card mutation | `bad_signature` | A rejected registration is atomic: it does not modify the store or notify subscribers. ## Adversarial scenario `scenarios/registry_integrity.yaml` runs seven deterministic agents with seed `42`: - three honest agents sign and register their own cards; - one Sybil agent attempts impersonation; - one Sybil agent submits an unsigned card; - one Sybil agent signs its own card and then mutates it to claim an honest capability; - one auditor checks the resulting discovery state. The auditor records four properties for every honest capability: - whether the authentic owner remains present; - whether the owner’s card still points to its authentic endpoint; - whether another `agent_id` is squatting on the capability; - whether each attack was accepted or rejected with the correct typed reason. ## Validators `VALIDATORS["registry_integrity"]` contains four implementation-independent checks: - `registry_unauthenticated_rejected` — every adversarial attempt must be rejected and the trace must contain real attacks; - `registry_honest_admitted` — honest signed registrations must still succeed; - `registry_rejection_reasons` — each attack must produce the correct typed reason; - `registry_authentic_discovery` — every honest owner must remain discoverable and authentic, with no capability squatting. The validators judge observable trace behavior rather than inspecting `VerifiedRegistry` internals. ## Differential proof The same scenario is tested against both registries. ### `registry: verified` - all honest registrations are accepted; - all three attacks are rejected; - every rejection carries the expected typed reason; - authentic discovery is preserved; - `squatting=0`; - all four validators pass. ### `registry: in_memory` The scenario still completes without crashing, but: - all three attacks are accepted; - the victim’s card is overwritten; - the poisoned lookup reports `present=1, authentic=0`; - the tampered Sybil card claims …
Try it
Open PR on GitHubView diffCheckout locally
git fetch origin pull/67/head:pr-67
git checkout pr-67