Protocol Documentation
Complete reference for the Decentralized Universal AI Discovery Protocol — endpoints, SDK usage, federation, and architecture.
Overview
DUADP is a federated discovery protocol for AI agents. It enables any agent, skill, or tool to be discovered across the open web without depending on a central registry.
The protocol is built on three layers:
DNS + WebFinger
Nodes advertise via DNS TXT records. Clients resolve agents using WebFinger (RFC 7033). Zero infrastructure beyond a domain.
Gossip Federation
Peer nodes exchange registrations via gossip protocol. Publish once, propagate everywhere. Eventual consistency across the mesh.
DID Identity
Every node has a did:web identity. Agent manifests carry verifiable credentials. Trust is cryptographic.
Quick Start
1. Install the package
npm install @bluefly/duadp2. Check the reference node
curl https://discover.duadp.org/.well-known/duadp | jq .3. Discover agents
curl https://discover.duadp.org/api/v1/agents | jq .agents[].name4. Search across everything
curl "https://discover.duadp.org/api/v1/search?q=code+review" | jq .5. Publish an agent
curl -X POST https://discover.duadp.org/api/v1/publish \
-H "Content-Type: application/json" \
-d @my-agent-manifest.jsonDiscovery API
Core endpoints for discovering agents, skills, and tools on a DUADP node. All responses are JSON.
/.well-known/duadpNode capabilities, version, and supported features
{
"protocol": "DUADP",
"version": "0.1.0",
"node": "discover.duadp.org",
"mcp_tools": 17,
"federation": "enabled",
"identity": {
"did": "did:web:discover.duadp.org",
"type": "reference-node"
}
}/api/v1/agentsList all registered agents with pagination
{
"agents": [
{
"id": "agt_doc123",
"name": "document-analyzer",
"version": "2.1.0",
"description": "Advanced document analysis",
"capabilities": ["text-extraction", "entity-recognition"],
"provider": "anthropic"
}
],
"total": 42,
"limit": 50,
"offset": 0
}/api/v1/skillsDiscover available skills across agents
{
"skills": [
{
"name": "code-review",
"description": "Automated code review with security analysis",
"agents": ["agt_review01", "agt_sec02"]
}
],
"total": 18
}/api/v1/toolsList published MCP-compatible tools
{
"tools": [
{
"name": "file-search",
"protocol": "MCP",
"description": "Full-text file search with regex support"
}
],
"total": 31
}Search
Full-text search across all registered agents, skills, and tools. Supports capability-based filtering, taxonomy paths, and faceted results.
/api/v1/searchQuery Parameters
| Param | Type | Description |
|---|---|---|
| q | string | Search query (natural language) |
| capability | string | Filter by capability (repeatable) |
| type | string | agent, skill, or tool |
| sort | string | relevance, created_at, name |
| limit | integer | Results per page (default 50, max 100) |
| offset | integer | Pagination offset |
curl "https://discover.duadp.org/api/v1/search?q=document+analysis&capability=text-extraction&capability=entity-recognition"{
"results": [
{
"id": "agt_doc123",
"name": "document-analyzer",
"relevance_score": 0.95,
"capabilities": ["text-extraction", "entity-recognition"],
"description": "Advanced document analysis with OCR"
}
],
"total": 15,
"facets": {
"capabilities": { "text-extraction": 15, "entity-recognition": 12 },
"types": { "agent": 10, "skill": 3, "tool": 2 }
}
}Registry
Publish agents, skills, and tools to the network. Validate OSSA manifests before publishing.
/api/v1/publishcurl -X POST https://discover.duadp.org/api/v1/publish \
-H "Content-Type: application/json" \
-d '{
"ossa_version": "0.4.6",
"name": "my-agent",
"version": "1.0.0",
"description": "My custom agent",
"capabilities": ["text-generation"],
"llm": { "provider": "anthropic", "model": "claude-sonnet-4-20250514" }
}'/api/v1/validatecurl -X POST https://discover.duadp.org/api/v1/validate \
-H "Content-Type: application/json" \
-d @my-agent-manifest.json
# Response:
# { "valid": true, "schema_version": "0.4.6", "errors": [], "warnings": [] }Federation
DUADP nodes form a federated mesh. Nodes discover each other via DNS, register as peers, and synchronize registrations via a gossip protocol.
How it works
- A node advertises itself via a DNS TXT record:
_duadp.example.com TXT "v=DUADP1 url=https://example.com/.well-known/duadp" - Other nodes discover it via DNS lookup or manual registration
- Peer nodes exchange registrations via
/api/v1/federation/gossip - Agents published on one node propagate across the mesh
curl https://discover.duadp.org/api/v1/federation/list
# {
# "peers": [
# { "url": "https://registry.example.com", "did": "did:web:registry.example.com", "status": "active" },
# { "url": "https://agents.corp.io", "did": "did:web:agents.corp.io", "status": "active" }
# ],
# "total": 2
# }curl -X POST https://discover.duadp.org/api/v1/federation/register \
-H "Content-Type: application/json" \
-d '{
"url": "https://my-node.example.com",
"did": "did:web:my-node.example.com",
"capabilities": ["agents", "skills", "tools"]
}'curl -X POST https://discover.duadp.org/api/v1/federation/gossip \
-H "Content-Type: application/json" \
-d '{
"source": "did:web:my-node.example.com",
"timestamp": "2026-03-06T12:00:00Z",
"entries": [
{ "type": "agent", "id": "agt_new01", "action": "register", "manifest": { ... } }
]
}'curl https://discover.duadp.org/api/v1/federation/peersIdentity & Governance
curl https://discover.duadp.org/api/v1/identity/node
# {
# "did": "did:web:discover.duadp.org",
# "type": "reference-node",
# "publicKey": "...",
# "created": "2026-01-15T00:00:00Z",
# "endpoints": {
# "discovery": "https://discover.duadp.org/.well-known/duadp",
# "federation": "https://discover.duadp.org/api/v1/federation/register"
# }
# }curl https://discover.duadp.org/api/v1/governance
# {
# "policies": {
# "registration": "open",
# "validation": "ossa-schema-required",
# "federation": "auto-accept",
# "retention_days": 365,
# "rate_limits": { "publish": "100/hour", "search": "1000/hour" }
# }
# }Operations
curl https://discover.duadp.org/api/v1/health
# { "status": "healthy", "uptime": "47d 12h", "version": "0.1.0" }curl https://discover.duadp.org/api/v1/metrics
# {
# "agents_registered": 42,
# "skills_registered": 18,
# "tools_registered": 31,
# "peers_connected": 2,
# "searches_24h": 1247,
# "publishes_24h": 8
# }SDK Usage
TypeScript
npm install @bluefly/duadpimport { UadpClient, resolveGaid } from '@bluefly/duadp';
const client = new UadpClient('https://discover.duadp.org');
// Discover the node
const manifest = await client.discover();
console.log(manifest.node_name, manifest.protocol_version);
// List agents
const agents = await client.listAgents({ limit: 10 });
console.log(`Found ${agents.meta.total} agents`, agents.data);
// List skills and tools
const skills = await client.listSkills();
const tools = await client.listTools();
// Search across everything
const results = await client.search({
q: 'document analysis',
kind: 'agent',
trust_tier: 'verified-signature',
facets: true,
});
// Publish an agent (requires token)
const authed = new UadpClient('https://discover.duadp.org', {
token: process.env.DUADP_TOKEN,
});
await authed.publishAgent({
name: 'my-agent',
version: '1.0.0',
description: 'Custom document analyzer',
capabilities: ['text-extraction'],
});
// Resolve a GAID URI directly
const { client: resolved, kind, name } = resolveGaid(
'duadp://skills.sh/skills/web-search'
);
const skill = await resolved.getSkill(name);
// Federation
const federation = await client.getFederation();
await authed.registerAsPeer({
url: 'https://my-node.example.com',
did: 'did:web:my-node.example.com',
});
// Advanced: reputation, governance, health
const rep = await client.getAgentReputation('duadp://acme.com/agents/scanner');
const gov = await client.getGovernance();
const health = await client.getHealth();Python
pip install duadpfrom duadp import DuadpClient
client = DuadpClient("https://discover.duadp.org")
# Discover the node
manifest = client.discover()
print(f"{manifest['protocol']} v{manifest['version']}")
# List agents
agents = client.list_agents(limit=10)
print(f"Found {agents['total']} agents")
for agent in agents["agents"]:
print(f" {agent['name']} — {agent['description']}")
# List skills and tools
skills = client.list_skills()
tools = client.list_tools()
# Search across everything
results = client.search(
q="document analysis",
capability="text-extraction",
kind="agent",
)
for r in results["results"]:
print(f" {r['name']} (score: {r['relevance_score']})")
# Publish an agent
import os
authed = DuadpClient("https://discover.duadp.org", token=os.environ["DUADP_TOKEN"])
authed.publish_agent({
"ossa_version": "0.4.6",
"name": "my-agent",
"version": "1.0.0",
"description": "Custom document analyzer",
"capabilities": ["text-extraction"],
})
# Federation
peers = client.federation_list()
authed.federation_register({
"url": "https://my-node.example.com",
"did": "did:web:my-node.example.com",
})
# Governance & health
governance = client.governance()
health = client.health()Advanced Patterns
Multi-Capability Filtering
Chain capability params to find agents matching multiple requirements:
curl "https://discover.duadp.org/api/v1/search?capability=text-extraction&capability=entity-recognition&capability=ocr"Federated Search
Federated search is the core differentiator of DUADP. When a client sets federated=true, the query fans out to every registered peer node in the mesh. Results stream back, are deduplicated by content hash, and merged into a single ranked response — all transparent to the caller.
How It Works
- Client sends query with
federated=true - Node fans out the query to all healthy peers in parallel
- Each peer searches its local registry and returns results
- Originating node deduplicates by
content_hashor GAID - Results are merged, ranked by relevance, and tagged with source node DID
- Circuit breaker skips peers that are slow or unreachable
Query Parameters
federatedboolean — enable mesh-wide search (default: false)max_hopsinteger — propagation depth limit (default: node config, typically 2)timeout_msinteger — per-peer timeout in milliseconds (default: 5000)include_sourceboolean — include source node DID on each result (default: true)curl "https://discover.duadp.org/api/v1/search?q=contract+analysis&federated=true" | jq .
{
"data": [
{
"kind": "Agent",
"metadata": { "name": "contract-analyzer", "version": "2.1.0" },
"identity": { "gaid": "agent://agents.enterprise.io/agents/contract-analyzer" },
"_source": { "node_id": "did:web:agents.enterprise.io", "node_name": "Enterprise AI" }
},
{
"kind": "Skill",
"metadata": { "name": "legal-review", "version": "1.0.0" },
"identity": { "gaid": "agent://community-agents.dev/skills/legal-review" },
"_source": { "node_id": "did:web:community-agents.dev", "node_name": "Community Agents" }
}
],
"meta": {
"total": 7,
"federated": true,
"sources": [
{ "node_name": "discover.duadp.org", "count": 3 },
{ "node_name": "Enterprise AI", "count": 2 },
{ "node_name": "Community Agents", "count": 2 }
]
}
}import { DuadpClient, deduplicateResources } from '@bluefly/duadp';
const client = new DuadpClient('https://discover.duadp.org');
// Federated search across the entire mesh
const results = await client.search({
q: 'contract analysis',
federated: true,
trust_tier: 'verified-signature',
facets: true,
});
console.log(`${results.meta.total} results from ${results.meta.sources?.length} nodes`);
// Results are already deduplicated, but you can also dedup manually:
const unique = deduplicateResources(results.data);from duadp import DuadpClient
async with DuadpClient("https://discover.duadp.org") as client:
results = await client.search(
q="contract analysis",
federated=True,
trust_tier="verified-signature",
)
for r in results.data:
print(f"{r.kind}: {r.metadata.name} (from {r._source.node_name})")Circuit Breaker Behavior
The SDK includes a built-in circuit breaker. If a peer fails 3 consecutive requests, it enters degraded state and is excluded from federated queries for 24 hours. After the cooldown, the next query probes it again. If it responds, it returns to healthy state. This prevents one slow node from degrading the entire mesh.
Running Your Own Node
DUADP is designed so any organization can run its own discovery node. You control your agent registry, your data stays on your infrastructure, and you choose which peers to federate with. The reference implementation runs on Node.js with SQLite — zero external dependencies.
1DNS Configuration
Add a DNS TXT record so other nodes can discover yours automatically. The record tells the federation mesh where to find your .well-known/duadp.json manifest.
# Add this to your DNS zone
_duadp.yourdomain.com IN TXT "v=DUADP1 url=https://yourdomain.com"
# Verify it propagated
dig TXT _duadp.yourdomain.com +short
# → "v=DUADP1 url=https://yourdomain.com"2Deploy the Server
The reference node is a lightweight Express server with SQLite storage. Deploy via npm or Docker.
npm install @bluefly/duadp
# Start the server
DUADP_NODE_NAME="My Registry" \
DUADP_PORT=3000 \
DUADP_DID="did:web:yourdomain.com" \
npx duadp-server
# Server starts at http://localhost:3000
# Manifest: /.well-known/duadp.json
# API: /api/v1/agents, /skills, /toolsdocker run -d \
--name duadp-node \
-p 3000:3000 \
-e DUADP_NODE_NAME="My Registry" \
-e DUADP_DID="did:web:yourdomain.com" \
-v duadp-data:/data \
ghcr.io/blueflyio/duadp-node:latest
# Data persists in the volume
# SQLite DB at /data/duadp.db3Configure Your Node
Customize your node via environment variables or a config file.
DUADP_NODE_NAME="My Agent Registry" # Human-readable name
DUADP_DID="did:web:yourdomain.com" # W3C DID for identity
DUADP_PORT=3000 # Server port
DUADP_DB_PATH="./data/duadp.db" # SQLite database location
DUADP_FEDERATION_GOSSIP=true # Enable gossip protocol
DUADP_FEDERATION_MAX_HOPS=2 # Gossip propagation depth
DUADP_CEDAR_CATALOG_PATH="./policies/" # Cedar policy directory
DUADP_LOG_LEVEL="info" # Logging verbosity4Join the Federation
Register your node with the reference node (or any peer) to join the mesh. Once registered, agents you publish will propagate to other nodes via gossip.
# Register your node
curl -X POST https://discover.duadp.org/api/v1/federation/register \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourdomain.com",
"name": "My Agent Registry",
"node_id": "did:web:yourdomain.com",
"capabilities": ["agents", "skills", "tools", "federation"]
}'
# Response:
# {
# "success": true,
# "peer": {
# "url": "https://yourdomain.com",
# "name": "My Agent Registry",
# "status": "healthy",
# "skill_count": 0,
# "agent_count": 0
# }
# }
# Verify federation
curl https://discover.duadp.org/api/v1/federation | jq '.peers[] | .name'5Publish Agents
Now publish agents to your node. They'll automatically propagate across the mesh.
curl -X POST https://yourdomain.com/api/v1/publish \
-H "Content-Type: application/json" \
-d '{
"apiVersion": "ossa/v0.5",
"kind": "Agent",
"metadata": {
"name": "my-custom-agent",
"version": "1.0.0",
"description": "My custom AI agent",
"tags": ["custom", "enterprise"]
},
"identity": {
"gaid": "agent://yourdomain.com/agents/my-custom-agent",
"did": "did:web:yourdomain.com:agents:my-custom-agent"
},
"spec": {
"agent_type": "worker",
"llm": { "provider": "anthropic", "model": "claude-sonnet-4-20250514" }
}
}'
# Agent is now discoverable on your node AND across the federationArchitecture
Each DUADP node is self-contained: Express HTTP server, SQLite database, optional Cedar policy engine. Nodes have no shared state — they synchronize via gossip. The reference implementation is ~2,000 lines of TypeScript with zero runtime dependencies beyond Express and better-sqlite3. You can also implement the protocol in any language — the spec is the REST API surface defined in the OpenAPI spec.
Resources
@bluefly/duadp on npm
TypeScript package with full type definitions
Source on GitLab
Framework source code and protocol implementation
GitHub Mirror
Read-only mirror for GitHub-based workflows
OSSA Standard
The contract layer specification DUADP builds on
Live Discovery
Browse agents, skills, and tools on the reference node
Reference Node
Live DUADP node at discover.duadp.org