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>
This commit is contained in:
@@ -2,30 +2,49 @@
|
||||
|
||||
Third-party connectors and the token vault.
|
||||
|
||||
## Connector interface
|
||||
## Signal source interface
|
||||
|
||||
Each connector implements `SignalSource` from `@oo/shared-types`:
|
||||
|
||||
```ts
|
||||
interface Connector {
|
||||
id: string // e.g. "todoist"
|
||||
scopes: string[] // human-readable list shown in consent UI
|
||||
beginOAuth(user): Promise<{ redirectUrl, state }>
|
||||
finishOAuth(code, state): Promise<StoredCredential>
|
||||
fetchSignals(user, since?): AsyncIterable<NormalizedEvent>
|
||||
// incremental-sync cursor (Todoist sync_token, webhook timestamps, etc.)
|
||||
// stored in Credential.meta; the connector owns its shape.
|
||||
act?(user, action): Promise<void> // optional write-back (complete task, etc.)
|
||||
revoke(user): Promise<void> // REQUIRED: provider-side token revocation on disconnect
|
||||
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
|
||||
|
||||
- Credentials encrypted at rest (libsodium sealed box); key from env/KMS.
|
||||
- Refresh handled transparently; consumers never see raw tokens.
|
||||
- One row per `(user, provider)` with provider-specific `meta`.
|
||||
OAuth tokens stored in the `integration_tokens` SQLite table (`services/api/src/db/schema.ts`):
|
||||
|
||||
## Roadmap
|
||||
| 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` |
|
||||
|
||||
- Phase 0: **Todoist** (OAuth2, read tasks, complete task).
|
||||
- Phase 2: Google Calendar, Apple Health (web import), generic webhook ingress.
|
||||
- Phase 5: public SDK so third parties can ship connectors.
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user