Racing

One box runs NMEA 2000 ingestion over native CAN, NATS-routed sensors, TimescaleDB persistence, and onboard dashboards. Five jails, the racing-boat platform.

Topology

     NMEA 2000 backbone   (J1939 over CAN @ 250 kbit/s)
              |
              v
  +--------------------+    +--------------------+    +--------------------+
  |   gateway  jail    |    |    core    jail    |    |   compute   jail   |
  | ------------------ |    | ------------------ |    | ------------------ |
  | CAN_RAW socket     | -> |   NATS bus         | -> |  translate_nmea    |
  | imcansock -> omnats|    |   JetStream KV     |    |  PGN decode        |
  | raw frames         | <- |   subject routing  | <- |  derived signals   |
  +--------------------+    +---------+----------+    +--------------------+
                                      |
                                      v
                            +--------------------+
                            |      vis  jail     |
                            | ------------------ |
                            |  NiceGUI on NATS   |
                            |  live instruments  |
                            +--------------------+

  Separately, host telegraf writes system metrics to the persistence
  jail (PostgreSQL + TimescaleDB), which is what the shipped Grafana
  dashboards read.  Telemetry archival is an optional InfluxDB
  forwarder in compute.

What runs where

gateway
Holds the CAN interface and does no decoding. imcansock opens a CAN_RAW socket and omnats forwards the frames verbatim onto telemetry.nmea2k.raw. Keeping the gateway dumb is deliberate: the decode rules change far more often than the transport does. Note that this path uses CAN_RAW, so can_nmea2000.ko and its in-kernel Fast Packet reassembly are not involved here; that module serves SOCK_DGRAM J1939 sockets.
core
NATS server. Subject routing, retention, MQTT bridge. Everything else talks to this jail; it talks to nothing else.
compute
Where the NMEA 2000 decoding actually happens. The translate_nmea processor pulls PGN and source address out of the 29-bit CAN id and explodes the payload into per-field subjects, against a PGN catalog held in a JetStream KV bucket and editable from the vis UI. Alongside it, a plugin model of Python processors for threshold alerts, peak detection and derived signals, all subscribing on NATS and publishing derived subjects back. An optional InfluxDB forwarder handles offline archival.
persistence
PostgreSQL with the TimescaleDB extension. In the shipped platform this backs host and appliance metrics from telegraf, which is also what the bundled Grafana dashboards read. The live telemetry path does not run through it: the bus is the live path, and archival is compute's optional forwarder. The cluster deliberately sits on the root filesystem with no dedicated ZFS dataset, on the basis that a rebuilt boat computer losing history is acceptable and a simpler layout is worth more at sea.
vis
Two surfaces with two different sources, which is worth knowing before you go looking for a race dashboard. NiceGUI drives the live crew instruments straight off NATS, so it shows what the bus is carrying now. Grafana is pointed at the telegraf metrics database and ships appliance dashboards (ZFS pool, appliance health, Redis), not race panels. Post-race telemetry analysis is not a shipped dashboard today.
WAKE (host-side, not shipped by default)
The racing platform does not install or enable WAKE. Where an operator adds it, host-level WAKE reads /usr/local/bastille/jails/*/root/var/log, the same glob the hosting platform uses, and runs the same detection stack as a SOC edge.

Built for the link being down

Not boat-specific

The shape - CAN gateway, NATS bus, compute processors, time-series persistence, dashboard jail - is what industrial edges with CAN buses look like. Move the gateway decoder, keep everything else, and you have a factory-floor edge or an automotive test bench.