Files
NODEDC_MISSION_CORE/deploy/telemetry-plane

NDC Mission Core local telemetry plane

Portable local-only telemetry infrastructure for compute contours.

One Docker Compose project, ndc-mission-core-telemetry, owns three long-running containers and one bounded bootstrap job:

  • ndc-mission-core-mqtt-broker — Eclipse Mosquitto 2.1.2-alpine;
  • ndc-mission-core-telemetry-timescaledb — TimescaleDB HA OSS pg16.14-ts2.28.2-all-oss;
  • ndc-mission-core-telemetry-bootstrap — one-shot database role/bootstrap job;
  • ndc-mission-core-telemetry-normalizer — Mission Core telemetry normalizer.

The Telegraf configuration templates cover Windows and Linux, but the agent runs as a host service rather than inside this Compose project. This preserves access to native Windows performance counters, Docker Desktop and NVIDIA telemetry.

The normalizer exposes a read-only normalized telemetry adapter on 127.0.0.1:18030. Mission Core reads this adapter; the browser never receives MQTT or Timescale credentials, and Timescale is not published on a host port. The normalizer uses a separate missioncore_normalizer database role with only SELECT, INSERT, UPDATE, and bounded retention DELETE on the telemetry hypertable. Raw telemetry older than 30 days is removed at most once per day by the normalizer. This stays compatible with the Apache-licensed Timescale image without depending on the Timescale License retention scheduler.

The stack is one deployment contour, not one multi-process container. Keeping broker, normalizer and database in separate containers preserves independent health checks, least-privilege boundaries and rollback while Compose provides one operator lifecycle:

docker compose ps
docker compose up -d
docker compose down

All NODE.DC-owned Docker objects use the lowercase ndc- namespace. Docker names are case-sensitive identifiers, so the product prefix is normalized to lowercase while the product name remains NODE.DC in operator-facing copy.

This directory does not contain credentials. Copy .env.example to .env, generate unique passwords and build the ACL/password files before starting the stack.

uv run python prepare.py --initialize --mqtt-bind-address <MISSION_CORE_HOST_LAN_IP>
docker compose up -d --build

Expected Docker object names:

project:   ndc-mission-core-telemetry
network:   ndc-mission-core-telemetry
containers:
  ndc-mission-core-mqtt-broker
  ndc-mission-core-telemetry-normalizer
  ndc-mission-core-telemetry-timescaledb
volumes:
  ndc-mission-core-mqtt-data
  ndc-mission-core-telemetry-timescale-data

prepare.py passes passwords to mosquitto_passwd through stdin. Secrets are not placed in process arguments or committed files. --initialize generates .env with mode 0600 and refuses to replace existing credentials. The generated .env and runtime/ directory are ignored by Git.

The product architecture and topic contract are defined in docs/adr/0031-local-compute-contour-telemetry-plane.md.

Security boundary

The current MQTT listener is authenticated and contour-scoped, but intentionally uses plaintext MQTT inside one owner-controlled laboratory LAN. Do not expose port 1883 through a router, cellular WAN, public Wi-Fi, or an Internet-facing host. A remote or shared-network deployment requires a reviewed TLS listener, a private CA distributed to every agent, and credential rotation. Wi-Fi link encryption is not a replacement for MQTT TLS.

Each agent credential is bound to one exact contours/<contour-id>/agents/<agent-id>/+ prefix. Adding another contour requires issuing another password entry and explicit ACL row; the wildcard contour writer is not permitted.

Worker agent

Worker 006 uses the official Windows Telegraf distribution as the host service NDC Mission Core Telemetry Agent. Install or update it with:

.\telegraf\Install-NdcMissionCoreTelegraf.ps1
.\telegraf\Update-NdcMissionCoreTelegraf.ps1

The update path validates the candidate configuration, backs up the active configuration and rolls back if the service does not return to Running. MQTT credentials are scoped to the service environment and must not be passed on a command line or stored in the repository.

The same host service reads the existing perception worker's loopback /health contract with Get-NdcMissionCorePipelineTelemetry.ps1 and publishes nine stage-keyed snapshots to the contour's pipeline topic. The perception container does not receive broker credentials and no second agent container is introduced. These snapshots expose current durable-worker state and cumulative stage timing; native per-run lifecycle events remain a separate compute contract.

When the mounted perception runner itself changes, use Update-NdcMissionCorePerceptionRunner.ps1 with exact predecessor and candidate digests. It backs up the mounted runner, restarts the same container, accepts only a ready health document with stage metrics, and restores the predecessor on failure.

The stack and agent are intentionally not started by repository tests. Provisioning a machine is a separate, explicit operation.