igneum/relay
igneum-josh 1950661c11 hot table (Counter ASIC 2.0 layer 5), measured and not adopted, on the ca2-v3 composed class (squash of tag ca2-cache-history-2026-10-05)
LoadClass::hot (Option<HotClass { mb, k, added }>) beside mix, slots, scratch, mixer_mult, growth and era; V3_CLASS = { era: None, hot: None, ..LoadClass::MX4 } (hot stays None: the hot code is behind the flag, measured and not adopted). Op::Hot, the hot slots drawn after the scratch slots, no width roll (v2_loads allows the added form's extra slots), the id suffix hot/<S><k>[added], the hot parsing inside parse_loads, the era branch first in name(). HotTable under seed_words("igneum-hot/" || epoch seed) with the cache chain and tag umHT, read at H[mulhi(src, HOT_WORDS)]; DatasetSource::hot attached by new_class_day and from_seed_bytes_class; the acceptance stand-in dataset_elem(idx, S[2], S[3]); the three emitters (hot argument after the init words, ht_segment and igneum_hot_fill beside the layout-aware cores); packfile.h hot fields beside class, era, attempt and mixerMult; OpenCL host, Metal packbench and NVRTC worker fill H on the device and self-test it. Eight packs under proto-cuda/packs-ca2-hot (replaced hot32k4 hot64k4 hot96k4 hot64k2 hot64k8, added hot32k4a hot64k4a hot96k4a) re-exported on the merged crate: vectors unchanged, program.h and program.json carry the mixer fields. docs/plans/hot-table.md (design, spec text, per-tier budget, chip model, Mac and PC measurements, the decision: layer 5 out of v3, the 3.0 note); bench-log entry and addenda with job ids and worker sha256s; the two PC playbooks.

Checks on this commit: cargo test --release 53 + 19 pass (the pinned v2, mx4, era, readwidth and hot packs); the pinned packs under proto-cuda/packs, packs-ca2-mixer, packs-ca2-era and packs-readwidth untouched; Metal packbench and Apple OpenCL --bench-pack on all eight hot packs (run lock, 2^20 at base 0): 96/96 lanes, hot table head, last line and FNV PASS, one fingerprint per pack on both harnesses, equal to the fingerprints before the rebase (hot32k4 679e5e83378d3790, hot64k4 d4c9e456b039fdef, hot96k4 7c98eceffee9fd73, hot64k2 f43b10a95879b8e5, hot64k8 c11309d743be9392, hot32k4a afb700b2d997c847, hot64k4a ba214baa9c1a9e85, hot96k4a 29e1916aed6deff5). No era or mixer behaviour changed: every resolution kept the ca2-v3 side and appended the hot branch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-05 22:57:39 +01:00
..
api relay: /wake long-poll for the apps' remote jobs (public GET held 45 s, authenticated POST of the stamp) 2026-10-05 09:22:02 +01:00
clients Relay: its own key (relay-key) replaces the intake key for the Mac tools and clients; relay token rotated 4 Oct 2026 (round 4, X23); prove package excludes cross-build folders 2026-10-04 19:25:19 +01:00
lib Workers: a pack's seed words are its program attempt's words, not the bare seed's (epoch 34 incident, 5 October 2026) 2026-10-05 20:10:07 +01:00
playbooks hot table (Counter ASIC 2.0 layer 5), measured and not adopted, on the ca2-v3 composed class (squash of tag ca2-cache-history-2026-10-05) 2026-10-05 22:57:39 +01:00
test Workers: a pack's seed words are its program attempt's words, not the bare seed's (epoch 34 incident, 5 October 2026) 2026-10-05 20:10:07 +01:00
.gitignore Relay: text, files and runnable tasks between the Mac, the PCs and the phone (relay.igneum.network) 2026-10-04 11:04:30 +01:00
apple-touch-icon.png Brand: one master mark (black square, no circle) for every icon, favicon and profile picture 2026-10-04 12:39:55 +01:00
favicon-32.png Brand: one master mark (black square, no circle) for every icon, favicon and profile picture 2026-10-04 12:39:55 +01:00
favicon.ico Brand: one master mark (black square, no circle) for every icon, favicon and profile picture 2026-10-04 12:39:55 +01:00
index.html Brand: one master mark (black square, no circle) for every icon, favicon and profile picture 2026-10-04 12:39:55 +01:00
package-lock.json Relay: text, files and runnable tasks between the Mac, the PCs and the phone (relay.igneum.network) 2026-10-04 11:04:30 +01:00
package.json Relay: text, files and runnable tasks between the Mac, the PCs and the phone (relay.igneum.network) 2026-10-04 11:04:30 +01:00
README.md relay: /wake long-poll for the apps' remote jobs (public GET held 45 s, authenticated POST of the stamp) 2026-10-05 09:22:02 +01:00
robots.txt Relay: text, files and runnable tasks between the Mac, the PCs and the phone (relay.igneum.network) 2026-10-04 11:04:30 +01:00
ui.html Console: a finished update shows as the OTA state ('update check: X is current'), not 'none' (PC 2 on 0.3.4); test 2026-10-04 21:43:51 +01:00
vercel.json relay: /wake long-poll for the apps' remote jobs (public GET held 45 s, authenticated POST of the stamp) 2026-10-05 09:22:02 +01:00

Igneum relay and console

Text, files and tasks between Josh's devices without Gmail: the Mac, PC1, PC2 and the phone post to one feed and read from it. Vercel project igneum-relay, served at https://relay.igneum.network. Built 4 October 2026. Since the evening of 4 October 2026 the same private page is the Igneum console (ui.html): seven tabs, phone first, refreshed every 15 s. The relay feed and drop box are its last tab.

Scope since 4 October 2026 (afternoon): the relay stays for the Mac and for humans (notes, files, tasks for a person or a Claude session on a PC). Commands and files for the PCs themselves go over the line to the Igneum Miner app instead: signed jobs published next to the update manifest (packaging/ota/publish-jobs.sh, read back with tools/jobs.mjs, documented in packaging/ota/README.md, "Remote jobs"). The app jobs replace the PC agent (igneum-agent.bat): PC 2 has no Claude session and nobody at the keyboard, and both PCs report the same hostname (DESKTOP-KMCV30N), which the relay's registration cannot tell apart; the app's per-install machine id can. The playbooks under relay/playbooks/ stay as the relay form of the same runs (shard-test.ps1 is the model for a run job) and are parse-checked by windows.yml.

The console

Tab Shows Source
Machines one card per machine: app and node version, height (daa), synced, hash rate, accepted blocks, peers, faults, power and temperatures, last seen; red after 3 min without an upload, grey "stopped (quit|update)" when the app's last upload ends on its own quit lines; a worker card whose STATUS line is over 180 s old is marked stale and left out of the machine total; the OTA state is the newest update line the app logged (downloading, downloaded, staged, installing, updated, failed, current); worker labels nvidia, amd, mac, metal, opencl, other and intel Neon miner_logs (the log intake in site/api/log.mjs): newest upload per label, the last 20 KB parsed server side (miner STATUS lines, node log, the app's stability: lines)
Jobs the signed jobs file with per-machine status (queued, running, done + exit code) and the result line; tap a run for the full upload igneum-jobs.json on the downloads host (fetched server side with DL_TOKEN), results from miner_logs rows whose run_id is job-<id>-<machine>
Builds the OTA manifest (version, notes, platforms, sizes), the last CI fetch, build events, the downloads folder listing igneum-app-latest.json and igneum-windows-ci.json on the downloads host; console_items kind build (posted by packaging/windows/fetch-ci-artifacts.sh and packaging/ota/publish-manifest.sh) and key dl (tools/console.mjs sync-dl)
Chain blocks, identities, hash estimate, difficulty, last lock, finality state, peers, blocks per minute sparkline, events, Hetzner results https://igneum.network/api/live fetched server side; console_items key hetzner (sync-hetzner)
Work log what the agents and the main session post, merged with every relay item, newest first console_items kinds log, build, note; the relay feed
Results the bench log entries (heading + first paragraph), newest first; the FUD ledger counts by status console_items kind bench and key ledger, written by tools/console.mjs sync-bench from docs/bench-log.md and docs/fud-ledger.md
Relay the feed and the drop box, unchanged relay_items, relay_machines

The console function is api/console.mjs, reached through the rewrite /r/<token>/c/<fn>. Every GET answer is cached 10 s in the function instance. No secret reaches the client: the token in the path is the only auth, and DL_TOKEN (the downloads folder) lives in the project env and is used only server side. No GitHub token anywhere: build events come from the Mac-side scripts.

Mac: node tools/console.mjs post --kind log --title "..." --body "..." writes one work-log item (kinds log, build, note); log, machines, chain, jobs, builds, results print the tabs; sync-bench, sync-dl, sync-hetzner or sync push the file-derived data; url prints the link.

The app log (label win-<id8> or mac-<id8>) reaches the intake since b8b349a (4 Oct 2026, 0.3.3); apps before that show app ?. The parsers (labels, miner tail, app tail, the stale mark) live in relay/lib/parse.mjs with no dependencies, and relay/lib/auth.mjs holds the constant-time secret compare; node --test relay/test/parse.test.mjs relay/test/auth.test.mjs runs their tests, and CI runs them in the site job.

The secret is the path

The web page lives at /r/<token>/ and every API call sits under /r/<token>/api/<fn>. The token is 20 base32 characters generated once and stored at ~/.config/igneum/relay-token on the Mac (and as RELAY_TOKEN in the project). Anyone with the link can read and post, so the link stays with Josh. Scripts may present the log intake key in x-igneum-key instead (RELAY_KEY, the same value as ~/.config/igneum/log-intake-key). There is no other login. Blob file URLs carry a random segment and a random suffix; they are not listed anywhere.

