Vote for your favorite SkillMD. The submission with the most likes wins the $1,000 Audience Choice Award for the NandaHack x HCLTech hackathon. Voting is open through September 25.Vote now →
in reviewhumanPR #67Registry

vladimirputkov: verified_registry — signature-verified registration with typed rejections and a Sybil scenario

Branch: hackathon/vladimirputkov-registry-integrity

Author

VladimirPutkov avatar

@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 diff

Checkout locally

git fetch origin pull/67/head:pr-67
git checkout pr-67