Skip to content

Data model

This page summarizes the core persistence model used by the platform.

When to use this page

Use this page when mapping API behaviour to persisted entities and relationships.

Storage backbone

  • Primary database: PostgreSQL with TimescaleDB.
  • Time-series hypertables: telemetry, anomalies, and monitoring_evidence.
  • TELEMETRY_RETENTION_DAYS controls the retention window applied to telemetry, monitoring evidence and anomalies.
  • TELEMETRY_STORAGE_CAP_GB optionally caps telemetry and monitoring evidence storage; the oldest completed daily chunks are dropped first.

Core entities

Entity Primary key Purpose
users id Identity, credentials, role, and verification state
chargers charger_id Charger inventory and MQTT/connection metadata
telemetry (charger_id, timestamp, type) Observed time-series values by charger and sensor type
services id Monitoring workload, topics, saved launch configuration, lifecycle, and operational stage
deployment_maintenance id (singleton) Durable release phase and monitor restart plan
off_key_schema_migrations version Reviewed SQL migration checksums and application times
mqtt_topics id Service-to-topic mappings
favorites favorite_id User-to-charger favourite mappings
anomalies (charger_id, timestamp, telemetry_type, service_id) Detected anomaly events and contributing sensor set
monitoring_evidence (service_id, timestamp, sequence_number) Per-inference strategy evidence used by charts and alarms
anomaly_identity anomaly_id Stable identifier mapped to an anomaly composite key
remembered_sessions token_hash Rotating remember-me refresh tokens grouped by session family

Catalogue and collection

Entity Primary key Purpose
system_configuration id (singleton) Current saved catalogue document and its revision
system_revisions revision Every saved catalogue document, for conflict checks and history
system_collection_configuration id (singleton) Collection policy per reading plus ingress and collector status
collection_state (charger_id, sensor_key) Latest boolean, text and identifier values per reading
collection_bindings charger_id Stable mapping from a catalogue entity to its runtime charger ID
collection_configuration id (singleton) Pre-catalogue collection settings, read once by the catalogue upgrade
collection_revisions revision Pre-catalogue collection history, kept as an archive

Pipelines and RADAR runtime

Entity Primary key Purpose
monitoring_pipelines id Pipeline designs and templates with their editable draft
monitoring_pipeline_revisions id Immutable compiled revisions of a pipeline
monitoring_pipeline_runs id A revision bound to inputs and running as one monitoring service
monitoring_pipeline_activations id Idempotent activation requests and their phase through preflight, start and heartbeat
radar_runtime_checkpoints service_id Signed detector checkpoint for each running RADAR service

Relationship highlights

  • favorites.user_id → users.id
  • favorites.charger_id → chargers.charger_id
  • mqtt_topics.service_id → services.id
  • anomaly_identity.(charger_id, timestamp, telemetry_type, service_id) → anomalies composite key
  • monitoring_evidence.service_id identifies the monitoring service that produced the inference record.

Time-series characteristics

Telemetry

  • Indexed for charger/time and charger/type queries.
  • created provides the ingestion-order cursor; timestamp is the event time.
  • Used by details views, charts, and monitoring input streams.

Anomalies

  • Stores detector type, value, value semantics, and the exact contributing sensor_set when available.
  • anomaly_identity gives the UI a stable deletion identifier without changing the time-series primary key.
  • Monitor alarms carry their originating service_id, so independent monitors can report multivariate alarms for the same entity and timestamp. Retrying one monitor's result remains idempotent. The API returns this ID for exact chart attribution; historical and manually created events return null.
  • Migration 007_monitor_anomaly_identity.sql preserves existing public anomaly IDs in an empty legacy scope. Historical alarms lost to earlier collisions cannot be reconstructed automatically. Apply through the release migration workflow with application writers stopped. Monitor IDs are retained after monitor deletion, alongside their evidence and alarms.

Monitoring evidence

  • Records strategy, model_type, sensor/input timestamps, threshold, and alarm state.
  • Static-baseline rows carry a conformal p_value; adaptive-stream rows carry an anomaly_score.
  • E-process and restarted-martingale fields preserve tracker evidence where the selected strategy produces it.
  • tracker_results holds additional tracker-specific results.

Monitoring workload intent

  • services.desired_state records whether the controller wants a worker running, stopped, or deleted. lifecycle_version fences delayed workload starts.
  • The row and sensor claim commit before Docker creation. container_id can be empty during a recoverable pending creation; launch_config, config_fingerprint and collection_fingerprint identify its validated inputs.
  • Reconciliation adopts matching workloads by deterministic name and verifies service, configuration and generation labels. Ambiguous Docker outcomes retain the row and claim; stop or delete persists intent before removing the worker.
  • Migration 009_radar_lifecycle_intent.sql preserves attached workers and marks already inactive services as stopped. Older attached workers without saved configuration remain observable and removable; they are not auto-created.

Data access paths

  • Frontend → API Gateway → TACTIC data services → database.
  • MQTT Proxy writes telemetry through the shared backend service layer.
  • RADAR writes monitoring evidence and anomalies.
  • DB Sync initializes the current schema and exposes schema readiness. Startup does not migrate historical development schemas. An obsolete table shape fails readiness without rewriting existing data; use a fresh development database. Current tables, indexes, identity synchronization, and TimescaleDB policies are owned by off_key_core.db.models and off_key_core.db.schema.
  • Production runs the packaged migration CLI before starting the target writers. Migrations and checksum records commit in one PostgreSQL transaction under a migration lock. See Production deployment.