R2-D2
Dashboard
Node Red
Restreaming
The Force
UpdatedNever
v1.0.0

Overview

Loading…
R2-D2
Dashboard
Node Red
Restreaming
The Force
UpdatedNever
v1.0.0
DDroidspeak / Docs
Operator handbook
Droidspeak
What is R2-D2?
Runtime architectureAuth — Keycloak migration plan
FleetRestreamingGalaxy MapThe RepublicTemple ArchivesSTANAG 4817The Force
Tech StackNext.js 15 + React 19Tailwind v4ZustandTanStack Table + DataViewhls.jsLow-latency playerreagraphoglreact-grid-layoutMonacoScalar API Referencefumadocs
Cluster InfraTrailBaseReductStoreRestreamer (datarhei/core)TBMQKeycloakLonghornkube-vipIngress (Caddy + nginx-ingress + Traefik)Netbird
Operator QA Runbook
Install — RKE2Install — Dokploy
Contributor guideRelease Notes
Cluster Infra

TrailBase

SQLite-as-API — the dashboard's application data store for everything that isn't a YAML config.

TrailBase is the dashboard's application database. It exposes SQLite tables over an HTTP API (with row-level auth, schema validation, and a typed TS SDK). The dashboard treats it as a managed Postgres-replacement for small structured data — feed metadata, panels, groups, layouts, audit lines.

Anything geographic / political with a long lifecycle that's edited in the GUI lives here. Anything that's a static config or registry lives in a YAML file in the repo.

Where it sits

In-cluster URL(set via TRAILBASE_URL env on the dashboard)
Chart / valuesr2d2-fleet/k8s/trailbase/ in the parent repo
Schema source of truthr2d2-fleet/k8s/trailbase/configmap-schema.yaml
Persistent storageLonghorn PVC
AuthSession cookies issued by TrailBase, set by the login flow

Tables the dashboard owns

Table familyWhat it storesRead by
restreamer_feed_metaalias, platform_id, tags, notes per feedHoloVids, Holoprojector
restreamer_layoutscols × rows + tile rectanglesHoloprojector LayoutEditor
restreamer_panelslayout ref + per-slot bindings + name + public tokenHoloprojector
restreamer_groupsordered list of panel IDs + name + public tokenHoloprojector groups
restreamer_platformsplatform records (e.g. "M300 #14")HoloVids feed detail
restreamer_tagstag stringsHoloVids filters
republic_*nations, ShopKeepers, holdingsRepublic
archives_*per-archive recordsTemple Archives
holocron_*site config overrides (when present)Galaxy Map

Session cookies

When AUTH_ENABLED=true, mutating routes require a TrailBase session cookie. The login flow exchanges a Keycloak token for a TrailBase session and sets the cookie on the dashboard's domain. Read routes stay public-within-cluster; only mutations gate.

Magic URLs (/p/<token>) bypass auth by design — see Restreaming.

Schema migrations

The schema is declarative — it lives in configmap-schema.yaml, and TrailBase applies it on container start. To add a column:

  1. Edit the schema ConfigMap.
  2. Roll the TrailBase Deployment (or restart the pod).
  3. Update the dashboard's TS shape (manually today; auto-generated from the schema is a future improvement).

For larger reshapes (renaming a table, splitting a column), the migration is done out- of-band — drain dashboard writes, snapshot, apply, restore.

Records API + the PK constraint

TrailBase v0.27's Records API exposes a SQLite table over HTTP at /api/records/v1/<name> — but only if the table's primary key is INTEGER or BLOB with an is_uuid_v7 CHECK constraint. A table with a TEXT PRIMARY KEY (e.g. feed_id, instance_id, cache_key) is rejected at config-load time with Does not have a suitable PRIMARY KEY column.

Each table the dashboard wants to read/write through the Records API needs a record_apis { name: "<table>" table_name: "<table>" acl_authenticated: [...] } block in configmap-config.yaml. No block → every call returns 405 Method Not Allowed.

Today only four tables qualify: audit_log, fleet_snapshot, ship_revisions, aircraft_revisions — they all use INTEGER PRIMARY KEY AUTOINCREMENT. The remaining ~16 tables use TEXT PKs and stay 405 until a schema migration adds a UUID-blob id column.

The withFileFallback pattern

src/lib/trailbaseRecords.ts wraps every Records API call in withFileFallback:

HTTP status from TrailBaseWhat the helper does
200/201/204Return the result
404 / 405 / 501Log once, call the fileOp callback — same response across every pod, so it's safe
5xx, network errorsRe-throw — falling back here would risk multi-pod split-brain

The 4xx fallback is what lets the dashboard ship a route that prefers TrailBase but degrades to a file backend when the Records API doesn't (yet) serve the table. Most read routes are wrapped this way — that's why the dashboard's HTTP responses stay 200 even when TrailBase isn't exposing the underlying table.

Direct callers without fallback drop writes silently. statePoller and sense/disturbances .upsertRecord(...) / .createRecord(...) straight into TrailBase — when those tables 405, every write is lost (statePoller feed_status: 11/11 upserts failed in the dev log). Either wrap them, or fix the schema PK and add the record_apis block.

The drizzle migration tracking re-sync recipe

Distinct from TrailBase but related — when the dashboard's drizzle-managed Postgres tables (used elsewhere) crashloop with "X already exists", the fix is to bulk-insert SHA-256 + when rows into drizzle.__drizzle_migrations. See the project memory note for the full procedure.

What TrailBase is not used for

  • Snapshot binary blobs. Those live in ReductStore.
  • High-write OLTP beyond dashboard editing volume. TrailBase is SQLite under the hood; sized for operator edit traffic, not bulk telemetry.
  • YAML configs. holocron-config.yaml, archives-config.yaml, republic-config.yaml, nav-config.yaml, instances/registry.yaml, etc. stay in the repo.

See also

  • ReductStore — sibling store for blobs
  • Restreaming — primary table consumer
  • Republic — primary table consumer

Cluster Infra

Per-service reference for the in-cluster systems R2-D2 itself runs on.

ReductStore

Dedicated time-series blob store for snapshot history and audit binaries.

On this page

Where it sitsTables the dashboard ownsSession cookiesSchema migrationsRecords API + the PK constraintThe withFileFallback patternThe drizzle migration tracking re-sync recipeWhat TrailBase is not used forSee also