Files
oO/services/integrations
alvis cba3f1a184 docs(services): update integrations + recommender READMEs for signal abstraction (#78)
integrations/README — replace stale Connector interface and fictional
libsodium vault with the actual SignalSource pattern, SQLite token table,
and real OAuth routes.

recommender/README — document the SignalAggregator pipeline, current
policy registry, and actual /recommend + /feedback contract shapes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 17:17:38 +00:00
..

integrations

Third-party connectors and the token vault.

Signal source interface

Each connector implements SignalSource from @oo/shared-types:

interface SignalSource {
  readonly id: string                                       // e.g. "todoist"
  fetchSignals(userId: string): Promise<Signal[]>          // returns normalized Signal[]
  act?(userId: string, signalId: string, action: string): Promise<void>  // optional write-back
}

SignalAggregator (services/api/src/signals/aggregator.ts) fans out to all registered sources in parallel, isolating per-source failures.

Token vault

OAuth tokens stored in the integration_tokens SQLite table (services/api/src/db/schema.ts):

Column Description
userId owner
provider e.g. todoist
accessToken OAuth access token (plain in dev; encrypted in prod via server secret store)
tokenStatus active | needs_reconnect

On a 401 from the upstream API, the connector marks the token needs_reconnect and publishes signals.integration.token_expired so the client can prompt re-auth.

Routes

Method Path Description
GET /api/integrations List connected integrations for current user
GET /api/integrations/todoist/connect Start Todoist OAuth flow
GET /api/integrations/todoist/callback OAuth callback — exchange code, store token
DELETE /api/integrations/:provider Disconnect + delete token

Connectors

Connector Status Signals produced
Todoist Phase 1 — active task signals (today + overdue); done write-back
Google Calendar Phase 2 — planned event signals

Extraction criteria

Extract to its own process when credential blast-radius isolation requires it (e.g. token vault with KMS-backed encryption needs to run in a hardened sidecar) or when connector volume justifies separate scaling.