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
cairn.jobs.*.<provider>jobs. - Fetches from the provider's API (using per-user tokens delivered over NATS).
- 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.
:::info Standalone by design
Earlier, the Strava worker shared Go packages with core via internal/. It was
extracted to be fully standalone (its own go.mod, a copied worker SDK + a
slim worker-side NATS client), proving the real contract is the wire format,
not shared code — exactly like the Python worker.
:::