rotation phase 2, executed: old folder stripped to the 0.3.5 manifest, local files renamed, relay on the new key, DL_TOKEN rotated; 7 October swap and the owner's rewrite checklist

logs.mjs --rotation reads the old fingerprints from the dated .old-* files once the .next files are gone; fresh-repo.sh scrubs the dated secret files too; ship-app.mjs mirrors payload-inputs.json.sig.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-05 15:37:02 +00:00
parent 442a1ccd0f
commit 2fa4cbccb1
4 changed files with 308 additions and 6 deletions

View file

@ -7,6 +7,8 @@ installers of the day. Phase 2 is this file: the 0.3.6 build carries the new val
across while the old folder still serves, then the old folder and the old key die, and only then the history is
rewritten into a fresh repository (owner's decision, 5 October 2026; `docs/plans/history-rewrite.md`, option B).
REMINDER, 7 October 2026: once `node tools/logs.mjs --rotation` shows Sam's Mac 3a9bf309 on 0.3.8 (moved), run section 8f: the old intake key off the site, the relay and GitHub, the old folder off the downloads host; the old files wait in `~/igneum-dl-old-20261005` until 12 October.
No value is written here. Each is named by its fingerprint, the first 8 hex of sha256 over the trimmed value
(`tr -d '[:space:]' < ~/.config/igneum/<file> | shasum -a 256 | cut -c1-8`; the app logs the same 8 characters):
@ -271,3 +273,294 @@ removed after the run; nothing left the Mac.
two for a synced fleet; a machine that is off waits for its owner).
5. Section 5: the deletion, in order.
6. The owner's two GitHub settings (login rename, one owner); then section 6, the fresh repository, from a frozen tree.
## 8. Execution, 5 October 2026 (owner: "3 execute"; from 15:25 UTC, branch `rotation-3`)
The order the owner set differs from section 5 in one place: the old intake key stays until Sam's Mac is on 0.3.8,
because that machine uploads with the old key until it updates. So steps 1, 2, 4 and 5 of section 5 ran today in the
owner's order (folder, local files, relay, GitHub), and the intake swap (section 5 step 3, plus the same swap on the
relay) is written out in 8f for 7 October.
### 8a. State at the start (`node tools/logs.mjs --rotation`, 15:25 UTC)
| Machine | Version | Key | Folder | Last upload (UTC) | State |
|---|---|---|---|---|---|
| mac-d937c69d (this Mac) | 0.3.8 | 477bb0ef | ed9c4d2e | 15:24:45 | moved |
| win-1ccfe586 (PC 2) | 0.3.8 | 477bb0ef | ed9c4d2e | 15:24:29 | moved |
| win-ae432dc7 (PC 1) | 0.3.8 | 477bb0ef | ed9c4d2e | 15:25:10 | moved |
| win-37ba0461 | 0.3.7 | 477bb0ef | ed9c4d2e | 13:52:20 | moved |
| mac-3a9bf309 (Sam's Mac, asleep) | 0.3.5 | - | - | 09:43:45 | unknown: reads the OLD folder and uploads with the OLD key |
| mac-MacBook-Pro | ? | - | - | 4 Oct 01:46:13 | unknown: a pre-header upload, stopped for good; accounted for |
Only Sam's Mac is behind. Its 0.3.5 checks the OLD manifest once an hour when its node is synced, downloads the file
named inside that manifest, and the new bundle carries the NEW manifest URL and the NEW key (section 2). Its job
runner polls `dl/<old>/igneum-jobs.json` every 10 minutes; a missing file is a quiet error in 0.3.5
(`jobrun.rs:338`, "no jobs file"), so the strip below costs it nothing.
### 8b. The OLD folder, stripped to what 0.3.5 needs (section 5 step 1, narrowed)
Kept, 4 files: `igneum-app-latest.json` (861 bytes, version 0.3.8, published 13:33:20Z, both platform URLs inside it
name the OLD folder), `igneum-app-latest.json.sig`, `Igneum-Miner-0.3.8.dmg` (41,247,419) and
`Igneum-Miner-Setup-0.3.8.exe` (19,794,320).
Before the strip, one copy into the NEW folder: `payload-inputs.json.sig` (129 bytes, byte-identical). The NEW folder
had never received it: `tools/ship-app.mjs` FOLDER_FILES carried the zip, the json and the sha256 but not the
signature, and `.github/workflows/windows.yml` fetches all four from the `DL_TOKEN` folder, which is the NEW folder
from 8e on. Without the copy the next Windows build would have failed at the inputs step. The list is fixed on this
branch (8h).
Removed: 56 files, 947,635,881 bytes, moved (not deleted) to `~/igneum-dl-old-20261005` (mode 0700), to be removed
on 12 October (8f step 6):
| File | Bytes | Modified (BST) | sha256 (first 12) |
|---|---|---|---|
| `Igneum-Miner-0.1.0.dmg` | 16994336 | 2026-10-03T23:28 | 69904f6191e0 |
| `Igneum-Miner-0.2.0.dmg` | 19924804 | 2026-10-04T08:57 | 2cb1c1d657bc |
| `Igneum-Miner-0.3.0.dmg` | 20320453 | 2026-10-04T12:46 | 670d26b49904 |
| `Igneum-Miner-0.3.1.dmg` | 20320855 | 2026-10-04T14:27 | 4b29d91cdd05 |
| `Igneum-Miner-0.3.2.dmg` | 20519491 | 2026-10-04T15:17 | 26fdc1e854ea |
| `Igneum-Miner-0.3.3.dmg` | 39775332 | 2026-10-04T17:56 | 3177bced3698 |
| `Igneum-Miner-0.3.4.dmg` | 39798913 | 2026-10-04T21:20 | 1b346811b807 |
| `Igneum-Miner-0.3.5.dmg` | 40027384 | 2026-10-05T07:39 | 2dad2b2615a6 |
| `Igneum-Miner-0.3.6.dmg` | 40156607 | 2026-10-05T10:34 | fddf3580353d |
| `Igneum-Miner-0.3.7.dmg` | 40156739 | 2026-10-05T11:16 | 8ee96b3b054f |
| `Igneum-Miner-Setup-0.3.0.exe` | 44005195 | 2026-10-04T12:46 | 42b3f167ba25 |
| `Igneum-Miner-Setup-0.3.1.exe` | 44013598 | 2026-10-04T14:32 | abc5b6226582 |
| `Igneum-Miner-Setup-0.3.2.exe` | 44138229 | 2026-10-04T15:17 | f5e0763ff126 |
| `Igneum-Miner-Setup-0.3.3.exe` | 44275245 | 2026-10-04T16:10 | 739f7e8af968 |
| `Igneum-Miner-Setup-0.3.4.exe` | 44304304 | 2026-10-04T21:14 | 756ee2c0af7f |
| `Igneum-Miner-Setup-0.3.5.exe` | 44447899 | 2026-10-05T07:38 | 0184abecaf99 |
| `Igneum-Miner-Setup-0.3.6.exe` | 19536899 | 2026-10-05T10:34 | ca0fb9197bee |
| `Igneum-Miner-Setup-0.3.7.exe` | 19784297 | 2026-10-05T11:16 | 2bacba64628b |
| `Igneum-Wallet-0.1.1.dmg` | 19565360 | 2026-10-05T09:24 | 02446a480dae |
| `Igneum-Wallet-0.1.2.dmg` | 19582462 | 2026-10-05T10:13 | 4ddfd7a07ac1 |
| `Igneum-Wallet-0.1.3.dmg` | 19598469 | 2026-10-05T14:21 | d5b7945e4fe8 |
| `build-inputs-t035.json` | 705 | 2026-10-05T10:17 | da48d4fbb7c4 |
| `build-inputs-t035.sha256` | 65 | 2026-10-05T10:17 | caa267821f17 |
| `build-inputs-t035.zip` | 7223133 | 2026-10-05T10:17 | d1dd841d9872 |
| `build-inputs-t036.json` | 701 | 2026-10-05T10:17 | a0cdfe1a83c8 |
| `build-inputs-t036.sha256` | 65 | 2026-10-05T10:17 | 37794f58f636 |
| `build-inputs-t036.zip` | 7244256 | 2026-10-05T10:17 | c01a7525a903 |
| `build-inputs-tests.json` | 1149 | 2026-10-05T10:04 | 0aa92c6a10f8 |
| `build-inputs-tests.sha256` | 65 | 2026-10-05T10:04 | 216760a99a61 |
| `build-inputs-tests.zip` | 8206608 | 2026-10-05T10:04 | f60713b133ec |
| `build-inputs.json` | 1046 | 2026-10-05T10:12 | ed3e06ef5fdf |
| `build-inputs.sha256` | 65 | 2026-10-05T10:12 | 0c0c2a0c186c |
| `build-inputs.zip` | 8206958 | 2026-10-05T10:12 | 7c63df808331 |
| `igneum-devnet-mac.dmg` | 17750579 | 2026-10-03T22:55 | c5b49d3c0097 |
| `igneum-jobs.json` | 64416 | 2026-10-05T15:04 | 6812e147601f |
| `igneum-jobs.json.sig` | 129 | 2026-10-05T15:04 | 3c3fbbeb258b |
| `igneum-mine-test-v2.zip` | 4442068 | 2026-10-03T22:52 | 16abd0ff2a42 |
| `igneum-mine-test-v3.zip` | 4442367 | 2026-10-03T22:52 | 646d6d4b659c |
| `igneum-mine-test.zip` | 4431923 | 2026-10-03T22:52 | ab1951f1450d |
| `igneum-node-windows-v4.zip` | 32973919 | 2026-10-04T08:57 | 8fbdc646596e |
| `igneum-node-windows.zip` | 29454819 | 2026-10-03T22:52 | beadad43d1f0 |
| `igneum-prove-wsl2-038.zip` | 1425452 | 2026-10-05T14:28 | 98c246146e89 |
| `igneum-prove-wsl2.zip` | 295176 | 2026-10-05T11:48 | 75e3a96fed84 |
| `igneum-wallet-latest.json` | 473 | 2026-10-05T14:21 | dc9bf8ac2bf5 |
| `igneum-wallet-latest.json.sig` | 129 | 2026-10-05T14:21 | 1568dbdc33d0 |
| `igneum-windows-app.zip` | 27591281 | 2026-10-05T14:33 | 8f08a3040ac7 |
| `igneum-windows-ci.json` | 199 | 2026-10-05T14:33 | f8e099c8c2c3 |
| `igneum-windows-v4.zip` | 64286803 | 2026-10-04T12:05 | a936c0f80715 |
| `igneum-windows.zip` | 22885438 | 2026-10-03T22:52 | 862d61098c4b |
| `igneum-worker-cuda.exe` | 1121792 | 2026-10-04T11:12 | 3f3f8337d53b |
| `mingw-runtime-gcc13.zip` | 8338215 | 2026-10-05T10:52 | 7f030f253571 |
| `mingw-runtime-x64.zip` | 9327832 | 2026-10-05T10:45 | b6671e811559 |
| `payload-inputs.json` | 995 | 2026-10-05T14:25 | 403d0108b1b8 |
| `payload-inputs.json.sig` | 129 | 2026-10-05T14:25 | a667d898e29e |
| `payload-inputs.sha256` | 65 | 2026-10-05T14:25 | e627981e8b09 |
| `payload-inputs.zip` | 26669995 | 2026-10-05T14:25 | b587ed19fac4 |
The deploy: one `vercel deploy --prod` of the downloads folder (`igneum-dl`), 15:29 UTC. Verified straight after:
| Check | Result |
|---|---|
| `packaging/ota/publish-manifest.sh --verify-only` (OLD folder, the default token at that moment) | version 0.3.8, byte-identical, signature OK, try 1 of 12 |
| the same with `--dest "$DLSITE/dl/$NEW" --base-url https://dl.igneum.network/dl/$NEW` | version 0.3.8, byte-identical, signature OK, try 1 of 12 |
| OLD folder, removed: `igneum-jobs.json`, `Igneum-Miner-0.3.7.dmg`, `build-inputs.zip`, `igneum-wallet-latest.json`, `payload-inputs.zip` | 404, 404, 404, 404, 404 |
| OLD folder, kept: the manifest, its signature, the 0.3.8 DMG, the 0.3.8 installer | 200, 200, 200, 200 |
| NEW folder: `payload-inputs.json.sig`, `igneum-jobs.json`, `igneum-jobs.json.sig`, `payload-inputs.zip` | 200, 200, 200, 200 |
Jobs whose `zip_url` names the OLD folder (the build jobs of the morning, `fetch-prove-038`, `rollback-035-pcs`) have
all run on their machines; a machine never re-runs a job it has finished, so nothing waits on those URLs. The wallet
manifest and DMGs left the OLD folder with everything else; the NEW folder carries `igneum-wallet-latest.json` and
`Igneum-Wallet-0.1.3.dmg` (mirrored 14:22 UTC). The wallet publisher is not on master (the wallet branch): if it reads
`dl-token` as every other publisher does, it writes the NEW folder from now on; confirm at the next wallet publish.
### 8c. The local files (section 5 step 2, as written)
```
mv ~/.config/igneum/log-intake-key ~/.config/igneum/log-intake-key.old-2026-10-05
mv ~/.config/igneum/log-intake-key.next ~/.config/igneum/log-intake-key
mv ~/.config/igneum/dl-token ~/.config/igneum/dl-token.old-2026-10-05
mv ~/.config/igneum/dl-token.next ~/.config/igneum/dl-token
```
| File | Mode | Fingerprint |
|---|---|---|
| `log-intake-key` | 0600 | 477bb0ef |
| `log-intake-key.old-2026-10-05` | 0600 | e2005de8 |
| `dl-token` | 0600 | ed9c4d2e |
| `dl-token.old-2026-10-05` | 0600 | df66a82c |
No `.next` file is left. Every script reads the NEW folder and the NEW key by default from here: `packaging/mac/packaged-config.sh --test`
all checks passed; `packaging/ota/publish-manifest.sh --verify-only` (now the NEW folder) 0.3.8, byte-identical,
signature OK; `publish-jobs.sh` has no next folder to mirror to, and the job another agent published at 15:32 UTC
landed in the NEW folder only, as intended. Two tools read the renamed files wrongly and are fixed on this branch (8h):
`tools/logs.mjs --rotation` printed the old fingerprints as 477bb0ef/ed9c4d2e (it took "old" from the plain files),
and `tools/repo/fresh-repo.sh` built its secret rules from the four plain names only, so the rewrite would have left
the OLD key and the OLD token in the history. The dated files stay until the fresh repository is pushed (8g step 10).
### 8d. The intake and the relay (section 5 step 3, split)
The site project `igneum` is untouched today: `LOG_INTAKE_KEY` (old) and `LOG_INTAKE_KEY_NEXT` (new) both stay, and
a POST to `/api/log` answered 200 with the new key and 200 with the old key (baseline for 8f; label
`rotation-check`). Sam's Mac keeps uploading with the old key until it has applied 0.3.8.
The relay project `igneum-relay` (`relay/lib/relay.mjs` `authed()` accepts `LOG_INTAKE_KEY` and `LOG_INTAKE_KEY_NEXT`
since this morning): `LOG_INTAKE_KEY_NEXT` added (production, piped from `~/.config/igneum/log-intake-key`), and
`DL_TOKEN` moved to the NEW token (`relay/api/console.mjs` reads the jobs file, the manifest and the CI record from
that folder, and the OLD folder no longer carries them). Deployed from the main checkout's `relay/` on master 23d11d5
with the command of `relay/README.md` (`vercel deploy --prod --yes --scope igneum`), 15:31 UTC.
| `POST https://relay.igneum.network/api/relay?fn=upload` with header `x-igneum-key` | Answer |
|---|---|
| the NEW key | `ok: true`, `api_version: 11`, 200 |
| the OLD key | `ok: true`, `api_version: 11`, 200 |
| a wrong key | 401 |
Relay env after: DL_TOKEN, LOG_INTAKE_KEY, LOG_INTAKE_KEY_NEXT, RELAY_KEY, RELAY_TOKEN, DATABASE_URL,
BLOB_READ_WRITE_TOKEN. `node tools/console.mjs jobs` lists the jobs through the NEW folder.
### 8e. The GitHub secrets (section 5 step 4, the token half)
`gh auth status`: igneum-labs active. `DL_TOKEN` set 15:32:05Z from `~/.config/igneum/dl-token` (the NEW token);
`DL_TOKEN_NEXT` deleted. `gh secret list`: DL_TOKEN (15:32:05Z), LOG_INTAKE_KEY (08:36:35Z, old), LOG_INTAKE_KEY_NEXT
(08:36:36Z, new). On the runner (`windows.yml`): the payload inputs and the manifest folder come from `DL_TOKEN`, the
NEW folder (which now carries `payload-inputs.json.sig`, 8b); `packaged-config.sh` packages the `.next` key, the new
one, while `LOG_INTAKE_KEY_NEXT` exists, and the plain `LOG_INTAKE_KEY` once 8f has run.
### 8f. Deferred to 7 October: the old key and the old folder, exact commands
Condition, checked first and never skipped: Sam's Mac reports 0.3.8 with the new fingerprints. If it is still asleep
on 7 October, wait; nothing below runs on a schedule.
```
cd /Users/joshm/Projects/igneum && git checkout master && git pull
node tools/logs.mjs --rotation | grep 3a9bf309 # must read: 0.3.8 477bb0ef ed9c4d2e ... moved
gh auth status # igneum-labs active
# 1. the site: LOG_INTAKE_KEY becomes the new value, LOG_INTAKE_KEY_NEXT goes, then a production deploy
cd site
npx --yes vercel@latest --global-config ~/.config/igneum/vercel env rm LOG_INTAKE_KEY production --scope igneum --yes
tr -d '[:space:]' < ~/.config/igneum/log-intake-key | npx --yes vercel@latest --global-config ~/.config/igneum/vercel env add LOG_INTAKE_KEY production --scope igneum
npx --yes vercel@latest --global-config ~/.config/igneum/vercel env rm LOG_INTAKE_KEY_NEXT production --scope igneum --yes
cd ..
git push origin master # the GitHub integration deploys; if master has nothing new: the hand deploy of packaging/README-ship.md
# the old key is dead (401), the new one lives (200)
curl -s -o /dev/null -w '%{http_code}\n' -X POST -H "x-igneum-key: $(tr -d '[:space:]' < ~/.config/igneum/log-intake-key.old-2026-10-05)" -H 'Content-Type: application/json' -d '{"label":"rotation-check","machine":"mac","run_id":"rotation-check","lines":"old key must be refused"}' https://igneum-six.vercel.app/api/log
curl -s -o /dev/null -w '%{http_code}\n' -X POST -H "x-igneum-key: $(tr -d '[:space:]' < ~/.config/igneum/log-intake-key)" -H 'Content-Type: application/json' -d '{"label":"rotation-check","machine":"mac","run_id":"rotation-check","lines":"new key accepted"}' https://igneum-six.vercel.app/api/log
# 2. the relay: the same swap, then its own deploy (relay/README.md)
cd relay
npx --yes vercel@latest --global-config ~/.config/igneum/vercel env rm LOG_INTAKE_KEY production --scope igneum --yes
tr -d '[:space:]' < ~/.config/igneum/log-intake-key | npx --yes vercel@latest --global-config ~/.config/igneum/vercel env add LOG_INTAKE_KEY production --scope igneum
npx --yes vercel@latest --global-config ~/.config/igneum/vercel env rm LOG_INTAKE_KEY_NEXT production --scope igneum --yes
npx --yes vercel@latest --global-config ~/.config/igneum/vercel deploy --prod --yes --scope igneum
cd ..
# old key 401, new key 200 (the answer carries a Blob client token: print the status only)
curl -s -o /dev/null -w '%{http_code}\n' -X POST -H "x-igneum-key: $(tr -d '[:space:]' < ~/.config/igneum/log-intake-key.old-2026-10-05)" -H 'Content-Type: application/json' -d '{"name":"rotation-check.txt","size":10}' 'https://relay.igneum.network/api/relay?fn=upload'
curl -s -o /dev/null -w '%{http_code}\n' -X POST -H "x-igneum-key: $(tr -d '[:space:]' < ~/.config/igneum/log-intake-key)" -H 'Content-Type: application/json' -d '{"name":"rotation-check.txt","size":10}' 'https://relay.igneum.network/api/relay?fn=upload'
# 3. GitHub: LOG_INTAKE_KEY carries the new value, LOG_INTAKE_KEY_NEXT goes
tr -d '[:space:]' < ~/.config/igneum/log-intake-key | gh secret set LOG_INTAKE_KEY --repo igneum-network/igneum
gh secret delete LOG_INTAKE_KEY_NEXT --repo igneum-network/igneum
gh secret list --repo igneum-network/igneum # DL_TOKEN, LOG_INTAKE_KEY
# 4. the OLD folder: its last 4 files join the holding folder, one deploy, 404
DLSITE="$(tr -d '[:space:]' < ~/.config/igneum/dlsite-dir)"; OLD="$(tr -d '[:space:]' < ~/.config/igneum/dl-token.old-2026-10-05)"
mkdir -p ~/igneum-dl-old-20261005/last-manifest && mv "$DLSITE/dl/$OLD"/* ~/igneum-dl-old-20261005/last-manifest/ && rmdir "$DLSITE/dl/$OLD"
ls "$DLSITE/dl" | wc -l # 1
(cd "$DLSITE" && npx --yes vercel@latest --global-config ~/.config/igneum/vercel deploy --prod --yes)
curl -s -o /dev/null -w '%{http_code}\n' "https://dl.igneum.network/dl/$OLD/igneum-app-latest.json" # 404
packaging/ota/publish-manifest.sh --verify-only # the NEW folder: live, byte-identical, signature OK
# 5. an hour later: every row moved (mac-MacBook-Pro stays unknown: stopped 4 October)
node tools/logs.mjs --rotation
# 6. on 12 October, and only after 8g step 10 has pushed the fresh repository (the rewrite reads the dated files)
rm -rf ~/igneum-dl-old-20261005
rm -P ~/.config/igneum/log-intake-key.old-2026-10-05 ~/.config/igneum/dl-token.old-2026-10-05
```
After step 3 the 0.2.0 launcher machines (`proto-cuda/windows-app`, `windows-miner`, the old key in `upload-log.bat`)
upload nothing, which is the intent of section 5.
### 8g. The owner's checklist: the login rename and the fresh repository (about twenty minutes)
Before: 8f done (the old values are dead values), `gh auth status` igneum-labs active, and
`~/.config/igneum/pytools/git_filter_repo.py` present (copied today from the dry run's download, version a40bce5;
if missing: `python3 -m pip install --target ~/.config/igneum/pytools git-filter-repo`). Browser work in the
"[user] (igneum.network)" Chrome profile. `<new>` is the handle: letters, digits, hyphens, no name.
1. (1 min) Pick `<new>` and the repository name. `igneum-core` is the script's default; the commands below use it.
2. (3 min) GitHub, signed in as igneum-labs: Settings > Account > Change username: `igneum-labs` to `<new>`. The
noreply id 337424239 stays, so the commit address becomes `337424239+<new>@users.noreply.github.com`. Then the
organisation igneum-network > People: [second-owner-login] leaves the owners (remove from the organisation). Check
the profile and the organisation: no name, no location, no personal avatar, 2FA on, the public members list empty.
Nothing on this Mac breaks: the token survives a rename and `gh auth token --user igneum-labs` reads it by the
stored name; the tidy-up is step 10.
3. (2 min) Freeze: every agent commits and pushes its branch and stops; no ship, no merge, no job publish that
commits. Then, from the main checkout:
`gh pr list --repo igneum-network/igneum` (must be empty) and
`git -C ~/Projects/igneum worktree list > ~/igneum-worktrees-$(date -u +%Y%m%d).txt` (42 worktrees today).
The freeze holds until step 10 is done.
4. (4 min) The pass, from the main checkout on master, nothing pushed by the script:
```
cd ~/Projects/igneum && git checkout master && git pull
IGNEUM_FILTER_REPO=~/.config/igneum/pytools/git_filter_repo.py tools/repo/fresh-repo.sh --new-login <new> --new-repo igneum-network/igneum-core --work ~/igneum-rewrite
```
Add `--public-claude-md <file>` when the scrubbed `CLAUDE.md` of `docs/fud-fixes.md` section 5 step 2 exists;
without it the private `CLAUDE.md` stays in every commit and the repository must stay private. Expected, against
the dry run of section 6: about 353 commits, 1 identity (`<new>`), 0 stamps off `+0000`, 0 commits touching the
four dropped files, 0 secret lines (the rules now carry 4 values: 2 current, 2 dated), 0 identity lines,
0 lines of the old login, then `clean.` and the printed push commands. On `NOT CLEAN`: stop, keep
`~/igneum-rewrite/report.txt`, ask.
5. (2 min) The push, the script's printed lines:
```
gh repo create igneum-network/igneum-core --private --description 'Igneum: the GPU-mined zkEVM L1' --disable-wiki
cd ~/igneum-rewrite/clone && git remote add origin https://github.com/igneum-network/igneum-core.git && git push --mirror origin
```
On GitHub: default branch master, the commit count of step 4, author `<new>` on the newest and the oldest commit.
6. (1 min) The two secrets on the new repository (two after 8f):
`tr -d '[:space:]' < ~/.config/igneum/dl-token | gh secret set DL_TOKEN --repo igneum-network/igneum-core`,
the same for `log-intake-key` into `LOG_INTAKE_KEY`, then `gh secret list --repo igneum-network/igneum-core`.
7. (3 min) Vercel, team igneum, project igneum > Settings > Git: disconnect `igneum-network/igneum`, connect
`igneum-network/igneum-core`, production branch master. Then one push to master from the new checkout (step 10)
triggers the first deploy; `curl -sI https://igneum.network | head -1` reads 200. The relay and the downloads
folder deploy by CLI and have no Git link: nothing to re-link.
8. (1 min) Archive `igneum-network/igneum` (Settings > Archive this repository). Private, never deleted the same day.
9. (3 min) Re-clone and re-identify:
```
cd ~/Projects && mv igneum igneum-old-history && git clone https://github.com/igneum-network/igneum-core.git igneum
cd igneum && git config user.name <new> && git config user.email 337424239+<new>@users.noreply.github.com
gh auth logout --user igneum-labs && gh auth login --web --hostname github.com # as <new>
git config credential.https://github.com.helper '' && git config --add credential.https://github.com.helper '!f() { echo username=<new>; echo "password=$(gh auth token --user <new> 2>/dev/null)"; }; f'
mv ../igneum-old-history/vendor ./vendor # gitignored: the fork worktrees and their targets
git commit --allow-empty -m "fresh repository: first push" && git push origin master # the deploy of step 7
```
The private `CLAUDE.md` names the login and the repository: update both lines in the same commit (or the scrubbed
file of step 4).
10. (0 min, each agent) Unfreeze: every agent removes its old worktree directory and re-creates it from the new
checkout, `git -C ~/Projects/igneum worktree add ../igneum-wt-<name> <branch>`; never a merge of an old-id branch
into a new one. `TZ=UTC` in every shell that commits. `~/igneum-old-history` and the dated secret files go on
12 October (8f step 6).
### 8h. What this branch changes (`rotation-3`)
| File | Change |
|---|---|
| `tools/logs.mjs` | `--rotation`: once no `.next` file exists, the "old" fingerprints come from the newest `log-intake-key.old-<date>` and `dl-token.old-<date>` instead of the plain files (which are the new values after the rename); the summary line says which. `--self-test` passes |
| `tools/repo/fresh-repo.sh` | the secret rules take every `log-intake-key.old-*` and `dl-token.old-*` file next to the four names, so the rewrite scrubs the old values after the rename; the count line no longer says "of 4" |
| `tools/ship-app.mjs` | `FOLDER_FILES` carries `payload-inputs.json.sig` (the mirror step had left the signature behind; `windows.yml` fetches it). `--self-test` passes |
| this file | section 8 |

View file

@ -10,7 +10,7 @@
// while any machine still reports with the old values
// node tools/logs.mjs --self-test the header parser on sample lines
// Reads DATABASE_URL from ~/.config/igneum/env. No dependencies: Neon HTTP SQL over fetch.
import { readFileSync, existsSync } from 'node:fs';
import { readFileSync, existsSync, readdirSync } from 'node:fs';
import { homedir } from 'node:os';
import { createHash } from 'node:crypto';
@ -80,7 +80,13 @@ const [runId, flag] = process.argv.slice(2);
if (runId === '--rotation') {
const cfg = `${homedir()}/.config/igneum`;
const want = { key: fingerprintFile(`${cfg}/log-intake-key.next`) || fingerprintFile(`${cfg}/log-intake-key`), folder: fingerprintFile(`${cfg}/dl-token.next`) || fingerprintFile(`${cfg}/dl-token`) };
const old = { key: fingerprintFile(`${cfg}/log-intake-key`), folder: fingerprintFile(`${cfg}/dl-token`) };
// the old values: the plain files while the .next files exist (phase 2 before the rename); after the rename
// (plan section 5 step 2) the newest dated copy, log-intake-key.old-<date> and dl-token.old-<date>
const dated = name => { try { return readdirSync(cfg).filter(f => f.startsWith(`${name}.old-`)).sort().reverse().map(f => `${cfg}/${f}`)[0] || ''; } catch { return ''; } };
const renamed = !existsSync(`${cfg}/log-intake-key.next`) && !existsSync(`${cfg}/dl-token.next`);
const old = renamed
? { key: fingerprintFile(dated('log-intake-key')), folder: fingerprintFile(dated('dl-token')) }
: { key: fingerprintFile(`${cfg}/log-intake-key`), folder: fingerprintFile(`${cfg}/dl-token`) };
// the latest upload per app label (mac-<id8>, win-<id8>); the header lines sit in the first bytes, the config line
// may repeat after an OTA restart, so the whole upload is parsed
const rows = await sql(`
@ -96,7 +102,7 @@ if (runId === '--rotation') {
return { label: r.label, machine: r.machine, version: p.version || '?', key: p.keyFp || '-', folder: p.folderFp || '-', sources: [p.keySource, p.manifestSource].filter(Boolean).join(' / ') || '-', last_received: new Date(r.received_at).toISOString().replace('T', ' ').slice(0, 19) + ' UTC', state: ok ? 'moved' : p.keyFp === old.key || p.folderFp === old.folder ? 'OLD' : 'unknown' };
}).sort((a, b) => a.state.localeCompare(b.state) || a.label.localeCompare(b.label));
console.table(table);
console.log(`expected: key ${want.key || '?'} folder ${want.folder || '?'} (from the .next files when they exist); old: key ${old.key || '?'} folder ${old.folder || '?'}`);
console.log(`expected: key ${want.key || '?'} folder ${want.folder || '?'} (from the .next files when they exist, else the plain files); old: key ${old.key || '?'} folder ${old.folder || '?'} (${renamed ? 'from the dated .old-* files' : 'from the plain files'})`);
console.log(`${moved} machine(s) on the new key and folder, ${stale} not yet; a machine silent for over a day is listed by its last upload`);
process.exit(stale ? 1 : 0);
}

View file

@ -93,12 +93,15 @@ LAST_NAMES="$(printf '%s\n' "$PERSONAL_NAMES" | awk 'NF>=2{print $NF}' | sort -u
SECOND_LOGINS="$(printf '%s\n' "$PERSONAL_EMAILS" | sed -nE 's/^[0-9]+\+([A-Za-z0-9-]+)@users\.noreply\.github\.com$/\1/p' | grep -v "^$STANDING_LOGIN$" | sort -u || true)"
# the other businesses named in the plan (brand names, not people)
OTHER_BUSINESSES='[other-business]|[other-business]|[other-business]|[other-business]|[other-business]'
# the secrets: whichever of the four files exist
# the secrets: whichever of the four files exist, plus every dated copy the rotation left behind
# (log-intake-key.old-<date>, dl-token.old-<date>: rotation phase 2 section 5 step 2 renames the .next files to the
# plain ones and keeps the old values dated, and those old values are the ones the history carries)
SECRET_FILES=(); for n in log-intake-key log-intake-key.next dl-token dl-token.next; do [ -f "$HOME/.config/igneum/$n" ] && SECRET_FILES+=("$HOME/.config/igneum/$n"); done
for f in "$HOME/.config/igneum"/log-intake-key.old-* "$HOME/.config/igneum"/dl-token.old-*; do [ -f "$f" ] && SECRET_FILES+=("$f"); done
say "standing login: $STANDING_LOGIN (noreply id $STANDING_ID)${NEW_LOGIN:+ -> $NEW_LOGIN}"
say "personal identities in the history: $(printf '%s\n' "$PERSONAL_PAIRS" | grep -c . || true) (names $(printf '%s\n' "$PERSONAL_NAMES" | grep -c . || true), addresses $(printf '%s\n' "$PERSONAL_EMAILS" | grep -c . || true), second owner logins $(printf '%s\n' "$SECOND_LOGINS" | grep -c . || true))"
say "secret files for the rules: ${#SECRET_FILES[@]} of 4"
say "secret files for the rules: ${#SECRET_FILES[@]} (the four names plus dated .old-* copies; 4 are needed once the rotation has renamed: 2 current, 2 old)"
# the rule files
REPLACE="$RULES/replace.txt"; MAILMAP="$RULES/mailmap"; IDENT="$RULES/identity.pl"; SECRETS="$RULES/secrets.pl"

View file

@ -196,7 +196,7 @@ const DEST_NEXT = BOTH && DLSITE && TOKEN_NEXT ? join(DLSITE, 'dl', TOKEN_NEXT)
const BASE_NEXT = `https://dl.igneum.network/dl/${TOKEN_NEXT}`;
// the folder-level files the apps and the CI read next to the manifest (jobs, the CI's inputs, the CI record, the
// WSL2 prover zip): mirrored into the NEXT folder when present in the current one
const FOLDER_FILES = ['igneum-jobs.json', 'igneum-jobs.json.sig', 'payload-inputs.zip', 'payload-inputs.json', 'payload-inputs.sha256', 'igneum-windows-ci.json', 'igneum-prove-wsl2.zip'];
const FOLDER_FILES = ['igneum-jobs.json', 'igneum-jobs.json.sig', 'payload-inputs.zip', 'payload-inputs.json', 'payload-inputs.json.sig', 'payload-inputs.sha256', 'igneum-windows-ci.json', 'igneum-prove-wsl2.zip'];
const DMG_NAME = `Igneum-Miner-${VERSION}.dmg`;
const SETUP_NAME = `Igneum-Miner-Setup-${VERSION}.exe`;
const ZIP_NAME = 'igneum-windows-app.zip';