ledger: X23 to X29, G13, G14 fixed on ledger-relay 1065b81; bench-log entry for the night's test runs
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
1065b81d05
commit
359d4662fd
2 changed files with 43 additions and 9 deletions
|
|
@ -1524,3 +1524,19 @@ What is measured: one BLS12-381 aggregate signature over 16 summed G1 keys plus
|
|||
| on, split 90 s | v3 | 0 / 2 | none / 3 | 278 / 265 | apart | none | 3 on n0 | 2 (n0 reconnected 6 s after the heal, A's chain at about 58 DAA, inside the table) |
|
||||
|
||||
Reading (the NEW finding, ledger C4). With the module off GHOSTDAG alone converges on the heavier chain and the losing side's records re-determine (F24 works when the chain moves). With the module on the overlay holds during the split (A, with 30% of the frozen table, locks nothing; B locks 7 and 8) and then fails at the heal in the shipped node: B's certificates for blocks off n0's chain are "kept pending until the chain decides (no lock at this index)", n0's chain never decides because GHOSTDAG keeps its heavier tip and nothing turns the certificate into a fork-choice constraint, and once n0's last lock (index 7, DAA 209) is one window old (DAA 329) the frozen table stops applying on A's chain ("no frozen table (no lock on this chain inside the window)"), A's two keys are 100% of A's own window (B's post-cut blocks are red there) and n0 locks 10, 11, 12 alone; B's certificates for 10 and 11 then log CONFLICTING on n0 (n0 log, 17:27:04 to 17:29:54 BST). A finality fork from a 96-s honest partition, no attacker, table intact at the heal; the 150-s run and the v2 control end the same way. The spec's fork choice ("GHOSTDAG among tips through all certified checkpoints", 3.5) is therefore implemented only for certificates over blocks already on the node's chain. Fix named in the ledger entry: verify an off-chain certificate against the table at its own block and let it constrain fork choice (a certificate-driven reorg), then re-determine. Raw: `scratchpad fud-a/c4-results-*.md`, node logs `c4-on90-tmp/`, `c4-v2-control-tmp/`.
|
||||
|
||||
## 5 October 2026 (night), ledger close round 1: relay and CI fixes X23 to X28, G13, G14
|
||||
|
||||
Branch `ledger-relay`, worktree `igneum-wt-ledger-relay`, commit `28c028b` (the ledger and this entry follow in the next commit). Mac only, node 22.23.2, no network, no database, nothing deployed, nothing pushed, no PC touched.
|
||||
|
||||
| What ran | Command | Result |
|
||||
|---|---|---|
|
||||
| the relay suites (parsers, secret compare, wake, the new guards, the handler against a fake Neon and fake blobs, the clients' shape) | `node --test relay/test/parse.test.mjs relay/test/auth.test.mjs relay/test/wake.test.mjs relay/test/guard.test.mjs relay/test/handler.test.mjs relay/test/clients.test.mjs` | 47 tests, 47 pass, 0 fail (15 + 10 + 12 + 10) |
|
||||
| the signed-inputs chain (G13), with the signer from the main checkout | `IGNEUM_OTA_SIGN=/Users/joshm/Projects/igneum/app/igneum-app/target/release/igneum-ota-sign bash packaging/windows/test-inputs-signing.sh` | 16 passed, 0 failed; the one-byte-appended zip is refused on its sha256 |
|
||||
| TZ=UTC in every tooling commit path (G14), self-test then the tree | `bash tools/ci/commit-tz-check.sh --self-test && bash tools/ci/commit-tz-check.sh` | fires on the bad case; the tree is clean after `packaging/ota/publish-jobs.sh` gained `export TZ=UTC`; 417 commits on the branch still carry a non-UTC offset (the rewrite, the project lead's date) |
|
||||
| no secret header on a curl command line (X29), self-test then the tree | `bash tools/ci/curl-header-check.sh --self-test && bash tools/ci/curl-header-check.sh` | fires on the .sh and .bat bad cases; 0 hits in the tree |
|
||||
| the existing tree checks | `tools/ci/no-secrets-check.sh`, `tools/ci/copied-sources-check.sh`, `bash -n` on every touched shell script, `node --check` on every touched .mjs | 0 hits, clean, all parse |
|
||||
|
||||
What the handler tests prove at the API (relay/test/handler.test.mjs): a token-only `POST task` with `kind: run` is 401 and a signed, tagged, fresh one is stored (X23); the `x-relay-token` header with no token in the path is the token tier (X24); a `result` whose `from` does not match the caller's machine secret is 403 (X27); the intake key may only upload and drop files, and reads nothing (X23); `GET inbox` never acks (X28); 120 rows and `limit=500` return 100 (X26); a row older than 30 days goes with its blob on the next sweep (X26); 429 after the per-IP limit (X28).
|
||||
|
||||
Not measured tonight, by design: anything on PC 1 or PC 2 (the `schtasks /Query` check for X25, the 401 against the deployed relay for X23, the Vercel log view for X24, the GitHub workflow run for G13). The PowerShell 5.1 parse of the rewritten clients runs in `windows.yml` on the next push; on the Mac the clients are checked in structural terms only (`clients.test.mjs`).
|
||||
|
|
|
|||
|
|
@ -1707,57 +1707,69 @@ Review: `docs/review/round-4-2026-10-04.md`. Scope: devnet v4 through `a21ff239`
|
|||
### X23. One shipped key is an administrator channel to the founder's PCs
|
||||
"Your relay accepts either the URL token or the `x-igneum-key` header for everything, including queuing PowerShell that the PC agent runs as administrator. The key is the log intake key, a literal in six tracked files and inside every Windows and prover package you have handed out. And the zip with both relay secrets sits on the downloads host behind the dl token, which is in your git history and in every installed app. One token, four hops, no privilege boundary."
|
||||
|
||||
Status: Open, fatal as an operational fact (4 October 2026). The agent was live and elevated on PC 1 at 13:41 UTC (`GET machines`, read-only). Sweep (5 October 2026): the relay token was rotated on 4 October (`~/.config/igneum/relay-token.old-2026-10-04` sits beside the new one); `POST task` with `kind: run` still needs only the token (`relay/api/relay.mjs:114-119`, no per-machine secret or signature), so the operational half stands. The 401 test needs the deployed relay and PC 1 (relay owner; rotation is the project lead's).
|
||||
Status: Fixed on a branch, pending merge (5 October 2026, night): ledger-relay 28c028b, tests 47 of 47 (relay, 6 suites) + 16 of 16 (packaging/windows/test-inputs-signing.sh) + 2 CI class checks. Was: Open, fatal as an operational fact (4 October 2026). The agent was live and elevated on PC 1 at 13:41 UTC (`GET machines`, read-only). Sweep (5 October 2026): the relay token was rotated on 4 October (`~/.config/igneum/relay-token.old-2026-10-04` sits beside the new one); `POST task` with `kind: run` still needs only the token (`relay/api/relay.mjs:114-119`, no per-machine secret or signature), so the operational half stands. The 401 test needs the deployed relay and PC 1 (relay owner; rotation is the project lead's).
|
||||
|
||||
Answer: Correct. `relay/lib/relay.mjs:33-42` returns a truthy value for either secret and `relay/api/relay.mjs:111-124` accepts `kind: run` with `flags.elevated` from it; `relay/clients/igneum-agent.ps1:165-166` runs every item returned, as administrator, within 20 s. `README.md:7` and `make-clients.sh:8` make `RELAY_KEY` the intake key. The hosted `igneum-relay-clients.zip` carries the relay token and the key in four files; the dl token that guards it is 0644 on the Mac, in commit `c47ff03`, and in `igneum-app.json` of every install. Fix, in order: the zip off the host; rotate the relay token; a relay key of its own; a second per-machine secret or an Ed25519 signature over `{id, to, body}` for `run`; rotate the intake key and repackage. Review ids R4.4.1, R4.4.2, R4.5.1.
|
||||
|
||||
Evidence: the files above; `docs/review/round-4-2026-10-04.md` sections 4 and 5. Experiment: after the fix, `POST task` with the old key and with a key-only header must return 401, and `GET machines` must show the rotated agent on PC 1.
|
||||
|
||||
Fix (5 October 2026, night): three tiers in `relay/lib/guard.mjs` (`authVia`): the console token (header or the phone page's path), the relay's own key (`RELAY_KEY`: reads and reports, never `task`, `run`, `name`, `role`, `secret`, `delete`), and the intake key (`LOG_INTAKE_KEY`, `_NEXT`) as a tier of its own that may only `upload` and `drop` a file or a note, no reads; it exists because the 0.3.6+ apps upload build-job outputs with it (`app/igneum-app/src/jobrun.rs`, `relay_upload`), and `RELAY_INTAKE_COMPAT=0` on the project closes it the day the apps carry a relay key (that app change is owed, not in this branch). A `run` task now needs, on top of the token, `flags.sig`, an Ed25519 signature by the Mac's run key (`~/.config/igneum/relay-run-key`, `node tools/relay.mjs keygen`) over `runCanon` = {machine, nonce, body sha256, elevated, reboot_continue, reboot}, verified by the relay with `RELAY_RUN_PUB` (401 without it or with a wrong one; 409 on a reused nonce; every run refused while `RELAY_RUN_PUB` is unset), and `flags.mac`, an HMAC-SHA256 tag with the target PC's own secret that `igneum-agent.ps1` (`Check-Task`) and `agent.sh` (`check_task`) verify before anything runs (exit 77 and a result when it fails; Windows PowerShell 5.1 has no Ed25519, so the agent's check is the HMAC). The console and the wake POST refuse the intake tier (`authedNoIntake`). Tests: `relay/test/handler.test.mjs` (a token-only `POST task` kind `run` is 401; a signed one is stored; a changed body, flag or target, another key, or no `RELAY_RUN_PUB` is 401; the relay key gets 403 on `task`; the intake key gets 403 on everything but `upload` and a file drop), `relay/test/guard.test.mjs` (the signature, the tag, `checkRun`). Owed to the project lead: `keygen` and `RELAY_RUN_PUB` on the project, one `secret` per PC with the zip carried by hand, the hosted `igneum-relay-clients.zip` off the downloads host (its values are dead since the 4 and 5 October rotations, the file remains), and the deploy (`relay/README.md`, "Rotation"). The 401 against the deployed relay and `GET machines` on PC 1 run after that deploy.
|
||||
|
||||
### X24. The relay token rides in the URL on every request
|
||||
"Every poll of every agent and every page refresh puts the token in the path, so it is in Vercel's request logs, in browser history and in every terminal that ran `tools/relay.mjs`. You built an `x-relay-token` header and nobody uses it."
|
||||
|
||||
Status: Open (4 October 2026); the print lines fixed: `tools/relay.mjs` shows `/r/<token>` in `list` and `watch` (only `url` prints the real one). Sweep (5 October 2026): `x-relay-token` is accepted (`relay/lib/relay.mjs:38`) but no client sends it (no match in `relay/clients`, `tools/relay.mjs` or `app/igneum-app/src`), so every request still carries the token in the path. The Vercel log check needs the deployment (relay owner).
|
||||
Status: Fixed on a branch, pending merge (5 October 2026, night): ledger-relay 28c028b, tests 47 of 47 (relay, 6 suites) + 16 of 16 (packaging/windows/test-inputs-signing.sh) + 2 CI class checks. Was: Open (4 October 2026); the print lines fixed: `tools/relay.mjs` shows `/r/<token>` in `list` and `watch` (only `url` prints the real one). Sweep (5 October 2026): `x-relay-token` is accepted (`relay/lib/relay.mjs:38`) but no client sends it (no match in `relay/clients`, `tools/relay.mjs` or `app/igneum-app/src`), so every request still carries the token in the path. The Vercel log check needs the deployment (relay owner).
|
||||
|
||||
Answer: Correct. `relay/vercel.json:6` rewrites `/r/<token>/api/<fn>` to a query string; `igneum-agent.ps1:14`, `send.ps1:26`, `send.sh:11`, `agent.sh:10` and `tools/relay.mjs:24` all build the tokened URL, and `tools/relay.mjs:83,88,125` print it. `Referrer-Policy: no-referrer` and `X-Robots-Tag` are set (`vercel.json:12-13`); there is no HSTS. Fix: the header in every client, the URL token kept for the phone's page only, HSTS, and the print lines masked. Review id R4.4.3.
|
||||
|
||||
Evidence: the files above. Experiment: the Vercel log view for `igneum-relay` after the change shows no token in any path.
|
||||
|
||||
Fix (5 October 2026, night): every client and Mac tool calls `/api/relay?fn=<fn>` with `x-relay-token` (and `x-igneum-key`) as headers: `igneum-agent.ps1` and `send.ps1` (`Api-Url`), `agent.sh` and `send.sh` (through a 0600 curl config file, `-K`, so the headers are on no command line either), `tools/relay.mjs`, `tools/console.mjs` (`/api/console?fn=`), `tools/build-job.mjs` (the token, else the relay key; the intake key reads nothing now). `igneum-agent.bat` and `send.bat` build no URL. The path token stays for the phone's page only: `/r/<token>/` (`ui.html`) and the calls that page makes (`/r/<token>/api/<fn>`, `/r/<token>/c/<fn>`, `/r/<token>/wake`), documented in `relay/README.md`. Print lines: `tools/relay.mjs` shows `/r/<token>` everywhere but `url` (unchanged). Tests: `handler.test.mjs` (a request with the header and no token in the path is the token tier; a wrong header is 401), `clients.test.mjs` (every client and tool sends the header and none builds a tokened API path). Owed: the Vercel log view after the deploy (relay owner).
|
||||
|
||||
### X25. The PC agent installs itself at every logon, at highest privilege, on every start
|
||||
"Double-click once and the agent writes a scheduled task with `/RL HIGHEST` and a RunOnce key. Your README says it only re-arms for a reboot. Closing the window does nothing."
|
||||
|
||||
Status: Open (4 October 2026). Sweep (5 October 2026): unchanged (`relay/clients/igneum-agent.ps1:72`, `schtasks /SC ONLOGON /RL HIGHEST`). The `schtasks /Query` check needs PC 1.
|
||||
Status: Fixed on a branch, pending merge (5 October 2026, night): ledger-relay 28c028b, tests 47 of 47 (relay, 6 suites) + 16 of 16 (packaging/windows/test-inputs-signing.sh) + 2 CI class checks. Was: Open (4 October 2026). Sweep (5 October 2026): unchanged (`relay/clients/igneum-agent.ps1:72`, `schtasks /SC ONLOGON /RL HIGHEST`). The `schtasks /Query` check needs PC 1.
|
||||
|
||||
Answer: Correct. `Arm-Restart` (`igneum-agent.ps1:68-79`) runs at `:154` on every start; `README.md:42` describes it as the `reboot_continue` path. Fix: arm only when a task asks for a reboot, and remove the task and the key on a clean exit. Review id R4.4.4.
|
||||
|
||||
Evidence: `relay/clients/igneum-agent.ps1`. Experiment: `schtasks /Query /TN IgneumRelayAgent` on PC 1 before and after.
|
||||
|
||||
Fix (5 October 2026, night): `igneum-agent.ps1` calls `Arm-Restart` only on the two paths that end in `shutdown.exe /r` (a task that printed `RELAY-REBOOT` on its own line AND was queued with `--reboot` or `--reboot-continue`), sets `$script:KeepArmed` for that one exit, and `Disarm-Restart` (`schtasks /Delete /F /TN IgneumRelayAgent`, `Remove-ItemProperty` on the RunOnce key) runs on every start and in the main loop's `finally` (Ctrl+C included; a closed window skips `finally`, so the next start disarms again). The top-level `Arm-Restart` is gone. Tested on the Mac in parsing terms only (no `pwsh` here): `relay/test/clients.test.mjs` asserts `Arm-Restart` is called exactly twice, never at top level, each within 4 lines of the restart, and that `Disarm-Restart` runs at start and in `finally`; braces and here-strings balance; the 5.1 parse runs in `windows.yml` on the next push. Owed: `schtasks /Query /TN IgneumRelayAgent` on PC 1 before the new agent starts (expected: the task exists) and after (expected: nothing); PC 1 is not touched tonight.
|
||||
|
||||
### X26. The feed is a permanent transcript, and it holds the dl token by design
|
||||
"One secret pages the whole history: every task body and every result transcript, usernames, hostnames, the folder that holds the secrets. And `tools/relay.mjs` writes the tokened download URL into task bodies before posting, so a relay leak is a dl-token leak."
|
||||
|
||||
Status: Open (4 October 2026). Sweep (5 October 2026): unchanged in `relay/api/relay.mjs` (no retention or cap on `feed`). Relay owner.
|
||||
Status: Fixed on a branch, pending merge (5 October 2026, night): ledger-relay 28c028b, tests 47 of 47 (relay, 6 suites) + 16 of 16 (packaging/windows/test-inputs-signing.sh) + 2 CI class checks. Was: Open (4 October 2026). Sweep (5 October 2026): unchanged in `relay/api/relay.mjs` (no retention or cap on `feed`). Relay owner.
|
||||
|
||||
Answer: Correct. `ITEM_COLS` (`relay/lib/relay.mjs:92`) includes `body`; `feed` returns up to 500 per call with no retention and no cap; `delete` leaves blobs. `tools/relay.mjs:76-80` substitutes `__DL_BASE__` with the tokened base and `playbooks/miner-v4.ps1:12` and `prover-setup.ps1:12` print it into the transcript. Today's 12 items hold 0 copies (count-only). Fix: retention (30 days), a cap on `feed`, the dl base passed as an environment value the agent holds rather than text in the body, blob deletion with the row. Review id R4.4.5.
|
||||
|
||||
Evidence: the files above. Experiment: `feed?limit=500&before=<id>` after the change returns nothing older than the retention.
|
||||
|
||||
Fix (5 October 2026, night): retention 30 days (`relay/lib/handler.mjs` `expire`: `DELETE ... WHERE ts < now - 30 d RETURNING file_url`, blobs deleted with the rows through `@vercel/blob` `del`, run on a feed read at most every 10 minutes per instance); `feed` capped at 100 a call (50 by default; `FEED_LIMIT_MAX`); `delete` removes the blob with the row. The downloads base is no longer text in any body: `tools/relay.mjs run` posts the playbook as written and refuses a body that says `__DL_BASE__` or carries the dl token; `make-clients.sh` bakes the base into the agent, which hands it to every task as `RELAY_DL_BASE` (`$env:RELAY_DL_BASE` in the five playbooks); the two print lines name the zip, not the URL. Tests: `handler.test.mjs` (120 rows, a `limit=500` read returns 100 with `limit: 100` in the reply; a row dated August goes on the next sweep with its blob, a row dated 1 October stays; `delete` reports `blobs: 1`), `guard.test.mjs` (`feedLimit`, `retentionCutoff`), `clients.test.mjs` (no playbook carries `__DL_BASE__` or prints the URL; the agents export the base). Owed: `feed?limit=500&before=<id>` against the deployed relay after the first sweep.
|
||||
|
||||
### X27. The relay has no clean rotation and no sender binding
|
||||
"Rotate the token and the key path stays open; rotate the key and every shipped package stops uploading logs. Any holder posts a `result` from any machine name, or registers a machine, and the Mac's watch prints it as truth."
|
||||
|
||||
Status: Open (4 October 2026). Sweep (5 October 2026): unchanged (`from` is a free field in `relay/api/relay.mjs`; nothing binds a `result` to the caller's machine). Relay owner.
|
||||
Status: Fixed on a branch, pending merge (5 October 2026, night): ledger-relay 28c028b, tests 47 of 47 (relay, 6 suites) + 16 of 16 (packaging/windows/test-inputs-signing.sh) + 2 CI class checks. Was: Open (4 October 2026). Sweep (5 October 2026): unchanged (`from` is a free field in `relay/api/relay.mjs`; nothing binds a `result` to the caller's machine). Relay owner.
|
||||
|
||||
Answer: Correct. `authed()` has two independent secrets with equal power; `insertItem` takes `from` as free text (`relay/api/relay.mjs:30`); `register` creates rows for any hostname (`:148-162`). Fix: the relay's own key (X23), `from` bound to the registered machine for `result` and `register` by a per-machine secret, and a documented rotation (token, relay key, intake key) with what each breaks. Review ids R4.4.6, R4.4.7.
|
||||
|
||||
Evidence: the files above. Experiment: a `result` posted with a `from` that does not match the caller's machine secret is refused.
|
||||
|
||||
Fix (5 October 2026, night): a per-machine secret (64 hex, `node tools/relay.mjs secret PC1`: written to `~/.config/igneum/relay-machines/PC1` at 0600, its sha256 bound on the relay with `POST secret` (token only), carried to the PC as `machine-secret.txt` by `make-clients.sh --machine PC1`). `register` with `x-machine-secret` names the machine whatever the hostname says (both PCs report DESKTOP-KMCV30N), drops `info.user` and `info.dir`, and a bound hostname without the secret is 403; a `result` with the secret is stored under the machine it proves and a `from` that does not match is 403; a result without the secret from a bound machine is 403; from a machine with no secret yet it is accepted with `flags.unbound` (the compatibility window, visible in the feed). `done` records `done_by`. The rotation of every secret (token, relay key, intake key, run key, machine secret: how, what stops, in what order) is the table "Rotation" in `relay/README.md`. Tests: `handler.test.mjs` (the forged `from` is refused; the matching one stored; unknown secret 403; bound hostname 403; the compatibility case marked unbound; `done` with a wrong secret 403), `guard.test.mjs` (`machineForSecret`). Owed: one `secret` per PC, the zips by hand, and `ADD COLUMN IF NOT EXISTS secret_hash` runs on the first `secret` or `feed` call after the deploy.
|
||||
|
||||
### X28. Relay hygiene, minor
|
||||
"`===` on secrets, no HSTS, a GET that acks, a reboot on any output containing `RELAY-REBOOT`, orphaned blobs, no rate limit anywhere, a WSL user `igneum`/`igneum` with NOPASSWD sudo, the username and secret folder posted on register, and a file in `~/.config/igneum` whose name is a token."
|
||||
|
||||
Status: Open, minor (4 October 2026); two of the points fixed: secrets compared in constant time (`relay/lib/auth.mjs`, `sameSecret`, unit test in CI) and HSTS on the relay (`relay/vercel.json`). Sweep (5 October 2026): the remaining points unchanged; relay owner.
|
||||
Status: Fixed on a branch, pending merge (5 October 2026, night): ledger-relay 28c028b, tests 47 of 47 (relay, 6 suites) + 16 of 16 (packaging/windows/test-inputs-signing.sh) + 2 CI class checks. The stray file is gone. Was: Open, minor (4 October 2026); two of the points fixed: secrets compared in constant time (`relay/lib/auth.mjs`, `sameSecret`, unit test in CI) and HSTS on the relay (`relay/vercel.json`). Sweep (5 October 2026): the remaining points unchanged; relay owner.
|
||||
|
||||
Answer: Correct on each point: `relay/lib/relay.mjs:38,40`; `relay/vercel.json:8-16`; `relay/api/relay.mjs:85`; `igneum-agent.ps1:133`; `:145`; no limiter in either function; `relay/playbooks/wsl-setup.ps1:39-40` and `prover-setup.ps1:21`; `igneum-agent.ps1:41, :113`; the stray file next to `desec-token` (3 Oct 19:33). Fix when convenient; the stray file today. Review ids R4.4.9 to R4.4.13.
|
||||
|
||||
Evidence: the files above.
|
||||
|
||||
Fix (5 October 2026, night): the remaining points. The GET that acks: `GET inbox` marks nothing (`ack=1` is ignored); `POST inbox {machine, kind, ack:true}` returns the list and marks it, and every client uses the POST. The reboot trigger: the agent restarts only when `RELAY-REBOOT` stands on a line of its own (`(?m)^RELAY-REBOOT\r?$`; `wantsReboot` in `guard.mjs`) AND the task was queued with `--reboot` or `--reboot-continue` (`flags.reboot`, part of the signed text); a marker inside other output, or in a task without the flag, is logged and refused. The rate limit: 120 calls a minute per IP and 10 failed authentications a minute per IP, 429 with `Retry-After` (`RateLimit` from `lib/wake.mjs`). The register payload: no username and no folder from either agent, and the relay strips `info.user` and `info.dir` whatever a client sends. The NOPASSWD line: `wsl-setup.ps1` writes `igneum ALL=(root) NOPASSWD:SETENV: /usr/bin/apt-get, /usr/bin/dpkg` (what `setup-wsl.sh` runs under sudo: lines 20, 21, 27, 28, 31) and checks it with `visudo -cf`; `prover-setup.ps1` no longer echoes the password into `sudo -S` and tests `sudo -n apt-get --version` instead. The `igneum`/`igneum` password itself stays (the user exists to be unattended). The stray file: `ls -la ~/.config/igneum` tonight (read-only) lists no file whose name is a token; the 28-character file beside `desec-token` is gone, so nothing is left for the project lead to delete there. Tests: `handler.test.mjs` (GET inbox with `ack=1` leaves `read` false, POST acks; 429 after the limit, a second IP unaffected, failed auths on their own counter; registration stripped), `guard.test.mjs` (`wantsReboot`), `clients.test.mjs` (the agents' marker regex and flag gate, no GET ack in any client, the sudoers line, no `sudo -S`). Review ids R4.4.9 to R4.4.13 closed; the WSL user's password is the one point kept by design.
|
||||
|
||||
### G12. The PoW schedule comes from the environment on every network, including mainnet
|
||||
"Your mainnet gate refuses the override file. It does not refuse `IGNEUM_POW_EPOCH_BLOCKS`. A node without a file installs the schedule from the environment and `Params.pow_epoch_blocks` is never consulted."
|
||||
|
||||
|
|
@ -1774,21 +1786,25 @@ Evidence: the files above. Experiment: start a node with `IGNEUM_POW_EPOCH_BLOCK
|
|||
### G13. The update signature covers binaries that nobody signed
|
||||
"The runner fetches `payload-inputs.zip` and its sha256 from the same host, builds the installer, and the Mac signs the manifest over whatever the latest green run produced. A compromised dl project or runner ships as a genuine update."
|
||||
|
||||
Status: Open (4 October 2026).
|
||||
Status: Fixed on a branch and verified locally (5 October 2026, night): ledger-relay 28c028b, tests 47 of 47 (relay, 6 suites) + 16 of 16 (packaging/windows/test-inputs-signing.sh) + 2 CI class checks. The GitHub run is owed (no push tonight). Was: Open (4 October 2026).
|
||||
|
||||
Answer: Correct. `.github/workflows/windows.yml` (step "payload inputs") checks the zip against a sha256 served beside it; `packaging/windows/fetch-ci-artifacts.sh` takes the latest green run and calls `packaging/ota/publish-manifest.sh` by default; the node fork is not in the repository the runner builds, so spec 08 item 4 has nothing to reproduce from. Fix: a detached Ed25519 signature over `payload-inputs.zip` made on the Mac and verified in CI before the build; `OTA_SKIP=1` by default with the signing step naming the run id it signs. Review id R4.5.2.
|
||||
|
||||
Evidence: the files above. Experiment: alter one byte of a hosted `payload-inputs.zip` on a test folder and run the workflow; it must fail before the build.
|
||||
|
||||
Fix (5 October 2026, night): the chain already on master was read end to end and run, not asserted. `packaging/windows/push-inputs.sh` writes `payload-inputs.json` (the zip's sha256 and size, every file's, the node fork commit and branch, the repository commit) and signs it on the Mac with the OTA key (`igneum-ota-sign sign-inputs`); `.github/workflows/windows.yml` step "payload inputs" verifies the signature with the key compiled into the app, the zip's hash, every unpacked file and the pinned node commit (`packaging/windows/node-source.pin`) before anything is built from the inputs (the engine step runs first only because it builds the verifier, from git); `packaging/windows/fetch-ci-artifacts.sh` leaves the manifest alone by default (`OTA_SKIP=1` is the default; `--sign-manifest` needs an explicit run id, re-downloads that run's `igneum-windows-inputs` artifact, re-verifies the signature and the pin at the run's commit on the Mac, checks the runner's record names that run, that commit and that key, then signs). The local test the ledger asked for: `IGNEUM_OTA_SIGN=<main checkout>/app/igneum-app/target/release/igneum-ota-sign packaging/windows/test-inputs-signing.sh`, tonight 16 passed, 0 failed, including "one byte appended to the zip: refused (sha256 ...)", a changed manifest byte, a changed unpacked file, an unlisted file, a missing file, a wrong and a short pin, another key, the embedded key against a throwaway signature, and the real OTA key verifying against `embedded`. Owed: the workflow run itself on GitHub (nothing is pushed tonight); the experiment on a hosted test folder stays as written.
|
||||
|
||||
### G14. Secrets and identity in the history of a repository with a public date
|
||||
"The intake key is in six files across eight commits, the dl token in one, the review and ledger files are tracked, 51 tracked files carry the founder's first name, and every commit today is stamped with the local-time offset. The 3 October sweep said zero hits."
|
||||
|
||||
Status: Open (4 October 2026); extends `docs/fud-fixes.md` section 5.
|
||||
Status: Fixed on a branch, pending merge (5 October 2026, night), the tooling part: ledger-relay 28c028b, tests 47 of 47 (relay, 6 suites) + 16 of 16 (packaging/windows/test-inputs-signing.sh) + 2 CI class checks. Decision owner: the project lead for the rewrite date (`docs/plans/ledger-decisions.md`). Was: Open (4 October 2026); extends `docs/fud-fixes.md` section 5.
|
||||
|
||||
Answer: Correct, count-only. The key: `packaging/mac/packaged-config.sh`, `infra/gpu-bench/upload.sh`, `proving/windows-wsl2/prove-block.sh`, `prove-shard.sh`, `proto-cuda/windows-miner/upload-log.bat`, `proto-cuda/windows-app/upload-log.bat`, commits `78df757` to `4c9810f`. The token: `docs/plans/morning-2026-10-04.md:49`, commit `c47ff03`. `git check-ignore` returns nothing for the ledger, fixes and review files. The CI identity grep covers the public export list, by design. Fix: both secrets join section 5 step 4's rewrite list (and are rotated regardless); `TZ=UTC` in the commit path now. Review ids R4.4.8, R4.5.3, R4.5.4.
|
||||
|
||||
Evidence: `git ls-files | xargs grep -lF <value>` counts, `git log -S`. Experiment: section 5 step 7 re-run after the rewrite returns nothing.
|
||||
|
||||
Fix (5 October 2026, night): `TZ=UTC` in every commit path the tooling owns: `tools/ship-app.mjs` (`git()` runs with `env: { TZ: 'UTC' }`), `packaging/ota/publish-jobs.sh` (`export TZ=UTC`, found by the class check), `tools/repo/fresh-repo.sh` (already). The class check `tools/ci/commit-tz-check.sh` (self-test first) fails CI on any script under `tools/`, `packaging/`, `infra/` or `.github/` that invokes `git commit` without `TZ=UTC`, and prints the count of commits on the branch with a non-UTC offset: 417 tonight, 0 after the rewrite. The two secrets on the rewrite list: `docs/plans/history-rewrite.md` section 2 already names the intake key and the dl token in `replace.txt`; added tonight: after the 5 October rotation the values IN THE HISTORY are the ones in `~/.config/igneum/log-intake-key.old-2026-10-05` and `dl-token.old-2026-10-05`, so `replace.txt` must be written from the `.old` files, not the live ones; the relay key, token, run key and machine secrets are in 0 files and 0 commits. The commit of this branch carries `+0000`. The rewrite itself (and its day) is the project lead's decision.
|
||||
|
||||
### X18. Two nodes with two override files connect, and only some mismatches fork
|
||||
"Your handshake compares the network name and nothing else. A PoW or difficulty mismatch forks and bans; a `finality` mismatch is a WARN; `rollout-v2.sh` throws the finality block away when it writes the file; the app rewrites the packaged file on every start."
|
||||
|
||||
|
|
@ -1992,12 +2008,14 @@ Evidence: `docs/design/miner-dev-fee.md`; the unit tests in `igneum/miner/src/ma
|
|||
### X29. Host and file hygiene, minor
|
||||
"The Mac's live node binds its gRPC to every interface. Four secrets or pointers in `~/.config/igneum` are world-readable, one token is a filename, and the intake key rides on `curl`'s command line. The manifest answers CORS `*` and the clock source is a cacheable page's Date header."
|
||||
|
||||
Status: Open, minor (4 October 2026). Sweep (5 October 2026): read-only checks on this Mac: every secret under `~/.config/igneum` is now mode 600 (only `ota-signing-key.pub` is world-readable, as it should be), so that half is fixed; the live node still listens on every interface (`lsof`: `igneumd` on `*:26610`). The `curl` command line and the clock source were not re-checked.
|
||||
Status: Fixed on a branch, pending merge (5 October 2026, night), the curl part: ledger-relay 28c028b, tests 47 of 47 (relay, 6 suites) + 16 of 16 (packaging/windows/test-inputs-signing.sh) + 2 CI class checks. Open: the live node's `--rpclisten` (decision, operator, group E) and the app's clock source (round 2). Was: Open, minor (4 October 2026). Sweep (5 October 2026): read-only checks on this Mac: every secret under `~/.config/igneum` is now mode 600 (only `ota-signing-key.pub` is world-readable, as it should be), so that half is fixed; the live node still listens on every interface (`lsof`: `igneumd` on `*:26610`). The `curl` command line and the clock source were not re-checked.
|
||||
|
||||
Answer: Correct. `--rpclisten=0.0.0.0:26610` on pid 33114 (no `--unsafe-rpc`, `--disable-upnp`); `ls -la ~/.config/igneum`; `app/igneum-app/src/update.rs` (`https_time`, `upload_log`); the dl host's headers. Vercel rewrote `Date` to now on a cache hit today, so the cached-Date failure did not show. Fix: RPC on loopback with PC 2 on a tunnel or its own node; `chmod 600`; the stray file removed; the key passed to `curl` through `-K` or a header file; an uncacheable path for the clock source. Review ids R4.5.5 to R4.5.7.
|
||||
|
||||
Evidence: `lsof`, `ls`, `curl -I`.
|
||||
|
||||
Fix (5 October 2026, night), the curl part: `infra/gpu-bench/upload.sh` writes `header = "x-igneum-key: ..."` to a 0600 temporary config and calls `curl -K`; `proto-cuda/windows-app/upload-log.bat` and `proto-cuda/windows-miner/upload-log.bat` do the same with a `%TEMP%` config deleted with the body; `relay/clients/agent.sh` and `send.sh` keep every secret header in a 0600 `headers.cfg` and use `-K`; `prove-block.sh` and `prove-shard.sh` already send the key from inside python's `urllib` (no command line). The class check: `tools/ci/curl-header-check.sh` (self-test first) fails CI on any `.sh`, `.bat`, `.cmd` or `.ps1` line that passes `x-igneum-key`, `x-relay-token` or `x-machine-secret` as a curl `-H` argument; tonight's tree: 0 hits. Not in this branch: the app's own `curl` calls still put the key on the command line (`app/igneum-app/src/update.rs:91`, `jobrun.rs` `relay_upload`); that is app code, owed to the app's next cut, named here so it is not lost. The mode half was fixed on 5 October (every secret 600); the `--rpclisten` half is the project lead's; the clock source is a round 2 candidate.
|
||||
|
||||
### X30. The live page and the bench page exposed operational detail
|
||||
"The engineering log page rendered the bench log with private strings; `/api/live` returned peer addresses, full key hashes and payout addresses; the mobile menu did not open."
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue