559caead36
Public community release. The complete working system — DSP + decoders (scripts/), web dashboard + API (dashboard/), container stack (docker/), config templates (config/) — plus APOCALYPSE-EDITION.md, the full build guide for a human or a coding agent. GPLv3 (PyEOT dependency). Scrubbed of all secrets, internal IPs, hostnames, and location detail. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
301 lines
23 KiB
HTML
301 lines
23 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>iSTRAIN — railroad VHF watch</title>
|
||
<link rel="stylesheet" href="/style.css">
|
||
</head>
|
||
<body>
|
||
<header>
|
||
<h1>🚆 iSTRAIN <span class="sub">Lassiter Creek · <span id="ver" title="commit this dashboard is serving (* = uncommitted local changes)">—</span> · railroad VHF 160–162 MHz</span></h1>
|
||
<div class="hdr-right">
|
||
<div class="controls">
|
||
<button id="btn-refresh" class="btn" title="re-pull data">↻ refresh</button>
|
||
<button id="btn-reset" class="btn reset" title="full reload">⟲ hard reset</button>
|
||
</div>
|
||
<div class="hits">
|
||
<div class="stat"><div class="num" id="hits-unique">—</div><div class="lab">unique</div></div>
|
||
<div class="stat"><div class="num" id="hits-total">—</div><div class="lab">total hits</div></div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- live: the headline inference — isTRAIN? — mounted from panels/banner/ (above all cards) -->
|
||
<div id="istrain-banner"></div>
|
||
|
||
<main>
|
||
<!-- FIRST PANEL: the "AIS-equivalent" for trains — dispatch / yard data.
|
||
In-band source now (defect-detector & dispatcher voice on the road channel, transcribed);
|
||
ATCS/CTC (900 MHz) was evaluated and dropped (FCC-retired → encrypted PTC; our 900 was a spur). -->
|
||
<section id="dispatch" class="panel dispatch">
|
||
<h2>Dispatch / Yard <span class="tag">rail-voice captures — the "AIS" for trains</span></h2>
|
||
<div class="placeholder">
|
||
<b>Airband is watching 5 channels on 1002.</b> Captured transmissions (squelch-gated clips →
|
||
whisper transcripts) will list here newest-first: channel, time, and the milepost / axle-count /
|
||
speed read-outs from detector call-outs. ★ <b>160.980 NS Dayton District</b> is the mission channel.<br>
|
||
<span class="dim">Live clips appear in the <b>Captures</b> panel (one per transmission, playable);
|
||
transcription (whisper) still pending.</span><br>
|
||
<b>scan status:</b> <span id="airband-status">service <code>istrain-airband</code> — live status & controls in the <b>Supervisor</b> panel below</span>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- captures panel RESTORED 2026-07-10 — now lives below Community Feeds (novelty tier), see there. -->
|
||
|
||
<!-- live: Trains — per-train head+tail cards from decoded EOT (lifecycle + crossing inference) -->
|
||
<section id="trains" class="panel detect"></section>
|
||
|
||
<!-- activity panel SHELVED 2026-07-02 — voice ACTIVITY stays a signal in the correlate graph
|
||
(which reads heard.jsonl); the dedicated activity panel stays down. -->
|
||
|
||
<!-- live: EOT presence on 457.9375 (dongle 1001) — a burst = a train's rear unit is near -->
|
||
<section id="eot" class="panel detect"></section>
|
||
|
||
<!-- live: multi-band train-activity correlation — co-occurring activity across voice/EOT/(head-end) -->
|
||
<section id="correlate" class="panel detect"></section>
|
||
|
||
<!-- scaffold: BOT / head-end presence on 452.9375 — lights up when the 452 hop feeds /api/bot -->
|
||
<section id="bot" class="panel detect"></section>
|
||
|
||
<!-- scaffold: mid-train DPU (~900 MHz) presence — lights up when researched + hopped, /api/midtrain -->
|
||
<section id="midtrain" class="panel detect"></section>
|
||
|
||
<!-- live: signal meter — bouncing dBFS bar for the band the hop is currently on (/api/level) -->
|
||
<section id="live" class="panel"></section>
|
||
|
||
<!-- DEFECT DETECTORS — static reference from defectdetector.net (one-time import by id, not a poller) -->
|
||
<section id="detectors" class="panel detect">
|
||
<h2>Defect Detectors <span class="tag">reference — your subdivision (example)</span></h2>
|
||
<div class="placeholder">
|
||
Wayside detectors near your crossing (example: MP 148.99), all on your mission channel (example <b>160.980</b>). The
|
||
nearest <b>talks on every train</b> — its call-out is the unambiguous "train through" event.<br>
|
||
• <b>CJ 146.4 ★</b> — <b>nearest</b>, ~2.6 mi east (New Rome OH). HBD-DED · <b>talks every train</b>
|
||
(Talk-On-Defect-Only: No) → routine call-out on <b>westbounds</b> · <a href="http://map.defectdetector.net/?id=8519">record</a><br>
|
||
• <b>CJ 144.7</b> — ~4.3 mi east, high-car (over-height) — defect-only<br>
|
||
• <b>CJ 157.8</b> — ~8.8 mi west, warns eastbounds — type TBD<br>
|
||
<span class="dim">Source: defectdetector.net — static records (location/type/freq/audio), pullable per detector by id. One-time import, not a live poller.</span>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- COMMUNITY FEEDS — live audio (when up) -->
|
||
<section id="feeds" class="panel detect">
|
||
<h2>Community Feeds <span class="tag">live audio — when up</span></h2>
|
||
<div class="placeholder">
|
||
• <a href="https://www.broadcastify.com/listen/feed/26042">Broadcastify — South Central Ohio Railroads</a>
|
||
— the one Franklin County rail feed; monitors our <b>160.980</b> (independently validates the channel). <span class="dim">Often offline, ~0 listeners.</span><br>
|
||
<span class="dim">Their rooftop hears more than our desk whip — "they catch it, we don't" = range, not broken. Someday: we become the local contributor.</span>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- captures: RESTORED 2026-07-10 (was shelved 07-02 "return with the new antenna" — it arrived,
|
||
same night the whisper pipeline went live). The public node serves VOICED clips only (pushed
|
||
from the VM: push-to-nas _push_clips → /api/push-clip, self-pruning ~120 files); 🗣 rows carry
|
||
the whisper transcript. Novelty tier below Community Feeds. -->
|
||
<section id="captures" class="panel detect"></section>
|
||
|
||
<!-- SIGNAL FLOW — hand-maintained ASCII map: frequency -> dongle -> approach, by band.
|
||
A learning tool. Keep it current as the build iterates (LIVE/TEST/NEXT/LATER tags). -->
|
||
<section id="flow" class="panel flow">
|
||
<h2>Signal Flow <span class="tag">how it fits together — the air ▸ the dongles ▸ meaning</span></h2>
|
||
<pre class="flow-chart">
|
||
iSTRAIN — SIGNAL FLOW the air ▸ the dongles ▸ meaning
|
||
mission: is a train blocking YOUR crossing? (set your channels in config/istrain.conf)
|
||
legend: ★ TCXO (precise) [nnnn] = dongle serial ─▶ data path
|
||
updated 2026-06-28: voice live (1002); 1001 WEIGHTED hop 452/457 (clock-step-proof); EOT decoded LIVE (ID+brake+motion); HOT head-end ADDRESS cracked → head+tail join; MID standing carrier ID'd = 457.925 EOT booster, carded. ATCS dropped.
|
||
updated 2026-07-05: EOT decode is TWO-PASS — off-frequency emitters (±800 Hz, ~100 ms bursts) now decode
|
||
via adaptive tones + fuzzy sync (O-11) → dwelling trains identified w/ brake psi: held ~60 = stopped
|
||
solid · bled to 0 + head polling = CUT & standing (crossing possibly open). Booster floor subtracted
|
||
from contacts & correlate (O-9).
|
||
updated 2026-07-18: THE WHOLE RIG MOVED TO THE MILL — both dongles + capture + decode + transcription
|
||
+ this site now run as one Docker (Dockge) stack on a TrueNAS box; the old VM runs zero radio
|
||
services; data is born where it's served. Voice transcription is LIVE (not planned — see below).
|
||
────────────────────────────────────────────────────────────────────
|
||
|
||
PRIMARY ░░ RAIL VHF VOICE — 160–162 MHz · AAR · NBFM, unencrypted ░░
|
||
└ defect detectors ("MP 123, no defects, 45 axles, 60 mph") + dispatcher
|
||
─▶ RTLSDR-Airband (multi-ch) ─▶ ffmpeg comms-filter ─▶ faster-whisper (small.en + VAD,
|
||
LIVE on the Mill since 07-10) ─▶ transcripts.jsonl — "a train is moving through," in words
|
||
▸ the most direct, gettable "is MY crossing blocked" signal
|
||
▸ proven 2026-06-16: same band as NOAA 162.550 — RX confirmed here
|
||
▸ CONFIRMED channel (2026-06-18): 160.980 (ch58) NS Dayton District, MP 148.99;
|
||
detectors CJ 144.7 / 157.8 announce here. Plan + sources → CHANNELS.md
|
||
▸ first intelligible speech 2026-07-10, the night the tuned antenna went in:
|
||
detector CJ at MP 146.4, transcribed (O-22)
|
||
stick: [1002 ★] Blog V4 — Airband demods the 5 pinned channels at once (160.86–161.19 @ 1.024 MS/s)
|
||
antenna: tuned railroad-band vertical (upgraded 07-10 — ≈2.4× the desk whip's real content;
|
||
office placement, rooftop mast = the standing next lever)
|
||
|
||
SECONDARY ░░ EOT-SYSTEM UHF — 1001 WEIGHTED HOP (452 ⇄ 457) · 1200-baud FFSK ░░
|
||
└ a train is a chain of RF emitters front-to-back; 1001 hops to watch both ends:
|
||
▸ 457.9375 EOT/FRED — the REAR unit; DECODED live: ID · brake · MOTION (rolling/stopped) ─▶ eot.jsonl + clips
|
||
decode is TWO-PASS (07-05): exact fast path + adaptive/fuzzy escalation for the
|
||
off-frequency short-burst emitters that used to read "strong but undecodable" (O-11)
|
||
▸ 452.9375 BOT / head-end (HOT) — the FRONT unit; frame RECOVERED + addressed-unit DECODED ─▶ bot.jsonl
|
||
▸ hop: iq_hop.py — retune-in-place, BOT 10s / EOT 10s (rebalanced 07-07: the head = the join + the EARLY WARNING; was 7/14 EOT-favored before the HOT decoder existed)
|
||
▸ EOT decode LIVE 2026-06-27 — real trains identified; brake-release + motion=1 = departed (crossing cleared)
|
||
▸ HOT address CRACKED 2026-06-28 from our own air (invert · sync 0110101010 · bit-49) → head↔tail JOIN:
|
||
a HOT frame addressing a card's own EOT = confirmed complete consist. Command/type + BCH still open.
|
||
stick: [1001] generic — drifts, ppm-calibrate for a clean FSK decode; 450 band clean (no filtering)
|
||
antenna: ~6" vertical — ¼λ ≈ 6.4" @ 457, well-matched
|
||
▸ MID / DPU — ±12.5k off both centers (452.925/.950, 457.925/.950), watched in-hop → midtrain.jsonl
|
||
found ░ a STANDING carrier on 457.925 = an FCC railroad EOT SIGNAL-BOOSTER (fixed wayside repeater, not a
|
||
train); carded as a STANDING SIGNAL. A booster here ⇒ marginal head↔tail RF — likely why our range is short.
|
||
|
||
SYNTHESIS ░░ the mission is DETECT a train, not decode one signal ░░
|
||
└ a train lights several bands at once — co-firing in the same 15-min slot = a candidate,
|
||
even when each is too weak to decode: 160.980 voice + 452 BOT + 457 EOT (+ ±12.5k DPU,
|
||
booster-floor subtracted so the fixed carrier can't fake a band — O-9)
|
||
▸ correlate panel = week heatmap (one row/band, aligned bins) — read DOWN a column
|
||
▸ BOT/mid/EOT strips below = the RECENT ZOOM (1-min bins, last 6 h) — correlate's magnifier
|
||
▸ live meter = bouncing dBFS bar for whichever band the hop is on (level.json)
|
||
|
||
MEANING ░░ the AIR CURVE is the state — psi tells the story silence can't (O-10 · O-11) ░░
|
||
└ from decoded EOT frames, per train:
|
||
psi ~85–90 + moving = PASSING through
|
||
psi drops to a HELD ~55–65 + motion 0 = DWELLING (brakes set — possibly blocking)
|
||
psi bleeds to 0 AND HOLDS + head still polling = CUT & STANDING — crews cut the crossing
|
||
(eyes-confirmed 07-05: ~11 h split, crossing open; never read the bleed as a departure)
|
||
psi RECHARGES toward ~88 + motion 0→1 = DEPARTED (the roll-out is the event time)
|
||
|
||
sanity ░ NOAA 162.550 (Columbus) — the proven known-good RX test on this band
|
||
(the per-radio toggle is parked since the Mill move; control relay TBD)
|
||
config ░ radio→task is swappable, not hardcoded — reassign per band
|
||
dropped ░ ATCS (900 MHz): LOS-limited + FCC-retired Sep 2025 → encrypted PTC;
|
||
our 900 "carriers" were a dongle spur. Why → radio/sdr/atcs/tuning-log.md
|
||
────────────────────────────────────────────────────────────────────
|
||
why voice first: unencrypted, easy at 160 MHz, and it says a train is
|
||
moving through your crossing in plain words — the direct commute signal.
|
||
</pre>
|
||
</section>
|
||
|
||
<!-- SYSTEM MAP — hand-maintained ASCII map of the software side: the browser, the local
|
||
servers/services, the ports, and how data flows. Sibling to SIGNAL FLOW (which maps the air).
|
||
A learning tool for us and whoever comes after. Keep it current as servers/flows change. -->
|
||
<section id="stack" class="panel stack">
|
||
<h2>System Map <span class="tag">the istrain dashboard & the services behind it</span></h2>
|
||
<pre class="flow-chart">
|
||
iSTRAIN — SYSTEM MAP the dashboard & the services behind it
|
||
how the pieces talk · keep current as it changes
|
||
legend: ─▶ request / data ◂ produced by ▣ = a container in the Dockge stack `istrain`
|
||
updated 2026-07-18 (the Mill cutover): ONE BOX — everything below is a container on the Mill
|
||
(a TrueNAS SCALE machine running Dockge); both dongles on its USB; every log AUTHORED where
|
||
it's served (the VM-era push pipeline is retired); istrain.jhestyr.net serves this stack.
|
||
────────────────────────────────────────────────────────────────────
|
||
|
||
BROWSER ▸ istrain.jhestyr.net (Cloudflare ─▶ the Mill's ▣ web container)
|
||
│
|
||
└─▶ ▣ web serve.py — stdlib Python, no framework
|
||
│ (keeps a jsonl PARSE CACHE keyed on file mtime/size + a 4 s TTL on the card
|
||
│ compute — polls are ~free; the logs only change every ~20-40 s)
|
||
├ static index.html · main.js · style.css · panels/strip.js (TWO grids: aligned week/15-min
|
||
│ for correlate · RECENT1M = 6h/1-min zoom for the band strips)
|
||
│ panels/{trains,live,supervisor,correlate,bot,midtrain,eot,checklist}
|
||
├ GET /api/version ◂ dashboard/VERSION (stamped by mill/deploy.sh) ─▶ header build stamp
|
||
├ GET /api/trains ◂ eot.jsonl + bot.jsonl (cards: tail+head ✓join, ⚓parked, ◉standing,
|
||
│ air-curve states: passing/dwelling/CUT&standing/departed) ─▶ Trains
|
||
├ GET /api/istrain ◂ strength-gated isTRAIN? verdict (+ dwelling-join fallback) ─▶ Banner
|
||
├ GET /api/level ◂ level.json (live dBFS) ─▶ Live meter (2×/s)
|
||
├ GET /api/heard ◂ heard.jsonl (voice detections) ─▶ Correlate (voice row)
|
||
├ GET /api/eot?hours=6 ◂ eot.jsonl ─▶ EOT zoom strip (no ?hours = week ─▶ Correlate)
|
||
├ GET /api/bot?hours=6 ◂ bot.jsonl ─▶ BOT zoom strip (ditto)
|
||
├ GET /api/midtrain?hours=6 ◂ midtrain.jsonl ─▶ Mid-train zoom strip (±12.5k DPU + booster)
|
||
├ GET /api/correlate ◂ scripts/correlate.py (recent-window why; booster-floor aware) ─▶ Correlate
|
||
├ GET /api/supervisor ◂ docker state via supervisor-probe (host cron, 1/min) + DATA-FLOW
|
||
│ liveness: hop = level.json freshness · voice = last-capture age
|
||
└ POST /api/noaa · /api/supervisor/{ctl,reset} (parked since the move — buttons decline
|
||
gracefully; a docker control relay is TBD)
|
||
|
||
DONGLES ░ USB by serial, both on the Mill through a hub · dvb driver blacklisted on the host
|
||
├ 1001 ▸ UHF hop — 452(BOT) ⇄ 457(EOT) └ 1002 ★ ▸ rail voice 160–162 / NOAA
|
||
|
||
CAPTURE ░ voice + the UHF hop run at once — separate dongles, separate containers
|
||
├ ▣ airband rtl_airband v5.2.0 (rtl-sdr-blog fork, V4-capable) -c istrain.conf
|
||
│ ─▶ 5 NFM channels on 1002 ─▶ captures/*.mp3 ─▶ heard.jsonl
|
||
└ ▣ scanhop rtl_tcp + iq_hop.py: retune-in-place hop on 1001 (BOT 10s ⇄ EOT 10s, monotonic dwell — step-proof)
|
||
├ 452 ─▶ bot.jsonl + bot_clips/*.s16 (presence + clip)
|
||
├ 457 ─▶ eot.jsonl + eot_clips/*.s16 (presence + clip)
|
||
└ both ─▶ level.json (live meter + the hop's liveness signal)
|
||
|
||
DECODE ░ containers · no radio · CPU on saved clips · NEVER cull
|
||
├ ▣ eot-decode ─▶ eot-decode.py → eot_scan (TWO-PASS: exact + adaptive/fuzzy for off-freq
|
||
│ emitters, O-11) → unit·brake·motion (+dq provenance) → enrich eot.jsonl
|
||
├ ▣ bot-recover ─▶ bot-recover.py: BOT clips → recover HOT frame + DECODE addressed unit → enrich bot.jsonl
|
||
└ ▣ transcribe-worker ─▶ voice clips >5 KB ─▶ ffmpeg comms-filter ─▶ the whisper stack
|
||
(faster-whisper small.en + VAD, a sibling Dockge stack, localhost) ─▶ sidecar .txt per clip;
|
||
real speech only ─▶ transcripts.jsonl (durable)
|
||
|
||
HOUSEKEEPING ░ ▣ trim — 24 h voice-audio prune (ingest-gated; heard.jsonl KEPT)
|
||
░ supervisor-probe — host cron, 1/min: docker truth into the snapshot
|
||
DATA ░ born on the Mill's ZFS datasets — jsonl logs = keep forever · clips accumulate
|
||
(keep-everything standing policy) · the VM-era corpus is archived alongside
|
||
|
||
build vs operate ░ the hands build the code; the radios are operated by services —
|
||
never fired ad hoc. deploy = mill/deploy.sh: rsync ─▶ build ─▶ up ─▶ smoke, idempotent.
|
||
history ░ the VM-capture / NAS-web era (2026-06-15 → 07-18) is preserved in MIGRATION.md;
|
||
the VM units stay on disk, disabled — the standing rollback.
|
||
────────────────────────────────────────────────────────────────────
|
||
</pre>
|
||
</section>
|
||
</main>
|
||
|
||
<!-- WITH THANKS — carried from the first istrain, extended for the rebuild -->
|
||
<section class="thanks">
|
||
<div class="th-h">📡 WITH THANKS</div>
|
||
<p>Railroad frequencies, the tools that pull signal out of the air, and the hands that built it.</p>
|
||
<p class="th-grp"><b>Frequency & data communities</b><br>
|
||
• <a href="https://trainweb.us/columbusrailfan/">Columbus Railfan Online</a> (Silver Rails / TrainWeb) — scanner frequencies<br>
|
||
• <a href="https://www.radioreference.com/db/subcat/77377">RadioReference</a> — NS Columbus Terminal & the US Railroads index<br>
|
||
• <a href="https://www.railroad-frequencies.com/train/norfolk-southern-railway/ohio/">Railroad-Frequencies.com</a> — NS & CSX Ohio assignments
|
||
</p>
|
||
<p class="th-grp"><b>Tools & projects</b><br>
|
||
• <a href="https://www.rtl-sdr.com/">RTL-SDR Blog</a> — the Blog V4 hardware and the community behind it<br>
|
||
• <a href="https://osmocom.org/projects/rtl-sdr/">Osmocom rtl-sdr</a> — <code>rtl_test</code> / <code>rtl_eeprom</code>, the tools that drive the dongles<br>
|
||
• <a href="https://github.com/szpajder/RTLSDR-Airband">RTLSDR-Airband</a> — the multi-channel voice watch (we run the <a href="https://github.com/rtl-sdr-blog/RTLSDR-Airband">rtl-sdr-blog fork</a>, V4-capable)<br>
|
||
• <a href="https://github.com/SYSTRAN/faster-whisper">faster-whisper</a> (SYSTRAN) + <a href="https://github.com/openai/whisper">OpenAI Whisper</a> — the transcription engine that lets istrain <em>hear</em> (small.en + VAD)<br>
|
||
• <a href="https://github.com/ggerganov/whisper.cpp">whisper.cpp</a> — lit the transcription path first, in the VM era<br>
|
||
• <a href="https://ffmpeg.org/">FFmpeg</a> — the comms-filter in front of the ears, and every clip's plumbing<br>
|
||
• <a href="https://github.com/jvde-github/AIS-catcher">AIS-catcher</a> — AIS decode, our known-good validation signal<br>
|
||
• <a href="https://github.com/ereuter/PyEOT">PyEOT</a> (Eric Reuter) — the open-source EOT/FFSK decoder (packet parser + BCH check) that proved we can read End-of-Train telemetry; ships the demo recording we validated against<br>
|
||
• <a href="https://www.sigidwiki.com/wiki/End_of_Train_Device_(EOTD)">SigidWiki — EOTD</a> & <a href="https://www.rtl-sdr.com/tag/eot/">rtl-sdr.com EOT guides</a> — the 1200-baud FFSK spec and the decode trail that pointed the way<br>
|
||
• <a href="https://numpy.org/">NumPy</a> — the signal-crunching behind the EOT demodulator<br>
|
||
• <strong>ATCS Monitor</strong> & <a href="https://rail.watch/rwmon.html">Rail Watch Monitor</a> — the 900 MHz dispatcher's board, and the community that mapped ATCS (evaluated & dropped — kept for provenance)<br>
|
||
• <a href="https://www.radioreference.com/db/subcat/69805">RadioReference ATCS DB</a> & <a href="https://www.sigidwiki.com/wiki/Automated_Train_Control_System_(ATCS)">SigidWiki</a> — the published channel plans that told us what we'd found<br>
|
||
• <a href="https://www.docker.com/">Docker</a> + <a href="https://github.com/louislam/dockge">Dockge</a> (louislam) — the runtime every service lives in since the Mill move<br>
|
||
• <a href="https://tailscale.com/">Tailscale</a> — the quiet plumbing between the boxes<br>
|
||
• <a href="https://www.cloudflare.com/">Cloudflare</a> — the front door
|
||
</p>
|
||
<p class="th-grp"><b>The machine</b><br>
|
||
• <a href="https://www.truenas.com/truenas-scale/">TrueNAS SCALE</a> on the Mill — the one box the whole rig runs on (since 2026-07-18): dongles, capture, decode, ears, and this site<br>
|
||
• <a href="https://www.virtualbox.org/">Oracle VM VirtualBox</a> + <a href="https://linuxmint.com/">Linux Mint (LMDE)</a> — the dev seat, and the box the rig grew up inside (2026-06-15 → 07-18)
|
||
</p>
|
||
<p class="th-grp"><b>Foundation</b><br>
|
||
• built on a reusable radio-capture engine and iterated with an AI pair-programmer (<a href="https://www.anthropic.com/claude">Claude</a>, Anthropic)<br>
|
||
• a community project — the credits above are the shoulders it stands on; if your tool, frequency, or hand helped and isn’t here yet, it will be.
|
||
</p>
|
||
<p class="th-grp"><b>A living list</b><br>
|
||
istrain is a community project — this list is intentionally unfinished and grows as the work brings in
|
||
more shoulders to stand on. If your tool, frequency, or hand helped and isn't here yet, it will be.
|
||
</p>
|
||
</section>
|
||
|
||
<!-- live: Supervisor — services + radios (moved below THANKS: the ops corner
|
||
lives at the bottom with the errors card; the triple-tap gate is on this panel's title) -->
|
||
<section id="supervisor" class="panel yard"></section>
|
||
|
||
<!-- ERRORS — live issues + recent events, below WITH THANKS (the owner 2026-07-06): the card to
|
||
copy/share when something looks wrong. Fed by /api/errors (supervisor audit + client JS reports). -->
|
||
<section id="errors" class="panel errors"></section>
|
||
|
||
<!-- BUILD CHECKLIST — rendered from checklist.md (single source; also a git host task list).
|
||
Keep checklist.md current; this panel mirrors it live. -->
|
||
<section id="checklist" class="panel checklist"></section>
|
||
|
||
<footer>
|
||
<span id="status">live — one box: the Mill captures, decodes & presents</span> ·
|
||
<a href="https://istrain.jhestyr.net">istrain.jhestyr.net</a> · an open community project
|
||
</footer>
|
||
|
||
<script type="module" src="/main.js"></script>
|
||
</body>
|
||
</html>
|