Back to Spy NATS
Spy NATS Blog/ Traceable Production Audit Trails with JetStream Sourcing

Traceable Production Audit Trails with JetStream Sourcing

A manufacturing use case: every production-line event captured immutably in JetStream for compliance audits, plus stream sourcing/mirroring to combine multiple lines into one traceability view without republishing anything.

4 min read
  • nats
  • jetstream
  • manufacturing
  • compliance
  • tutorial

The scenario

A factory with several production lines must keep an immutable, queryable audit trail of every step a part goes through (for recalls and regulatory compliance) — while each line's own systems still want their own independent, line-scoped stream for local tooling.

Step 1 — server + one stream per line

terminal 1
docker run -d --name nats-server -p 4222:4222 -p 8222:8222 -v nats-js-store:/data nats:latest -js -sd /data
nats context add local --server="nats://localhost:4222" --select
terminal 2 — line 1's own stream
nats stream add LINE_1 \
  --subjects "line.1.event" \
  --storage file --retention limits \
  --max-age=365d --max-msgs=-1 --max-bytes=-1 --defaults
terminal 2 — line 2's own stream
nats stream add LINE_2 \
  --subjects "line.2.event" \
  --storage file --retention limits \
  --max-age=365d --max-msgs=-1 --max-bytes=-1 --defaults

Step 2 — each line publishes independently

terminal 3
nats pub line.1.event '{"part":"P-1001","step":"weld","result":"pass"}'
nats pub line.1.event '{"part":"P-1002","step":"weld","result":"fail","reason":"misalign"}'
nats pub line.2.event '{"part":"P-2001","step":"paint","result":"pass"}'

Step 3 — a factory-wide audit stream, sourced from both, no republishing

Instead of every line also publishing a second copy to a shared subject (which risks drift between the two), a sourced stream pulls messages from other streams directly, at the JetStream level:

terminal 2 — combine LINE_1 and LINE_2 into one audit trail
nats stream add FACTORY_AUDIT \
  --source LINE_1 --source LINE_2 \
  --storage file --retention limits \
  --max-age=2555d --max-msgs=-1 --max-bytes=-1 --defaults

--max-age=2555d (~7 years) reflects a typical regulatory retention requirement — tune this to the actual compliance rules that apply.

terminal 4 — the compliance team queries the combined trail for one part
nats stream view FACTORY_AUDIT --subject "line.1.event"

Every event published to either line's own stream now also appears — immutably, in original publish order per source — in FACTORY_AUDIT, with no line's software needing to know the audit stream exists.

Step 4 — mirroring: an identical read-only copy for a separate compliance cluster

Sourcing combines and can transform subjects; mirroring instead makes an exact 1:1 copy of a single stream — useful for handing an unmodifiable, identical replica to a separate, access-restricted compliance environment:

terminal 2 — an exact read-only mirror of the audit stream elsewhere
nats stream add FACTORY_AUDIT_MIRROR \
  --mirror FACTORY_AUDIT \
  --storage file --max-age=2555d --max-msgs=-1 --max-bytes=-1 --defaults

Troubleshooting

SymptomLikely causeFix
FACTORY_AUDIT is missing events published before it was createdSourcing only forwards messages from the point the source stream had them retained, not retroactively for messages already deleted by the source's own retentionCreate the audit/compliance stream before or very early alongside line streams, and keep line streams' own retention generous enough to overlap
nats stream add FACTORY_AUDIT --source ... fails validationTypo'd source stream name, or source stream doesn't exist yetnats stream ls to confirm exact stream names before wiring sourcing
Compliance team sees duplicate-looking entriesBoth a line's own stream and a manually-duplicated publish path exist (i.e. sourcing wasn't used, the line republishes manually)Prefer stream sourcing over any manual "publish twice" pattern — it's exactly what avoids this class of drift/duplication bug
Mirror stream falls behind the sourceNormal under network partition — mirrors catch up once connectivity resumesMonitor nats stream info FACTORY_AUDIT_MIRROR for lag; alert if it grows unexpectedly, rather than assuming instant consistency

Recap

ConceptTakeaway
One stream per lineKeeps each line's local tooling independent and simple
--source A --source BCombines multiple streams' messages into one, without any producer changes
--mirrorAn exact, separately-retained read-only copy — good for isolated compliance environments
Long --max-ageModel the actual regulatory retention period, not an arbitrary default

SpyNATS is an independent tool and is not affiliated with, sponsored by, or endorsed by Synadia Communications or the NATS.io project.