igneum/relay/README.md

75 lines
8.8 KiB
Markdown

# Igneum relay and console
Text, files and tasks between the project lead'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 | 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.
Known gap (4 Oct 2026): the app log (label `win-<id8>` or `mac-<id8>`) never reaches the intake, because `app/igneum-app/src/main.rs` names the file with its own `stamp_now()` while `engine.rs` uploads `app-<engine stamp>.log`. Until that is fixed the Machines tab has no app version, no `stability:` power and temperature lines and no `status:` lines; everything else comes from the node and miner logs.
## 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 the project lead. 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` | unread, not done tasks for that machine; `ack=1` marks them read |
| `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 |
## 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.