Rust implementation of the agent:// URI scheme for topology-independent agent identity.
Agent URIs remain stable as agents migrate across infrastructure. When an agent moves between cloud regions, scales across replicas, or switches providers, its agent:// URI stays the same. Clients resolve the URI through a distributed hash table to find the current network location. No hardcoded endpoints, no broken references.
The scheme separates identity from location: a trust root anchors the agent to an organization, a capability path describes what it does, and a TypeID suffix uniquely identifies the instance. This provides DNS-like stability with built-in capability semantics and cryptographic attestation.
Add to your Cargo.toml:
[dependencies]
agent-uri = "0.5"Parse an existing URI:
use agent_uri::AgentUri;
let uri = AgentUri::parse(
"agent://anthropic.com/assistant/chat/llm_chat_01h455vb4pex5vsknk084sn02q"
).unwrap();
assert_eq!(uri.trust_root().host_str(), "anthropic.com");
assert_eq!(uri.capability_path().as_str(), "assistant/chat");
assert_eq!(uri.agent_id().prefix().as_str(), "llm_chat");Build a new URI with compile-time enforcement:
use agent_uri::{AgentUriBuilder, TrustRoot, CapabilityPath, AgentId};
let uri = AgentUriBuilder::new()
.trust_root(TrustRoot::parse("anthropic.com").unwrap())
.capability_path(CapabilityPath::parse("assistant/chat").unwrap())
.agent_id(AgentId::new("llm_chat")) // generates fresh UUIDv7
.build()
.unwrap();
println!("{}", uri);
// agent://anthropic.com/assistant/chat/llm_chat_01jk8m3v...The typestate builder catches missing components at compile time, not runtime.
┌─────────────────────────────┐
│ your application │
└─────────────────────────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐
│ agent-uri │ │ agent-uri- │ │ agent-uri-dht │
│ │ │ attestation │ │ │
│ parse, validate, │ │ │ │ discover agents │
│ construct URIs │ │ verify identity │ │ by capability │
└───────────────────┘ └───────────────────┘ └───────────────────┘
▲ │ │
│ │ │
└─────────────────────┴─────────────────────┘
depends on
Use agent-uri alone for parsing and validation. Add agent-uri-attestation when you need cryptographic proof of identity. Add agent-uri-dht when you need to discover agents by capability.
Validates URIs against the formal ABNF grammar and prevents invalid construction.
[dependencies]
agent-uri = "0.5"use agent_uri::{AgentUri, TrustRoot, CapabilityPath, AgentId};
// Parse and extract components
let uri = AgentUri::parse(
"agent://acme.corp/workflow/approval/rule_fsm_01h5fsk..."
).unwrap();
println!("Trust root: {}", uri.trust_root()); // acme.corp
println!("Capability: {}", uri.capability_path()); // workflow/approval
println!("Agent type: {}", uri.agent_id().prefix()); // rule_fsm
// Check capability hierarchy
let path = CapabilityPath::parse("workflow/approval/invoice").unwrap();
assert!(path.starts_with(&CapabilityPath::parse("workflow").unwrap()));Agent IDs use TypeID format: a semantic prefix plus a 26-character Crockford Base32 UUIDv7 suffix. The UUIDv7 provides time-ordering for distributed systems.
Feature flags:
serde- Serialize and deserialize all types
Proves an agent's identity and capabilities without contacting the issuer.
[dependencies]
agent-uri-attestation = "0.4"use agent_uri::AgentUri;
use agent_uri_attestation::{Issuer, Verifier, SigningKey};
use std::time::Duration;
// Trust root issues attestation
let uri = AgentUri::parse(
"agent://acme.com/workflow/approval/rule_01h455vb4pex5vsknk084sn02q"
).unwrap();
let signing_key = SigningKey::generate();
let issuer = Issuer::new("acme.com", signing_key.clone(), Duration::from_secs(86400));
let token = issuer.issue(&uri, vec!["workflow/approval".into()]).unwrap();
// Verifier checks token without callback
let mut verifier = Verifier::new();
verifier.add_trusted_root("acme.com", signing_key.verifying_key());
let claims = verifier.verify(&token).unwrap();
assert_eq!(claims.agent_uri, uri.to_string());Tokens use PASETO v4.public (Ed25519 signatures). The URI path is identity-defining: every attested capability must equal that path or be its descendant. Changing the path creates a different agent identity and requires a new Agent ID and attestation.
Finds agents by what they do, not where they are.
[dependencies]
agent-uri-dht = "0.2"use agent_uri::{AgentUri, TrustRoot, CapabilityPath};
use agent_uri_attestation::{Issuer, SigningKey, Verifier};
use agent_uri_dht::{Dht, SimulatedDht, Registration, Endpoint, SimulationConfig};
use std::time::Duration;
// Agent registers its current location
let uri = AgentUri::parse(
"agent://anthropic.com/assistant/chat/llm_01h455vb4pex5vsknk084sn02q"
).unwrap();
let signing_key = SigningKey::generate();
let issuer = Issuer::new(
"anthropic.com",
signing_key.clone(),
Duration::from_secs(3600),
);
let token = issuer.issue(&uri, vec!["assistant/chat".into()]).unwrap();
let mut verifier = Verifier::new();
verifier.add_trusted_root("anthropic.com", signing_key.verifying_key());
let dht = SimulatedDht::with_verifier(SimulationConfig::default(), verifier);
let registration = Registration::new(
uri.clone(),
vec![Endpoint::https("us-east-1.agent.anthropic.com")]
).with_attestation(token);
dht.register(registration).unwrap();
// Client discovers by capability prefix
let results = dht.lookup_prefix(
&TrustRoot::parse("anthropic.com").unwrap(),
&CapabilityPath::parse("assistant").unwrap(),
).unwrap();
assert!(!results.is_empty());DHT keys are derived as SHA-256(trust_root || "/" || capability_path). A registration is replicated to its exact path key and every ancestor key, so lookup_prefix performs one exact-key lookup and returns that subtree. This costs O(path depth) writes and can make broad ancestor keys hot.
Feature flags:
serde- Serialize and deserialize types (enablesagent-uri/serde)
agent://trust-root/capability/path/prefix_01h455vb4pex5vsknk084sn02q
└────┬────┘└────┬─────┘└──────────────┬──────────────────┘
authority capability agent id
| Component | Constraint | Example |
|---|---|---|
| Trust root | Max 128 chars; domain, IPv4, or IPv6 with optional port | anthropic.com, 192.168.1.1:8080 |
| Capability path | Max 256 chars, max 32 segments of 1-64 chars each | assistant/chat, workflow/approval/invoice |
| Agent ID | Max 90 chars; prefix + _ + 26-char Base32 suffix |
llm_chat_01h455vb4pex5vsknk084sn02q |
| Full URI | Max 512 chars |
Query strings and fragments are supported but stripped for identity comparison and DHT key derivation.
See SPECIFICATION.md for the complete formal specification, including ABNF grammar, normalization rules, DHT key derivation algorithm, attestation claims structure, and security considerations.
Conformance vectors for the current lowercase-by-grammar behavior are in
test-vectors.json (v0.5). The published v0.4 behavior,
which permitted implementations to normalize some uppercase path and Agent ID
inputs, remains archived in test-vectors-v0.4.json
for reproducibility. Version 0.5 rejects those inputs so identity material is
never silently rewritten.
This implementation is based on the research paper:
Agent URI: A Topology-Independent Identity Scheme for Agentic Systems arXiv:2601.14567
MIT OR Apache-2.0