Self-contained manifests + ops image Dockerfile to run the ops console as the
public entry point (ops.r2d2.office.ilab.zone / VIP 192.168.3.153) in the
r2d2-status-kuma namespace on the RAID cluster, fronting an Uptime Kuma backend.
- ops-server (git.office.ilab.zone/raid/r2d2-ops) serves the FMV/telemetry UI and
logs into Kuma over socket.io on localhost; Service/LB/Ingress target it (4013).
- Kuma (git.office.ilab.zone/raid/r2d2-status-kuma:2.4.0) is a private backend
on a MariaDB sidecar.
- Ordered native sidecars mariadb -> netbird -> kuma -> wait-kuma-login gate, so
the DB, the mesh, and a working Kuma socket-login are all ready before
ops-server starts. Fixes the boot-time login hang (timeout-less emitWithAck in
serve.mjs) and the mesh EHOSTUNREACH false-downs.
- Secrets (db creds, netbird setup key, admin/ops creds) live only in-cluster,
referenced by secretKeyRef; nothing sensitive in the manifest.
Plan + shared mqtt.config.json for subscribing to the office broker's
retained node/description topics: live registry of announced UXVs with
standard-identity/liveness, manual rescan, and a bidirectional alias link
between a drone node and its video feed. Planning only; no consumer code.
Native media detail modal with in-browser HLS playback, in-detail record
/ snapshot controls wired to the capture summary, a source-URL inspector,
group/subgroup-aware facets, and tag-driven Page tabs.
- Split the region breadcrumb into group (theatre) + subgroup (country)
facets on rows, /api/monitor, and recorder session sidecars.
- Make listClips() a stale-while-revalidate async fs scan so the heavy
tuf8tb disk walk never blocks the event loop on a request path.
- Add a per-asset capture summary (clips/snapshot sessions/live state)
to /api/monitor to drive the in-detail record controls.
datarhei Core mints a fresh ?session= per master GET and generates .ts
segments lazily per session; the session re-cools within ~1s, so hls.js
stalls on a cold fragment. Register a background keep-alive per session
(warm poll < memfs idle expiry), warm it before returning the master
playlist, and idle it out when the browser stops polling.
Reword the seed toolchain to 'legacy endpoint config' now that the product is
standalone Uptime Kuma + Ops console: convert.mjs comments, seed/README,
seed/package.json description, .old-config.yml header, and features/02. The
converter only reads `endpoints`, so output (monitors.json) is unchanged.
(Also carries an incidental .beads/issues.jsonl export.)
Replace the stock Uptime Kuma README with one describing the actual
app: the R2D2/Task Force X ops console, its architecture, local dev,
configuration/security model, and the standalone k8s deployment at
ops.r2d2.office.ilab.zone. Drops upstream badges/sponsors/live-demo.
R-010b: the F07 doc referenced REC_LIVENESS_MS and DISK_POLL_MS, neither
of which exists in recorder.mjs, and cited the record-set path as
ops/recordset.json. Corrected to reality: the liveness sample cadence is
hardcoded (5s for the hls backend, REC_FPS for snapshot), the disk floor
is DISK_STOP_GB (default 20), and the record-set persists to REC_SETFILE
(default <REC_ROOT>/.recordset.json). Every recorder var named in the doc
now resolves to real code.
Closes r2d2-236.
R-008 (F-05): seed.mjs defaults to NATO/NATO1949 for local dev, but
nothing stopped those demo creds being baked into a production Kuma if
KUMA_URL pointed off-box. Now: if the creds are still the built-ins AND
the target host isn't localhost/127.0.0.1/::1, seed refuses unless
ALLOW_DEFAULT_CREDS=1. Local dev (the common case) is unaffected.
Verified: remote+default -> refuse (exit 1); remote+ALLOW_DEFAULT_CREDS
or real creds -> proceed; localhost+default -> proceed.
Closes r2d2-fz9.
R-005 (F-04): seed.mjs and statuspage.mjs had diverging copies of the
status-page build logic — statuspage.mjs only rebuilt the link-less
master page, so re-running it after a seed dropped the other 4 pages and
every projector link.
Extract the builder into seed/statuspages.mjs as the single source of
truth. seed.mjs imports it; statuspage.mjs now joins the live Kuma
monitorList to monitors.json BY NAME (to recover tags + projector links,
which Kuma's monitorList doesn't carry) and rebuilds all 5 pages via the
same builder.
Verified offline (no live Kuma) with a dry-run harness: all 5 pages
produced, master=118, videos=73, 146 linked rows keep their projector
url, and the seed vs refresh callers yield byte-identical page shape.
Closes r2d2-1ga.
Cleanse all Gatus references now that the Kuma ops console is a
standalone deployment at ops.r2d2.office.ilab.zone: removed the helm/
status-page pointers (architecture snapshot + appendix), genericized
data-source lineage to 'legacy endpoint config', and dropped the
Gatus-vs-Kuma comparison from F-03, R-007, D-006 and call-out #2. The
Gatus status page (status.r2d2) is unrelated to this review and no
longer mentioned.
Per operator decision (D-006, 2026-07-08): the Kuma ops console deploys
to a new, separate host ops.r2d2.office.ilab.zone; the Gatus status page
at status.r2d2.office.ilab.zone is left untouched. Removed the
cutover/migration/'Gatus is blind' conflict framing (F-03, R-007,
call-out #2, D-007); remaining Gatus mentions are neutral data-source
lineage. convert.mjs still sources the legacy Gatus YAML + holovids CSV.
R-010 (F-09): recorder.mjs defaults REC_ROOT to the dev tuf8tb mount,
which fails silently on other hosts. Added a Configuration (environment)
table to features/07 covering REC_ROOT (with a machine-specific
override warning), REC_MAX, SNAP_MAX, DISK_MIN_GB, DISK_STOP_GB and the
capture-rate/dedup/persistence knobs, using the real code defaults.
Closes r2d2-bno.
R-006 (F-07): docs still said 112 monitors / 16 groups. monitors.json
now has 118 monitors and 21 group definitions (empty groups skipped at
seed). Updated seed/README.md, features/README.md, and
features/01-search-and-filter.md; kept the counter example
arithmetically consistent (103 up + 12 down + 3 degraded = 118).
Closes r2d2-evz.
R-002 (F-02): serve.mjs bound 0.0.0.0 and exposed unauthenticated
mutating endpoints (delete-monitors, set-interval, record/*) that act
on Kuma as admin. Now:
* OPS_BIND defaults to 127.0.0.1 (opt in to 0.0.0.0 for the LAN wall)
* OPS_SECRET, when set, requires an x-ops-secret header on every
mutating POST (403 otherwise); read endpoints stay open
R-003 (F-02): rewrote the file header — dropped the stale 'read-only
companion' claim and documented the mutating surface + auth model.
Verified: no/wrong secret -> 403, correct secret -> passes guard,
GET /api/feed.json -> 200, socket bound to 127.0.0.1 only.
Staged only the security hunks; unrelated in-flight HLS/media WIP in
this file is intentionally left in the working tree.
Closes r2d2-ahw, r2d2-6ml.
R-001 (F-01): the double-seed guard attached its monitorList listener
AFTER the login ack, but Kuma pushes the list during afterLogin — the
listener raced the push and usually saw 0, silently re-seeding populated
instances. Subscribe before login (mirrors statuspage.mjs) so the guard
reads the real count.
R-004 (F-06): default KUMA_URL 3001 -> 3011 to match statuspage.mjs,
serve.mjs, docker-compose.yml and the README.
Closes r2d2-4lx, r2d2-qh0.
Ops console upgrade — F06/F07/F08 land together because they wire through
the same serve.mjs routes and index.html render path:
- F06 native detail: expandable Kuma-style row detail (facts + heartbeat
history + ping sparkline), /api/monitor, HLS player via vendored
hls.min.js served at /vendor/hls.min.js (offline, no CDN)
- F07 recording: recorder.mjs per-feed state machine (idle->live->dead->cut),
/api/record/{start,stop,status}, storage gauge + recordings panel,
writes to tuf8tb
- F08 KLV: klv-probe.sh discovery helper for MISB ST 0601 in the source TS
- PLAN.md: umbrella C2-FMV console arc
ops-server.log (runtime) gitignored.
- holovids.csv refreshed from 2026-07-07 export; convert.mjs re-derives
channel/port/source/protocol enrichment and per-monitor descriptions
- monitors.json regenerated from the new roster
- seed.mjs: carry per-monitor link -> status-page rows use custom_url so
video rows open in the projector instead of the raw m3u8; group
descriptions now seeded
- add lucide dep for status-page icons
Kuma ships branding baked into hashed frontend assets, so patch at boot
instead of forking the image:
- seed/branding/apply-branding.sh: swaps "Uptime Kuma" -> "R2D2 Status"
in dist/assets and replaces icon.svg, then execs the original command
- docker-compose: entrypoint runs the branding step before server start,
mounts branding/ read-only
- public/favicon.png: TFX favicon
Ops feed (.settings/ops/index.html):
- Tree view: nest rows by region breadcrumb (Sea → Country) with per-group
down/pending/up/total rollups, outage groups floated up, collapsible
(per-group + collapse/expand-all), state persisted via ?group=1 + localStorage.
- Up filter chip alongside DOWN/Pending.
- Export the currently-shown rows as CSV (UTF-8 BOM) / JSON / XLS (no deps).
- 30s re-render tick so relative times stay live if SSE goes quiet.
Seed (.settings/seed/monitors.json):
- All monitor interval/retryInterval -> 60s (also pushed live to 112 monitors).
- Move Greece group under Central Med (was Eastern Med) — also re-parented live.
Also carries prior in-tree WIP in these two files (seed enrichment regen:
NATO/Arctic group, projector link/description/snapshot fields; Feature 07 UI).
Currently translated at 100.0% (1549 of 1549 strings)
Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (1547 of 1547 strings)
Co-authored-by: Aluisio <aluisiodeavila@hotmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pt_BR/
Translation: Uptime Kuma/Uptime Kuma
Currently translated at 75.3% (1166 of 1547 strings)
Translated using Weblate (Persian)
Currently translated at 74.9% (1160 of 1547 strings)
Co-authored-by: Goudarz Jafari <goudarz.jafari@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/fa/
Translation: Uptime Kuma/Uptime Kuma