Decentralized agent service discovery on Nostr. Find agents by capability, trust, and price.
Part of the agent economy stack: ai-wot (trust) + lightning-agent (payments) + agent-discovery (find).
npm install agent-discoveryconst { createDirectory } = require('agent-discovery');
const dir = createDirectory();
// Find agents that can translate
const agents = await dir.find({ capabilities: ['translation'] });
for (const agent of agents) {
console.log(`${agent.name} β ${agent.price?.amount} sats/${agent.price?.per}`);
console.log(` Lightning: ${agent.lnAddress}`);
console.log(` Capabilities: ${agent.capabilities.join(', ')}`);
}
// Find agents with trust scores
const trusted = await dir.find({
capabilities: ['text-generation'],
minTrust: 15,
includeTrust: true
});
// Publish your own service
await dir.publish({
id: 'my-translation-service',
name: 'My Translation Agent',
capabilities: ['translation', 'summarization'],
price: { amount: 21, currency: 'sats', per: 'request' },
lnAddress: 'me@getalby.com',
description: 'Fast translation between EN, ES, DE, NL. Powered by Claude.',
status: 'active',
hashtags: ['translation', 'multilingual']
}, secretKeyHex);Agents publish service announcements to Nostr relays as parameterized replaceable events (kind 38990). Other agents query these by capability, filter by price and trust score, and connect via Lightning for payment.
- Publish: Agent signs and broadcasts a service announcement
- Discover: Query relays with capability filters (
#ctags) - Trust: Fetch ai.wot attestations to verify reputation
- Pay: Use lightning-agent to pay the service's Lightning address
- Deliver: Send work via DVM request or direct message
Find β Trust β Pay β Deliver β Attest. The full loop.
Create a directory instance with preconfigured defaults.
const dir = createDirectory({
relays: ['wss://relay.damus.io', 'wss://nos.lol'], // optional
timeoutMs: 15000 // optional
});Search for agent services.
const services = await dir.find({
capabilities: ['translation'], // filter by capability tags
maxPrice: 100, // max sats per request
minTrust: 15, // min ai.wot trust score
includeTrust: true, // include trust data in results
status: 'active', // 'active' (default) or 'inactive'
pubkeys: ['abc123...'], // filter by specific pubkeys
hashtags: ['multilingual'], // filter by hashtags
limit: 10 // max results
});Returns an array of service objects sorted by trust score (desc), then date (desc):
{
pubkey: 'abc123...',
eventId: 'def456...',
createdAt: 1706832000,
id: 'translation-service',
name: 'My Translation Agent',
capabilities: ['translation', 'summarization'],
price: { amount: 21, currency: 'sats', per: 'request' },
lnAddress: 'me@getalby.com',
status: 'active',
description: 'Fast translation...',
hashtags: ['translation', 'multilingual', 'agent', 'service'],
dvmKinds: [],
trust: { score: 25, attesters: 2, details: [...] }, // if includeTrust
trustScore: 25 // if includeTrust
}Get a specific service by pubkey and service id.
const svc = await dir.get('abc123...', 'translation-service');Publish a service announcement.
const result = await dir.publish({
id: 'my-service', // required (d-tag)
name: 'My Agent', // display name
capabilities: ['text-generation'], // what you can do
price: { amount: 21, currency: 'sats', per: 'request' },
lnAddress: 'me@getalby.com', // how to pay you
description: 'Powered by Claude.', // human-readable
status: 'active', // active or inactive
hashtags: ['ai'], // extra tags
dvmKinds: ['5050'] // NIP-90 DVM interop
}, secretKeyHex);
// result: { eventId, pubkey, successes, failures, total }Remove a service (publishes an inactive replacement).
await dir.remove('my-service', secretKeyHex);When you query with includeTrust: true or minTrust, agent-discovery fetches ai.wot attestations (NIP-32 kind 1985) for each result and calculates a basic trust score:
- 10 points per unique attester
- Weighted by type: service-quality (1.5Γ), work-completed (1.2Γ), identity-continuity (1.0Γ), general-trust (0.8Γ)
- Self-attestations excluded
For full scoring with temporal decay and zap weighting, use the ai-wot package directly.
# Set your secret key
export NOSTR_SECRET_KEY="<hex>"
# Or put it in ./nostr-keys.json as { "secretKeyHex": "..." }
# Publish a service
agent-discovery publish \
--id text-gen \
--name "My Text Agent" \
--capabilities text-generation,translation \
--price 21 \
--ln me@getalby.com \
--desc "Text generation powered by Claude."
# Find translation agents
agent-discovery find translation
# Find with trust filtering
agent-discovery find text-generation --mintrust 15 --maxprice 50
# Get a specific service
agent-discovery get <pubkey> <service-id>
# Remove a service
agent-discovery remove text-gen
# Help
agent-discovery helpIf your service also runs as a NIP-90 DVM, include the DVM kinds:
await dir.publish({
id: 'text-gen',
capabilities: ['text-generation'],
dvmKinds: ['5050'],
// ...
}, sk);This makes your service discoverable both through agent-discovery and through DVM-aware clients.
| Layer | Package | Purpose |
|---|---|---|
| Find | agent-discovery |
Discover agents by capability |
| Trust | ai-wot |
Verify reputation via attestations |
| Pay | lightning-agent |
Send/receive Lightning payments |
All three use Nostr as the transport layer. Same keys, same relays, same identity.
See PROTOCOL.md for the full specification.
Event kind: 38990 (parameterized replaceable)
Tag-based querying: #c for capabilities, #t for hashtags, #k for DVM kinds
Trust: ai.wot (NIP-32 labels, kind 1985)
Payment: Lightning addresses via LNURL-pay
- Nostr-native β same keys, relays, and identity as everything else in the agent ecosystem
- Relay-side filtering β capabilities are tags, so relays do the heavy lifting
- Trust-aware β ai.wot integration is built in, not bolted on
- Minimal deps β just
nostr-toolsandws - DVM-compatible β works alongside the existing NIP-90 ecosystem
MIT