What is stored where

Thing Where Limit
Items (text, title, who, kind, flags, read and done marks) Neon table relay_items (database igneum) body 1 MB
Machines (name, hostname, role, GPU and WSL facts, last seen) Neon table relay_machines
Files Vercel Blob store igneum-relay (public URLs with random path and suffix, London) 50 MB per file through a client token; 4 MB when pushed through the function
The token and key ~/.config/igneum/relay-token, ~/.config/igneum/relay-key (the relay's own key since 4 October 2026, round 4 X23; the log-intake key no longer opens the relay); project env never in the repo

Kinds: text (a note), file, task (for a person or a Claude session on a PC), run (a script the agent executes), result (what a task produced, linked by task_id). Roles: miner, prover, bench, mac, phone.

API (all under /r/<token>/api/)

Call Does
GET feed?since=&before=&machine=&limit= items newest first (200 by default) plus every machine with its unread count
GET item?id= one item with its full body
GET file?id=[&download=1] 302 to the file
`GET inbox?machine=PC1&kind=run task&ack=1`
GET machines names, roles, hostnames, last seen
POST drop JSON {from,to,kind,title,body,file_name,file_url,size,task_id,flags}; or raw bytes with Content-Type: application/octet-stream and x-file-name (4 MB cap)
POST task same fields; kind task or run; run needs one named machine and flags {elevated, reboot_continue}
POST upload {name,size} returns a one-hour Blob client token and put_url; PUT the bytes there, then drop with the returned url
POST ack {ids} POST done {id,exit_code} POST delete {id} marks
POST register {hostname,info} a machine checks in; returns its name, role and whether it is named
POST name {hostname,name} POST role {name,role} naming and roles, from the Mac

Wake (api/wake.mjs, 0.3.6, 5 October 2026): GET /wake?since=<stamp> is public (the apps hold no token) and rate limited, 30 a minute per IP. It holds up to 45 s and answers {stamp, at, added, changed, held_ms} the moment the stored stamp differs from since, else the unchanged stamp at the deadline; without since it answers at once. POST /r/<token>/wake {stamp, added} (or POST /wake with x-relay-token or x-igneum-key) records the stamp; packaging/ota/publish-jobs.sh sends it after every verified deploy, with the ids it added. One row per stamp in Neon table relay_wake (created by the first POST); tools/jobs.mjs status reads the rows for the woken latency. The function's maxDuration is 60 s (vercel.json). Tests: relay/test/wake.test.mjs drives the handler with a fake database and clock.

Mac

node tools/relay.mjs (feed), read <id>, drop "<text>"|<file>, task PC2 "title" [file], run PC2 "title" script.ps1 [--elevated] [--reboot-continue], watch, inbox PC1, machines, role PC2 prover, name DESKTOP-XYZ PC2, ack|done|rm <id>, url. Playbooks live in relay/playbooks/; run fills __DL_BASE__ in from ~/.config/igneum/dl-token.

PCs

relay/clients/make-clients.sh bakes the URL, key and token into copies of the clients and writes ~/Desktop/igneum-relay-clients.zip. Unzip anywhere on the PC. send.bat for people and Claude sessions (see CLAUDE-PC.md), igneum-agent.bat for the automatic runner: double-click once, leave it open. It registers the PC (hostname, GPUs, WSL, nvcc), polls every 20 s, runs each run task in order, posts a result (exit code, last 64 KB inline, full log as a file when longer) and marks it done. A script that prints RELAY-REBOOT triggers shutdown /r /t 10; with reboot_continue the agent re-arms (scheduled task at logon with highest privileges, RunOnce as a fallback) and re-runs the task after the restart with RELAY_PASS incremented. The PC must sign in by itself for that to be unattended.

An unknown hostname that registers appears in the feed with a "name this machine" box, or node tools/relay.mjs name <hostname> PC2. PC1 is DESKTOP-KMCV30N.

Deploy

cd relay && npx --yes vercel@latest --global-config ~/.config/igneum/vercel deploy --prod --yes --scope igneum

Env on the project: DATABASE_URL, RELAY_KEY, RELAY_TOKEN, BLOB_READ_WRITE_TOKEN (added by vercel blob create-store), DL_TOKEN (the downloads folder token, for the console; added 4 Oct 2026). DNS: relay CNAME cname.vercel-dns.com in the deSEC zone.

Untested until a PC runs it (4 Oct 2026)

send.ps1, igneum-agent.ps1 and the five PowerShell playbooks were written and syntax-reviewed on the Mac (no pwsh here). The bash twin agent.sh and send.sh ran end to end against the live relay. Expect a first-run fix on Windows: Start-Process -Wait exit codes through the wrapper, wsl --install --no-launch on pass 2, the RunOnce path after a reboot.