Provider workers
A provider worker is a standalone process in its own repository. Core does not depend on any worker's code, and a worker does not depend on core's code. The entire contract between them is:
- The proto messages —
cairn.v1(activities, segments, metrics…) andcairn.worker.v1(Job,JobResult,WorkerEvent,WorkerManifest). - The NATS subjects — see the pipeline.
This is why the workers can be in different languages: cairn-provider-strava
is Go, cairn-provider-garmin is Python. Each commits its own generated proto
stubs (Go .pb.go / Python _pb2.py) so it builds with no buf/toolchain
dependency.
What a worker does
- Registers via the enrollment-token flow (NATS auth-callout mints scoped nkey credentials).
- Consumes its provider's jobs through durable pull consumers on the exact
cairn.jobs.<job_type>.<provider>subjects (fetch_source,reconcile,parse_blob,import_metrics,backfill) and servescairn.discover.<provider>. - Fetches from the provider's API (using per-user tokens or credentials
delivered over NATS:
cairn.tokens.<provider>.*,cairn.creds.<provider>.get). - Publishes
cairn.worker.v1.JobResultwithWorkerEvents (imported activities, laps, segment efforts, records, metrics). - Heartbeats its capability manifest (which data types it can read/backfill).
Write your own provider
Pick a language with protobuf + NATS support, generate the cairn.v1 +
cairn.worker.v1 stubs from the proto contract, implement the job loop, and
declare a capability manifest. The Go (strava) and Python (garmin) workers are
the two reference implementations — the Python one is the smaller, clearer
template since it implements the loop from scratch.
The normative checklist of everything a compliant worker MUST implement —
required consumers, heartbeats, claim-checked results, failure envelopes,
reconcile semantics — is the
provider contract, verifiable mechanically
with cairn-core's worker-conformance harness.
Each worker is a standalone repository with its own build and release cycle. The Go worker carries its own worker SDK and NATS client, the Python worker implements the loop directly; neither imports core. The contract is the wire format, not shared code.