Registration and Discovery

Registration is service discovery without a discovery daemon. When a managed resource reaches a stable state, it publishes an entry into a NATS JetStream stream. Other nodes read those entries, usually from a template, to build configuration such as a load-balancer upstream or a Prometheus target list. Publishing is a side effect of apply; lookup is a template function or a CLI query.

Where it lives

registration: the transport, subject grammar, stream, lookup, and watch. model/registration.go and model/registration_ttl.go: the entry and TTL data model. Key files: registration/subject.go, registration/stream.go, registration/nats.go, registration/lookup.go, registration/watch.go.

The publish gate

A resource carries a register_when_stable list. After the resource applies, publishRegistration (resources/apply/apply.go:736) decides whether to announce it. The gate is conservative: it publishes only when the resource has entries, the manager is not in noop mode, the event did not fail, and every health check returned OK. A resource with no health checks passes that last test.

Load-bearing decision

A missing registration publisher is tolerated, not fatal. The manager defaults to a no-op publisher, so a register_when_stable manifest runs safely on a node with no registration backend. This is what lets the same manifest apply on nodes with and without NATS.

A KV store built from a stream

Node A · applyresource stable +health checks passJetStreamREGISTRATION1 msg / subjectTTL · rollup · markersNode B · lookup()renders configWatcherregister / removepublishchoria.ccm.registration.v1.<cluster>.<protocol>.<service>.<address>.<instance>
The stream behaves like a KV store keyed on the entry tuple; readers take the last message per subject.

Each entry maps to a single subject. The address contributes its IP with dots turned into underscores so it is one token, and an InstanceId FNV hash of the full tuple forms the final token, so every unique cluster, service, protocol, address, and port maps to one stable subject. The stream captures the whole namespace with MaxMsgsPerSubject: 1 and AllowRollup, and reliable publishes carry a Nats-Rollup: sub header.

Load-bearing decision

One message per subject plus rollup plus a hashed per-instance subject makes the stream behave like a KV store keyed on the entry tuple. Re-registering overwrites in place, and a read is simply “last message per subject.” Full KV semantics only hold with the JetStream destination; the plain nats destination is best-effort and omits the TTL and rollup headers.

Expiry, lookup, and watch

Expiry is two-tiered. A per-entry Nats-TTL header sits under the stream-wide MaxAge, and on removal the server writes a subject delete marker so watchers see the removal rather than a silent disappearance. The stream denies hard deletes but allows purge, so JetStreamRemove can retire a single entry.

Lookup
JetStreamLookup builds a wildcard filter subject, fetches the last message per matching subject, drops server marker messages, and returns entries sorted by address then port.
Watch
JetStreamWatch streams WatchEvents: normal messages become Register events, marker messages become Remove events with the entry reconstructed from the subject.
From a template
The manager exposes lookup as registrations(cluster, protocol, service, ip), so a rendered config file discovers its peers directly.
Prometheus
RegistrationEntries.PrometheusFileSD() converts entries into file_sd JSON, grouped by cluster, service, and protocol.
Load-bearing decision

Both lookup and watch special-case the Nats-Marker-Reason header, per ADR-43. Lookup drops markers; watch turns them into removals. A reader that ignored markers would surface expired or purged entries as live services.

The Priority and Annotations fields travel on every entry but, within this subsystem, are only consumed by the Prometheus conversion. Priority is validated and sorted around but not otherwise acted on here, reserved for downstream consumers such as SRV-style weighting.

Next

Continue to Observability to see how each apply is recorded, measured, and health-checked.