trails/docs/adr/0001-connected-services-credential-discriminator.md
Ullrich Schäfer cfba3146e2
Add deepen-connected-services architecture artifacts
Reshape the sync-providers seam before Komoot (web-login) and Apple
Health (device) adapters land. Captures the decisions in three ADRs,
seeds CONTEXT.md with Connected Services vocabulary, and proposes the
OpenSpec change covering schema rename + ConnectedServiceManager +
capability seams (Importer / RoutePusher / WebhookReceiver).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 01:14:38 +02:00

19 lines
1.2 KiB
Markdown

# Connected Services use a credential-kind discriminator + JSONB blob
trails.cool integrates with external services whose credentials have
fundamentally different shapes: OAuth2 (Wahoo, future Coros/Garmin/Strava),
web-login session (Komoot — no official API, we authenticate against the
provider's web login with email + password and reuse the cookie jar),
and device pairing (Apple Health, future Health Connect — no remote
credential at all). We considered an OAuth-only schema with nullable extras
and per-kind tables, and rejected both: nullable-OAuth would force Komoot
and Apple Health to leave most columns NULL and bake OAuth assumptions into
queries; per-kind tables would fragment `connected_services` and complicate
the user-facing "Connected Services" list.
We store all linked external accounts in one `connected_services` table
with a `credential_kind ∈ {oauth, web-login, device}` discriminator and a
`credentials` JSONB blob whose schema is determined by kind. OAuth-specific
`granted_scopes` stays a top-level column because feature gates query it
directly. Per-kind `CredentialAdapter` modules own the credential lifecycle
(refresh / relogin / noop); callers never read the JSONB directly.