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

Contributor guide

Repo layout, dev server, conventions, and the rules that keep the seven planes coherent.

Repo layout (the parts you'll actually touch)

nodered/
├── r2d2-fleet/               <- the dashboard (Next.js 15, React 19, Tailwind 4)
│   ├── src/
│   │   ├── app/              <- App Router routes
│   │   │   ├── (root)/       <- the dashboard pages
│   │   │   ├── api/          <- 40+ route handlers (the OpenAPI source of truth)
│   │   │   └── droidspeak/   <- this handbook lives here
│   │   ├── components/       <- reusable UI primitives + per-plane components
│   │   ├── lib/              <- domain logic, no React
│   │   └── store/            <- Zustand stores
│   ├── content/docs/         <- MDX content for Droidspeak (you are here)
│   ├── public/openapi.yaml   <- hand-authored OpenAPI spec (until zod adoption)
│   └── k8s/                  <- deployment manifests + Helm values
├── instances/                <- the Node-RED registry (registry.yaml lives here)
└── ...

Running the dev server

Always use the persisted dev server. Never start next dev directly — two dev processes on the same port corrupt .next/ and you'll waste 20 minutes diagnosing chunk-not-found errors.

cd r2d2-fleet
npm run dev:persist          # starts (or attaches to) the dev server on :3210
npm run dev:persist:clean    # nukes .next first, then starts

Open http://localhost:3210. The sidebar should appear with the Star-Wars-themed nav. If you see a 500 Internal Server Error from a fresh checkout, run dev:persist:clean once.

Conventions that aren't optional

  • One source of truth per plane. Each plane has a single YAML config file (registry.yaml, videos-config.yaml, holocron-config.yaml, archives-config.yaml, republic-config.yaml). The Settings UI edits those files; nothing else does.
  • Read-before-write on every mutation. Especially for Restreamer — every PUT fetches the current process config first, diffs in-memory, and writes the merged result. We never blind-overwrite a process we haven't just observed.
  • Reuse the table primitives. src/components/table/DataView + TanStack is the only way new tables get built. Never hand-roll a <table> — the toolbar, sort, filter, and pagination are non-trivial to get right.
  • No internal-guideline subtitles in page chrome. Page subtitles either say something the operator needs to know to do their job, or they're empty. They're not a place to paste design rationale.
  • No internal tracker IDs in user-facing copy. This handbook is user-facing. If a decision is interesting enough to mention, it gets a paragraph here, not a code.
  • Errors are structured. API routes return { ok: false, error: "<machine-readable-key>" } with an appropriate status. The dashboard's toast system reads that.

Adding a new API route

  1. Drop a route.ts under src/app/api/<plane>/<resource>/. Stay inside an existing tag if one fits — the plane that owns the resource owns the route.
  2. Use plain NextResponse.json(...). No streaming, no SSE unless you have a strong reason — the existing 40+ routes are all JSON-in / JSON-out.
  3. Validate the request body. Today that's hand-coded — there is an open plan to adopt zod across every route, which will also auto-generate the OpenAPI spec. Until then, the spec at public/openapi.yaml is the source of truth and must be updated in the same PR as the route.
  4. Audit-log every POST / PUT / DELETE that mutates external state. Restreamer routes write to data/restreamer-audit.jsonl; new mutating routes should pick the audit log for their plane (or create one).

Adding a Droidspeak page

  1. Create content/docs/<plane>/<page>.mdx with frontmatter title + description.
  2. Add the slug to the parent meta.json so it appears in the sidebar in the right order.
  3. Cross-link from sibling pages when relevant. Use relative links — Fumadocs rewrites them.
  4. Don't reference internal trackers, beads codes, or commit hashes in the body. Those belong in .settings/features/* and the commit message.

Star Wars vocabulary cheat-sheet

Code nameWhat it is
R2-D2the dashboard itself
HoloVidsthe camera feed grid (one per feed)
HoloChrona UXV/map widget embedded from the Force
HoloNetthe MQTT Explorer iframe
Holoprojectorthe camera wall projection (Views = layout + bindings)
Holocrona config surface (fleet/map settings)
Magic URLa public, auth-bypassed read-only URL for a panel/group
The Forcethe collection of embedded apps
The Republicthe nations/ShopKeepers/holdings data model
Temple Archivesthe telemetry archive
ShopKeepersa CRM-ish vendor list
Rift Gatesnetwork-overlay debug page
Probeobservability iframe

Install — Dokploy

Single-node alternative for evaluation. Authored from the K8s mapping; not yet validated on a real Dokploy host.

Release Notes

What's new in the R2-D2 dashboard.

On this page

Repo layout (the parts you'll actually touch)Running the dev serverConventions that aren't optionalAdding a new API routeAdding a Droidspeak pageStar Wars vocabulary cheat-sheet