diff --git a/docs/plans/rotation-phase-2.md b/docs/plans/rotation-phase-2.md index 123ede9ed..0c1c317e0 100644 --- a/docs/plans/rotation-phase-2.md +++ b/docs/plans/rotation-phase-2.md @@ -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/ | 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//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. `` is the handle: letters, digits, hyphens, no name. + +1. (1 min) Pick `` 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 ``. The + noreply id 337424239 stays, so the commit address becomes `337424239+@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-repo igneum-network/igneum-core --work ~/igneum-rewrite + ``` + Add `--public-claude-md ` 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 (``), 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 `` 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 && git config user.email 337424239+@users.noreply.github.com + gh auth logout --user igneum-labs && gh auth login --web --hostname github.com # as + git config credential.https://github.com.helper '' && git config --add credential.https://github.com.helper '!f() { echo username=; echo "password=$(gh auth token --user 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- `; 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-` and `dl-token.old-` 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 | diff --git a/tools/logs.mjs b/tools/logs.mjs index 919af6d60..608b25455 100755 --- a/tools/logs.mjs +++ b/tools/logs.mjs @@ -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- and dl-token.old- + 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-, win-); 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); } diff --git a/tools/repo/fresh-repo.sh b/tools/repo/fresh-repo.sh index 4edfd4458..67b741739 100755 --- a/tools/repo/fresh-repo.sh +++ b/tools/repo/fresh-repo.sh @@ -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-, dl-token.old-: 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" diff --git a/tools/ship-app.mjs b/tools/ship-app.mjs index b47e997b4..895715d14 100644 --- a/tools/ship-app.mjs +++ b/tools/ship-app.mjs @@ -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';