SynapSynapDocs
DevelopmentExtending

Intelligence Registry

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