Intelligence Registry
Service discovery for AI and intelligence capabilities
What Is It?
A registry where AI services announce their capabilities.
- Services register what they can do
- Data Pod discovers available services
- Frontends adapt to available capabilities
Why Use It?
✅ Dynamic discovery: Find services at runtime
✅ Decoupled: Services independent of Data Pod
✅ Capability-based: Route by what service can do
✅ Frontend-aware: UI adapts to available services
Registration
Register a Service
POST /trpc/intelligenceRegistry.register
{
serviceId: 'my-ai-service', // Unique ID
name: 'My AI Service', // Display name
webhookUrl: 'https://my-service.com/webhook',
apiKey: 'secret-key-for-callbacks', // For Data Pod → Service auth
capabilities: [ // What can it do?
'lifefeed-analysis',
'document-summary',
'sentiment-analysis'
],
pricing: 'free', // or 'paid'
version: '1.0.0'
}
What Gets Stored
{
id: 'gen_123', // Auto-generated
serviceId: 'my-ai-service',
name: 'My AI Service',
webhookUrl: 'https://...',
apiKey: 'secret...', // Encrypted
capabilities: [...],
pricing: 'free',
version: '1.0.0',
status: 'active', // active | inactive
registeredAt: '2024-...',
lastActiveAt: '2024-...'
}
Discovery
List All Services
const services = await client.rpc.intelligenceRegistry.list.query();
// Returns:
[
{
id: 'gen_123',
serviceId: 'lifefeed-ai',
name: 'Life Feed Intelligence',
capabilities: ['lifefeed-analysis'],
pricing: 'free',
status: 'active'
}
]
Capabilities API
Frontend-friendly discovery:
const capabilities = await client.capabilities.list();
// Returns:
{
core: {
version: '1.0.0',
features: ['notes', 'tasks', 'chat', ...]
},
intelligenceServices: [
{
id: 'gen_123',
serviceId: 'lifefeed-ai',
name: 'Life Feed Intelligence',
capabilities: ['lifefeed-analysis'],
pricing: 'free',
version: '1.0.0'
}
]
}
Service Selection
Capability-Based Routing
Data Pod selects services by capability:
// Find service that can analyze inbox items
const services = await db
.select()
.from(intelligenceServices)
.where(eq(intelligenceServices.status, 'active'));
const lifefeedService = services.find(s =>
s.capabilities.includes('lifefeed-analysis')
);
if (lifefeedService) {
await fetch(lifefeedService.webhookUrl, {
method: 'POST',
headers: {
'Authorization': `Bearer ${lifefeedService.apiKey}`
},
body: JSON.stringify({ ... })
});
}
Integration Patterns
Pattern 1: Event-Driven
Event triggers service call:
Event published (inbox.item.received)
↓
Event handler queries registry
↓
Finds service with 'lifefeed-analysis' capability
↓
Calls service webhook
↓
Service processes and calls back
↓
Callback publishes new event (inbox.item.analyzed)
Pattern 2: On-Demand
Frontend requests service directly:
// Frontend
const capabilities = await client.capabilities.list();
const hasAnalysis = capabilities.intelligenceServices.some(
s => s.capabilities.includes('document-summary')
);
if (hasAnalysis) {
// Show "Summarize" button
}
Authentication
Data Pod → Service
Data Pod uses service's API key:
await fetch(service.webhookUrl, {
headers: {
'Authorization': `Bearer ${service.apiKey}`
}
});
Service → Data Pod
Service needs its own auth:
// When registering, generate API key for callbacks
const callbackKey = await client.rpc.apiKeys.create.mutate({
name: 'My AI Service Callback',
scopes: ['write:events']
});
// Service stores this key
process.env.DATA_POD_API_KEY = callbackKey.key;
// Service uses it for callbacks
await fetch(dataPodCallbackUrl, {
headers: {
'X-Webhook-Secret': process.env.DATA_POD_API_KEY
}
});
Management
Update Service
await client.rpc.intelligenceRegistry.update.mutate({
id: 'gen_123',
updates: {
capabilities: ['lifefeed-analysis', 'email-triage'], // Add capability
version: '1.1.0'
}
});
Deactivate Service
await client.rpc.intelligenceRegistry.updateStatus.mutate({
id: 'gen_123',
status: 'inactive'
});
Health Check
await client.rpc.intelligenceRegistry.heartbeat.mutate({
id: 'gen_123'
});
// Updates lastActiveAt timestamp
Real Example
Life Feed Intelligence Service:
// On startup, register
const response = await fetch(
`${process.env.DATA_POD_URL}/trpc/intelligenceRegistry.register`,
{
method: 'POST',
body: JSON.stringify({
serviceId: 'lifefeed-intelligence',
name: 'Life Feed Intelligence',
webhookUrl: `${process.env.SERVICE_URL}/analyze`,
apiKey: process.env.WEBHOOK_SECRET,
capabilities: ['lifefeed-analysis'],
pricing: 'free',
version: '1.0.0'
})
}
);
Data Pod event handler:
// When inbox item received, find service
const services = await db
.select()
.from(intelligenceServices)
.where(eq(intelligenceServices.status, 'active'));
const lifefeedService = services.find(s =>
s.capabilities.includes('lifefeed-analysis')
);
// Call it
await fetch(lifefeedService.webhookUrl, {
method: 'POST',
headers: {
'Authorization': `Bearer ${lifefeedService.apiKey}`
},
body: JSON.stringify({
itemId: event.subjectId,
data: event.data,
callbackUrl: `${process.env.API_URL}/webhooks/intelligence/callback`
})
});
Best Practices
✅ Use specific capability names: lifefeed-analysis not analysis
✅ Version your services: Semantic versioning
✅ Heartbeat regularly: Keep lastActiveAt updated
✅ Handle failures gracefully: Service might be down
✅ Validate callbacks: Check webhook secrets
Next Steps
- Build a remote service → Remote Plugins Guide
- See complete example → Life Feed Implementation
- Understand hybrid pattern → Hybrid Plugins