Expand description
Capability-based DHT discovery for the agent:// URI scheme.
This crate provides DHT (Distributed Hash Table) infrastructure for discovering agents by their capabilities. It includes:
- Key derivation:
DhtKeyfor Kademlia-style routing - Registration records:
Registrationwith endpoints and attestations - Trait interface:
Dht, an async trait abstracting DHT backends - In-memory simulation:
SimulatedDhtfor evaluation and testing - Prefix matching:
PathTrie, a standalone helper for callers that want a local hierarchical index. Discovery itself does not use it: in this crate a prefix lookup reads one materialized ancestor key. A networked backend reads pointers from that key instead, which is one of several differences the Scope section below sets out.
§Overview
SimulatedDht implements the direct record model of SPECIFICATION.md
§6.2: agents register at keys for their exact capability path and every
ancestor, and a prefix lookup is one exact-key read.
key = SHA256(trust_root || "/" || capability_path)That model holds where one store owns the namespace and a stored value has
no practical size bound, which is what an in-process index is. A Kademlia
overlay is not, and is required to use the sharded model instead; see
agent-uri-dht-libp2p. The
Dht trait is the same either way.
This enables capability-based discovery: “find me an agent at
anthropic.com that can do assistant/chat”.
§Scope
SimulatedDht is the reference implementation and the test double: one
in-process index holding one authoritative copy, which is what you want to
develop, test, and benchmark against. It is not a deployment, and two things
follow from that.
Its limits are its own. SimulationConfig bounds a key at 1000
registrations by default and bounds a stored value not at all. A Kademlia
overlay bounds a record at the wire size a peer will accept, which for
libp2p-kad is 1 to 27 registrations, so it stores pointers at ancestor keys
and sharded pages behind them. Code that asks whether a write succeeded and
pages through what a lookup returned ports unchanged. Configuration tuned
against these numbers does not.
A measurement taken against it is a measurement of indexing. Nothing here
partitions, replicates, retries, or loses a peer, so a lookup returns exactly
what was written to it. That makes it a clean way to ask whether this crate
indexes and pages what it was given correctly, and no way at all to ask what
a distributed store returns under load or partition. The discovery precision
and recall figures in agent-uri-eval were measured against this crate and
are bounded by both facts; see its crate docs before quoting them.
§Quick Start
use agent_uri::{AgentUri, CapabilityPath, TrustRoot};
use agent_uri_attestation::SigningKey;
use agent_uri_dht::{
Dht, Endpoint, MutationProof, Query, ReadOptions, Registration, SimulatedDht,
SimulationConfig, WriteOptions,
};
use futures::executor::block_on;
// Disable verification only for this isolated indexing example. Production
// registration uses the secure default and a configured Verifier.
let dht = SimulatedDht::new(
SimulationConfig::default().with_verify_attestations(false)
);
// Register an agent. The key it names is the only one that will be able to
// change or remove the record later.
let uri = AgentUri::parse(
"agent://anthropic.com/assistant/chat/llm_01h455vb4pex5vsknk084sn02q"
).unwrap();
let endpoint = Endpoint::https("agent.anthropic.com:443");
let agent_key = SigningKey::generate();
let registration = Registration::new(uri, agent_key.verifying_key(), vec![endpoint]);
let proof = MutationProof::sign_registration(&agent_key, ®istration);
// Every operation is async, because a real backend crosses the network.
block_on(dht.register(registration, &proof, WriteOptions::default())).unwrap();
// Discover agents by capability. Results are paged.
let query = Query::prefix(
TrustRoot::parse("anthropic.com").unwrap(),
CapabilityPath::parse("assistant").unwrap(),
);
let page = block_on(dht.lookup(&query, &ReadOptions::default())).unwrap();
assert_eq!(page.len(), 1);
assert!(!page.has_more());§Operating Against a Network
Dht describes a distributed store, not a map, and its shape reflects
that even when the backend is SimulatedDht:
- Every operation is async and takes a deadline.
WriteOptionsandReadOptionscarry a timeout; a call that does not finish returnsDhtError::Timeout, which does not mean the write failed to apply. - Writes name a
Quorum. How many replicas must acknowledge is the caller’s latency-versus-durability trade, andWriteReceiptreports what was actually reached. - Lookups are paged.
Dht::lookupreturns aPage; followPage::next_cursoruntil it isNoneor you are sampling rather than enumerating. - Failures are distinguishable.
DhtError::is_transientseparates a slow or partitioned network from a rejected request. Collapsing the two turns a partition into an apparently empty namespace. - Writes are authorized, not merely addressed. An agent URI is public,
and so is the attestation token stored beside it, so every write carries a
MutationProofsigned by the agent’s own key. Without this, reaching a replica would be enough to repoint an agent.
SimulatedDht honors all of this against one in-process copy, so code
written against it does not acquire habits a real backend will punish.
§Key Derivation
DHT keys are derived deterministically from trust root and capability path using SHA-256. This enables:
- Exact lookup: find agents at a specific capability path
- Prefix lookup: find agents at a path and every path beneath it, from the single key that path derives
There is deliberately no cross-trust-root lookup. Every key is derived from one trust root, so a query is answered by one authority, and a compromised trust-root key reaches no further than the namespace it already owned.
use agent_uri::{TrustRoot, CapabilityPath};
use agent_uri_dht::DhtKey;
let trust_root = TrustRoot::parse("anthropic.com").unwrap();
let path = CapabilityPath::parse("assistant/chat").unwrap();
// Derive a key for this capability
let key = DhtKey::derive(&trust_root, &path);
// Keys are deterministic
let key2 = DhtKey::derive(&trust_root, &path);
assert_eq!(key, key2);
// Partial depth for hierarchical queries
let parent_key = DhtKey::derive_at_depth(&trust_root, &path, 1).unwrap();§Agent Migration
The identity stability of agent:// URIs means agents can migrate between network locations without changing their identity:
use agent_uri::AgentUri;
use agent_uri_attestation::SigningKey;
use agent_uri_dht::{
Dht, Endpoint, Mutation, MutationProof, Registration, SimulatedDht, SimulationConfig,
WriteOptions,
};
use futures::executor::block_on;
let dht = SimulatedDht::new(
SimulationConfig::default().with_verify_attestations(false)
);
let uri = AgentUri::parse(
"agent://anthropic.com/assistant/chat/llm_01h455vb4pex5vsknk084sn02q"
).unwrap();
let agent_key = SigningKey::generate();
// Register at initial location
let registration = Registration::new(
uri.clone(),
agent_key.verifying_key(),
vec![Endpoint::https("us-east-1.agent.anthropic.com")]
);
let registration_proof = MutationProof::sign_registration(&agent_key, ®istration);
block_on(dht.register(registration.clone(), ®istration_proof, WriteOptions::default()))
.unwrap();
// Migrate to new location (same identity). The proof is signed against the
// record as it stands, so an intercepted migration cannot be re-aimed at
// other endpoints, applied twice, or replayed after the agent re-registers.
let endpoints = vec![Endpoint::https("eu-west-1.agent.anthropic.com")];
let mutation = Mutation::UpdateEndpoint {
endpoints: &endpoints,
expires_at: registration.expires_at(),
};
let proof = MutationProof::sign_next(&agent_key, ®istration, &mutation);
block_on(dht.update_endpoint(
&uri,
endpoints.clone(),
&proof,
WriteOptions::default(),
)).unwrap();Structs§
- Cursor
- An opaque position in a paginated lookup.
- DhtKey
- DHT key derived from trust root and capability path.
- DhtStats
- Statistics about the DHT state.
- Endpoint
- Network endpoint for contacting an agent.
- Migration
Result - Result of a simulated migration operation.
- Mutation
Proof - Authorization for one write to one registration.
- NodeId
- The identity of a node participating in the DHT.
- Page
- One page of lookup results.
- Path
Trie - Trie structure for efficient prefix matching on capability paths.
- Peer
Addr - The address of a peer used to join the overlay.
- Query
- A capability lookup.
- Read
Options - Options for operations that read from the DHT.
- Record
Version - Which record, and where in that record’s history, a write applies.
- Registration
- A registration record stored in the DHT.
- Simulated
Dht - Simulated DHT for evaluation.
- Simulation
Config - Configuration for the simulated DHT.
- Write
Options - Options for operations that write to the DHT.
- Write
Receipt - The outcome of a write that had to reach a quorum of replicas.
Enums§
- DhtError
- Errors that can occur during DHT operations.
- Match
Mode - How a capability path is matched during lookup.
- Mutation
- A write to an existing registration, with the parameters that write carries.
- Mutation
Kind - The operation a proof authorizes.
- Quorum
- How many replicas must respond for an operation to be considered successful.
Traits§
- Dht
- Abstract DHT operations.
Functions§
- signing_
payload - Returns the exact bytes a proof for this mutation signs.