The sweep (main's item 1): 199 tracked text files, 783 lines. The founder's full name, first name and possessive become "the founder" (sentence starts capitalised); the lowercase operating-system user name in WSL paths and commands becomes <user>; the second owner login becomes "the second owner login"; the three earlier businesses and the two other brands become "the other business", "the earlier entity", "the earlier business" and "another brand"; the Chrome profile rule names the igneum.network profile, not the profile's label. The standing commit login igneum-labs is not a founder term here: the fresh-repository step renames it in the history (docs/plans/history-rewrite.md, tools/repo/fresh-repo.sh). The patterns never appear in plain text in the tree (a plaintext list would be the hit): tools/ci/founder-strings.b64 (perl regex, tab, a sample per row) is read by tools/ci/founder-strings-check.sh (every tracked text file, perl, known-failed first: the self-test plants each row's sample in a fixture and the hit must name the file), by tools/community/discord-hooks.mjs (the guard's founder and business rows; the test takes its fixtures from the samples) and by tools/repo/fresh-repo.sh (the business names of the rewrite rules). site/forbidden-strings.txt carries the same patterns as b64: lines, decoded case-insensitive by site/scrub.mjs and tools/ci/launch-gates-check.mjs (whose fixture now plants an encoded made-up name). The check runs in the gate's tree checks on every merge. Not in this commit, by main's word: the 105 commit messages and 40 personal-identity commits that need the history rewrite (listed, not run), and the secrets found by gitleaks over the history (reported with owners). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
129 lines
22 KiB
Markdown
129 lines
22 KiB
Markdown
# Keys: inventory, backup, the signing-key plan (5 October 2026)
|
|
|
|
Internal. Every key the project depends on sat unencrypted in `~/.config/igneum` on one Mac with no backup. This file is
|
|
the inventory written from the real files and the scripts that read them, the backup and restore commands, the plan for
|
|
a second OTA signing key, and the emergency path if the one key leaks. No value is written here. The only fingerprint
|
|
quoted is the public key's. Owner of every rotation below: the founder, unless a row says otherwise.
|
|
|
|
Modes read on 5 October 2026 18:50 UTC: every secret file 0600; the folder itself was 0755 and is 0700 since this branch
|
|
(`vercel/` and `txgen/` too). `ota-signing-key.pub`, `build-slots` and `vercel/config.json` (team ids, no token) are 0644,
|
|
which is fine.
|
|
|
|
## 1. The inventory
|
|
|
|
Column "lost" = the Mac dies and there is no backup. Column "leaked" = someone else holds the value.
|
|
|
|
| Name | Where it lives | What it unlocks (readers) | If lost | If leaked | Rotate: who, how | Rotation status |
|
|
|---|---|---|---|---|---|---|
|
|
| `ota-signing-key` (Ed25519 seed, 32 bytes as hex, 65 B, 0600, made 4 Oct 12:32) | the Mac only. Never in CI, never in the repo. Its public half is compiled into every app (`app/igneum-app/src/manifest.rs:28`, `OTA_PUBLIC_KEY_HEX`, fingerprint sha256 `8f186e37b48cfddf52d2b37478c562d78a74236fc87f0a675393ed6a35baac4e`) and on the Mac as `ota-signing-key.pub` | signs the update manifest `igneum-app-latest.json` (`packaging/ota/publish-manifest.sh`, `publish-public.sh`, `tools/ship-app.mjs`), the remote jobs file `igneum-jobs.json` (`publish-jobs.sh`, `tools/jobs.mjs`; `kind: run` jobs execute on every app machine, `jobrun.rs:800`) and the Windows build inputs `payload-inputs.json` (`packaging/windows/push-inputs.sh`, verified in CI with the embedded key, `windows.yml:172`). Verified by `ota.rs:1030`, `jobrun.rs:800-811`, `igneum-ota-sign verify-inputs embedded` | no new manifest, job or Windows build can be signed. Every installed 0.3.x keeps checking hourly and never updates again. The only way forward is a hand reinstall by every miner of a build that embeds a new key: the fleet is stranded | whoever holds it signs a manifest or a jobs file the apps accept. With the manifest URL (the folder token is in every app) and a way to serve it (the Vercel token, or DNS) they run code on every machine | the founder only. No fleet rotation path exists today (section 4): `igneum-ota-sign keygen`, change `OTA_PUBLIC_KEY_HEX`, ship, and every machine must apply that build first | never rotated; one key; backup = this branch |
|
|
| `ota-signing-key.pub` (65 B, 0644) | the Mac; the same bytes in the app | the local check of every publish (`publish-manifest.sh`, `ship-app.mjs:563`, `test-publish-jobs.sh`) | nothing: `igneum-ota-sign embedded` prints it from any app build | nothing, it is public | with the private key | with the private key |
|
|
| `relay-token` (28 B, 0600, 4 Oct 19:23) | the Mac; `RELAY_TOKEN` on Vercel `igneum-relay`; baked into the relay clients on PC 1 and PC 2 (`relay/clients/make-clients.sh:8-12`, `igneum-agent.ps1`); the phone bookmark | the relay URL `/r/<token>/`: read and post the feed, the drop box, and `POST task kind: run` on the PCs with only this token (`docs/fud-ledger.md` X23/X24, still open). Readers: `tools/relay.mjs`, `console.mjs`, `build-job.mjs`, `ship-app.mjs`, `packaging/ota/publish-jobs.sh` | generate a new one, set the env, rebuild the clients, redistribute to the PCs and the phone; nothing is unrecoverable | elevated command execution on PC 1 and PC 2, and every file on the relay | the founder: new value into the file, `vercel env rm/add RELAY_TOKEN production --scope igneum`, deploy, `make-clients.sh`, install on both PCs, delete the old | rotated 4 Oct 2026 (the `.old-2026-10-04` copy is dead and can go) |
|
|
| `relay-key` (48 B, 0600, 4 Oct 19:23) | the Mac; `RELAY_KEY` on `igneum-relay`; in the relay clients on the PCs | the same relay, as the `x-igneum-key` header for scripts (`relay/lib/relay.mjs:34-44`; its own key since round 4 X23, no longer the intake key) | as the token | as the token | as the token | new 4 Oct 2026 |
|
|
| `dl-token` (11 B, 0600, 4 Oct 19:25; new value since the 5 Oct rotation) | the Mac; `DL_TOKEN` GitHub secret (the Windows runner writes it to `~/.config/igneum/dl-token`, `windows.yml:147`); `DL_TOKEN` on `igneum-relay` (the console); in every installed app's manifest URL (0.3.6 and later; 0.3.5 and older carry the old one) | the private downloads folder `dl/<token>/` on dl.igneum.network: installers, the manifest, the jobs file, the CI inputs. Readers: `packaging/mac/build-dmg.sh`, `packaged-config.sh`, `packaging/ota/*.sh`, `packaging/windows/*.sh`, `tools/ship-app.mjs`, `logs.mjs`, `jobs.mjs`, `console.mjs`, `build-job.mjs`, `relay.mjs`, `app/igneum-app/src/config.rs` | the folder name is in every installed app's `igneum-app.json` and in the GitHub secret's consumer; recoverable from any install | the installers and the signed files are readable (the signature still guards what the app accepts); low | the founder, by `docs/plans/rotation-phase-2.md` (a second folder, a build that carries the new token, delete the old folder when `tools/logs.mjs --rotation` reads 0 behind) | rotated 5 Oct 2026; the old folder dies on 7 Oct (8f) |
|
|
| `dl-token.old-2026-10-05` (12 B) | the Mac; still the folder name 0.3.5 and older apps read; in 2 commits of the history (masked on master; `tools/repo/fresh-repo.sh` rewrites it) | the OLD folder until 8f | nothing | as above, low | delete on 12 Oct per 8f step 6, after the fresh repository is pushed (the rewrite reads it) | dying |
|
|
| `log-intake-key` (32 B, 0600, 4 Oct 19:25; the NEW value since 5 Oct) | the Mac; Vercel `igneum` as `LOG_INTAKE_KEY_NEXT` (new) and `LOG_INTAKE_KEY` (old) until 8f; the same pair on `igneum-relay`; GitHub secrets `LOG_INTAKE_KEY_NEXT` (new) and `LOG_INTAKE_KEY` (old); in every installed app 0.3.6 and later (`igneum-app.json`); PC build scripts via `IGNEUM_INTAKE_KEY` | `POST /api/log` on the site and `fn=upload` on the relay (`site/api/log.mjs:45`, `relay/lib/relay.mjs:44`): write-only telemetry. Readers: `packaging/mac/packaged-config.sh`, `infra/gpu-bench/upload.sh`, `tools/build-job.mjs`, `logs.mjs`, `ship-app.mjs`, `repo/fresh-repo.sh`, `app/igneum-app/src/config.rs` | Vercel and GitHub keep it write-only (Hidden), so a new key must be generated and repackaged (phase 2 again) | junk rows in Neon and junk uploads to Blob; no read; low | the founder, by phase 2 | rotated 4 to 5 Oct 2026; the old key dies on 7 Oct (8f) |
|
|
| `log-intake-key.old-2026-10-05` (24 B) | the Mac; the 0.3.0 to 0.3.5 installs and the 0.2.0 launcher machines; in 9 commits of the history (0 tracked files on master; `fresh-repo.sh` rewrites it) | the intake, until 8f | nothing | low (write-only) | delete on 12 Oct per 8f step 6 | dying |
|
|
| `hetzner-token` (64 B, 0600, 3 Oct 22:43) | the Mac only | the Hetzner Cloud API: create and delete servers (`infra/seed-nodes/config.sh:29`, `infra/cloud-devnet/lib/common.sh:25-27`): the seed nodes and the cloud devnet, on the founder's bill | make a new one in the Hetzner console; the servers stay | servers created on the bill, the seed nodes and the devnet deleted, every server listed | the founder: Hetzner console, Security, API tokens: new token, write the file, delete the old | never rotated |
|
|
| `desec-token` (28 B, 0600, 3 Oct 19:34) | the Mac only | the deSEC DNS API for the igneum.network zone (`infra/seed-nodes/dns.sh:4-11`; the relay CNAME lives there, `relay/README.md:73`). CLAUDE.md says the domains sit on Vercel nameservers (3 Oct); `dns.sh` is the later file. Approximate until the founder confirms which nameservers answer today | make a new one at deSEC | the zone: point dl, relay or the site anywhere, get a certificate for it, serve a fake manifest (the signature still guards the apps) and a fake site; high | the founder: deSEC, token management | never rotated |
|
|
| `dev-fee-devnet.json` (249 B, 0600; purpose, address, private_key) | the Mac only | the devnet (chain 4463) funder for `tools/txgen/run.mjs` (`--funder`, line 57). Devnet coins only | devnet funds; regenerate | devnet coins; nothing real | any time: a new wallet, fund it on the devnet | none needed |
|
|
| `dev-fee-release.json` (234 B, 0600; an address only) | the Mac | `DEV_FEE_ADDRESS`, the founder's payout address from the Igneum Wallet (`docs/design/miner-dev-fee.md:50`). No private key here: the key is in the Igneum Wallet on the founder's Mac, outside this folder and outside this backup | the address is in the fork's `release-0.3.6` source | nothing, an address is public | not a secret. The wallet's own key needs its own backup (open) | n/a |
|
|
| `txgen/wallets.json` (3,090 B, 0600; 16 devnet wallets with keys) | the Mac only | the devnet load generator (`tools/txgen/run.mjs:56`) | regenerate | devnet coins; nothing real | any time | none needed |
|
|
| `vercel/auth.json` (397 B, 0600; token, refreshToken, expiresAt) and `vercel/config.json` (team ids, 0644) | the Mac only (`--global-config ~/.config/igneum/vercel`) | the `igneum` team: projects `igneum` (site), `igneum-dl` (downloads), `igneum-relay`; deploys, env vars (add, remove, pull the non-sensitive ones), domains. Readers: `packaging/ota/*.sh`, `packaging/windows/*.sh`, `tools/ship-app.mjs` | `vercel login` again as the igneum.network Google login; nothing unrecoverable | deploy anything to the three hosts, including a fake manifest at the real URL (still unsigned without the OTA key), read `BLOB_READ_WRITE_TOKEN`, delete projects; high | the founder: Vercel, Settings, Tokens: revoke; `vercel logout`/`login`. The access token expires (it is OAuth with a refresh token; 5 Oct: expiry the same evening) | expires on its own; the refresh token does not |
|
|
| `env` (406 B, 0600; `DATABASE_URL`, `DATABASE_URL_UNPOOLED`) | the Mac; `DATABASE_URL` on all of `igneum` and `igneum-relay` | Neon `igneum` (soft-voice-31914738, London): telemetry rows, faucet grants, the relay feed, tasks and wake stamps, the jobs ledger. Readers: `tools/build-job.mjs`, `jobs.mjs`, `logs.mjs`, `tuning.mjs`, `observer/observer.mjs`, `infra/cloud-devnet/experiments/observer.sh`, `site/api/*.mjs`, `relay/lib/relay.mjs` | Neon console, reset the role password; nothing unrecoverable | read every upload and relay message; write rows, including relay tasks the PC agents poll and run; high | the founder: Neon, reset password, then the file and both projects' `DATABASE_URL`, redeploy | never rotated |
|
|
| `build-slots`, `dlsite-dir`, `pytools/` | the Mac | a build cap, a local folder path, a pip copy of git-filter-repo | nothing | nothing | not secrets; excluded from the backup | n/a |
|
|
| GitHub tokens: `igneum-labs` (admin:org, repo, workflow) and `the second owner login` (gist, read:org, repo, workflow) | the macOS keychain through `gh` (`gh auth status`), not in this folder | the organisation igneum-network and the repository: push, Actions, the repository secrets, the Windows runner | `gh auth login` again | push to master (the site deploys on push), rewrite secrets, run workflows that receive `DL_TOKEN` and the intake key; the runner never holds the signing key, so no release can be signed from it; high | the founder: GitHub, Settings, Applications, revoke GitHub CLI; `gh auth login` | never rotated |
|
|
| GitHub repository secrets `DL_TOKEN`, `LOG_INTAKE_KEY`, `LOG_INTAKE_KEY_NEXT` | GitHub, write-only copies of the files above | the Windows build (`windows.yml:132-152`) | re-set from the files (`gh secret set`) | as the files | with the files; 8f step 3 drops `_NEXT` | in rotation |
|
|
| Vercel env `igneum`: `FAUCET_KEY` (two environments), `LOG_INTAKE_KEY`, `LOG_INTAKE_KEY_NEXT`, `DATABASE_URL` | Vercel, Hidden (not readable back) | `FAUCET_KEY` is the faucet wallet's private key (`site/api/faucet.mjs:2`): it exists ONLY on Vercel, not on the Mac, so it is not in this backup | the faucet wallet's funds are unreachable. Devnet today (chain 4463), so nothing real; the public testnet (4462) "gets its own key": generate THAT one on the Mac into `~/.config/igneum/faucet-testnet.json` first, then `vercel env add` from the file, so the backup covers it | the faucet's balance; a testnet drain | the founder: new wallet, `vercel env rm/add`, move the balance | file-first rule for the testnet key (open) |
|
|
| Vercel env `igneum-relay`: `DL_TOKEN`, `LOG_INTAKE_KEY`, `LOG_INTAKE_KEY_NEXT`, `RELAY_KEY`, `RELAY_TOKEN`, `DATABASE_URL`, `BLOB_READ_WRITE_TOKEN` | Vercel. All Hidden except `BLOB_READ_WRITE_TOKEN`, which is a plain variable (`vercel env ls` prints its prefix and `vercel env pull` fetches it) | the Blob store of the relay (files, build outputs from the PCs) | regenerate in the Vercel dashboard (Storage, the Blob store, tokens) | read, write and delete every relay file; medium | the founder: dashboard; re-add as Sensitive (`vercel env add BLOB_READ_WRITE_TOKEN production --sensitive`) so it stops being readable | recommend: make it Sensitive |
|
|
| Vercel env `igneum-dl` | none | the downloads host deploys from the folder; nothing secret in env | | | | n/a |
|
|
|
|
Copies that live outside the Mac and are not in the backup: the relay token and key inside the client scripts on PC 1,
|
|
PC 2 and the phone; the intake key and the folder token inside every installed app's `igneum-app.json`; the dated old
|
|
values in `~/igneum-dl-old-20261005` (folder files, not keys). None of them is needed to rebuild the Mac.
|
|
|
|
## 2. The backup and the restore
|
|
|
|
```
|
|
tools/keys/backup.sh --dry-run # what would go in: names, modes, sizes; creates nothing
|
|
tools/keys/backup.sh # ~/Desktop/igneum-keys-<date>.dmg, AES-256, hdiutil's own passphrase prompt
|
|
# (twice: create, then the verify attach); verified by sha256, listed, detached
|
|
tools/keys/restore.sh <dmg> --check # every file in the image against ~/.config/igneum: match / DIFFERS / missing
|
|
tools/keys/restore.sh <dmg> --to <dir> # copy back (0700 dirs, 0600 files, .pub 0644), refuses to overwrite without --force
|
|
tools/keys/test-backup.sh # the end-to-end test on a scratch folder with a throwaway passphrase
|
|
```
|
|
|
|
The image holds every file of `~/.config/igneum` except `build-slots`, `dlsite-dir` and `pytools/`, plus `README.txt`
|
|
(the listing and a copy of this file). The passphrase never passes through argv, the shell history or a file:
|
|
`hdiutil` prompts on the terminal (`--agent` for the macOS dialog). `--stdinpass` exists for the test harness only.
|
|
|
|
What the founder does with the image: two copies on two media that are not this Mac (a USB stick at home and a second stick
|
|
or an encrypted cloud folder), the Desktop copy deleted, the passphrase on paper away from both media. Run it again
|
|
after every rotation and replace both copies; keep one older image. `restore.sh --check` after each run.
|
|
|
|
Test record, 5 October 2026: `tools/keys/test-backup.sh` passed all 8 steps (dry run lists 16 files and creates
|
|
nothing; create and verify report 17 files byte-identical; `--list`; `--check` matches; a changed live file makes
|
|
`--check` fail and name the file; `--to` restores 16 files with 0600/0644 and 0700, refuses a second run without
|
|
`--force`; a wrong passphrase is refused; the raw image bytes do not contain the test values). The real folder was
|
|
run in `--dry-run` only.
|
|
|
|
## 3. What is exposed today, and what was done
|
|
|
|
| Finding | Fix |
|
|
|---|---|
|
|
| One copy of every key, on one disk, unencrypted | this branch: the encrypted image and the two-media rule (section 2) |
|
|
| `~/.config/igneum` was 0755 (listable by any local user; the files themselves were 0600) | `chmod 700` on the folder, `vercel/` and `txgen/` (done 5 Oct) |
|
|
| The old intake key sits in 9 commits of the history and the old folder token in 2 (0 tracked files on master) | known: `tools/repo/fresh-repo.sh` rewrites both after 8f; the fresh repository plan (`docs/plans/history-rewrite.md`). Not changed here |
|
|
| `BLOB_READ_WRITE_TOKEN` on `igneum-relay` is a plain env var, readable by anyone with project access | recommend: re-add as Sensitive (section 1) |
|
|
| `FAUCET_KEY` exists only on Vercel, write-only, no copy anywhere | recommend: the testnet faucet key is generated on the Mac into the folder first, then set from the file |
|
|
| `relay-token.old-2026-10-04` is a dead value still on disk | recommend: `rm -P` it (nothing reads it; `fresh-repo.sh` reads only the intake and dl files) |
|
|
| The dev-fee payout key lives in the Igneum Wallet, outside this folder and this backup | open: the wallet's own backup |
|
|
| Published Hardhat and Anvil developer keys in `tools/evm-smoke/smoke.mjs` and `tools/exec-attacks/lib/common.mjs` | public test vectors, allowlisted in the CI check; never fund those addresses on testnet 4462 or mainnet |
|
|
| Nothing in CI, no token in any script: every script reads a file under `~/.config/igneum` or an env var (checked: `git grep` of every file name above and of the current values, 0 hits in tracked files) | `tools/ci/no-secrets-check.sh` keeps it so (section 5) |
|
|
|
|
## 4. The OTA signing key: today, the second key, the emergency path
|
|
|
|
Today. One Ed25519 key signs three things: the update manifest, the jobs file and the Windows build inputs. Its public
|
|
half is one constant, `OTA_PUBLIC_KEY_HEX`, read at `ota.rs:1030`, `jobrun.rs:168,800,811` and by `igneum-ota-sign
|
|
embedded|verify-inputs`. Every app in the field trusts that one key and nothing else: on 5 October 2026 18:40 UTC the
|
|
intake shows 0.3.8 (one Mac), 0.3.7 (one Windows PC), 0.3.5 (Sam's Mac) and three machines without a version line
|
|
(`node tools/logs.mjs --rotation`). The signature file is 64 raw bytes as 128 hex, no key id.
|
|
|
|
The second key, as the next step (one release, about two hours of work).
|
|
|
|
| Step | What |
|
|
|---|---|
|
|
| 1 | `igneum-ota-sign keygen` a second key (K2) on the Mac with the output directed INTO a mounted `igneum-keys` image, so its private half exists only inside the encrypted copies; keep `ota-signing-key-2.pub` on disk. The current key is K1 |
|
|
| 2 | `manifest.rs`: `OTA_PUBLIC_KEYS: &[&str] = &[K1, K2]`; `verify_signature` tries each key in turn (two Ed25519 verifies, microseconds); `fingerprint()` of each; `embedded` prints both. `ota.rs`, `jobrun.rs`, `jobs.rs`, `inputs.rs` and `ota-sign.rs` take the slice instead of the constant. Tests: a manifest signed by K2 verifies, by a third key fails, the existing K1 vectors still pass. The `.sig` format does not change, so 0.3.x apps keep verifying K1 signatures of the same files |
|
|
| 3 | The same release carries revocation: the manifest gains an optional `revoked_keys: [<fingerprint>]`, signed like the rest. An app that verifies a manifest with the OTHER key and sees its current key listed writes `updates/revoked.json` and refuses that key from then on. Without this, a leaked K1 stays trusted forever (step 2 alone covers loss, not leak) |
|
|
| 4 | Ship it as 0.3.9, signed with K1, so every 0.3.5 to 0.3.8 machine takes it on its hourly check. From then on the publisher keeps signing with K1; K2 stays offline. `tools/logs.mjs --rotation` gains a column for the embedded fingerprints the app logs (it already logs `fingerprint` at `jobrun.rs:168`) |
|
|
| 5 | When every machine reports 0.3.9 or later, K2 is live as the fallback: K1 lost = mount the image, sign with K2, ship; K1 leaked = sign the next manifest with K2 with K1 in `revoked_keys` |
|
|
|
|
If K1 leaks TODAY, before 0.3.9: a manifest signed with any new key is useless. Every 0.3.x app verifies with K1 only
|
|
(`ota.rs:1030`), rejects the new signature ("manifest signature does not verify"), stays on its version and keeps
|
|
fetching the same URL every hour, where it will accept anything K1 signed. Plainly: the fleet cannot be moved to a new
|
|
key by the update path, and it stays open to whoever holds K1 for as long as that URL serves. The mitigation, in order:
|
|
|
|
| Order | Action | Effect |
|
|
|---|---|---|
|
|
| 1 | Take the manifest and the jobs file off the folder (`dl/<token>/igneum-app-latest.json`, `.sig`, `igneum-jobs.json`, `.sig`): a deploy of the downloads folder without them | the apps read 404 as "no manifest at the update URL yet" and stay put; the attacker's signed manifest has nowhere to be served unless they also hold the Vercel token or the DNS |
|
|
| 2 | Rotate the folder token (phase 2 again) and the Vercel token, the deSEC token, the Neon password, the relay token and key, the GitHub tokens: one leaked file from this folder means the folder was read | the old URL dies; the attacker cannot place a file at the new one |
|
|
| 3 | Build 0.3.9 (section 4 steps 1 to 3, with K1 NOT in the trusted list) and sign its manifest with K1, once, into the NEW folder; publish it before anything else goes there | every machine that applies it stops trusting K1. The race is real: a machine that fetched an attacker's K1 manifest first is owned; `logs.mjs` shows which machines moved |
|
|
| 4 | Machines that did not move (asleep, offline, or owned) are reinstalled by hand from the public links (`dl.igneum.network/public/`, `packaging/README-ship.md`) | the only path for an app that never saw step 3 |
|
|
|
|
Until 0.3.9 ships, the K1 file is the single point of failure in both directions, and this branch's backup covers the
|
|
loss direction only. The leak direction is covered by step 3 of the plan, and by keeping K1 off this disk: the publish
|
|
scripts read `~/.config/igneum/ota-signing-key` by path, so the file can become a symlink into a mounted image that is
|
|
attached only for the minutes of a publish (a follow-up, not done here; `publish-manifest.sh:36`, `ship-app.mjs:305`).
|
|
|
|
## 5. The CI check
|
|
|
|
`tools/ci/no-secrets-check.sh` runs in `ci.yml` (site job) after a `--self-test` that must fire on a known-bad tree
|
|
(a tracked `ota-signing-key`, a `dl-token.old-<date>`, an `igneum-app.json`, `FAUCET_KEY=0x<64 hex>`, `signingKey =
|
|
"<64 hex>"`, `x-igneum-key: <64 hex>`) and stay quiet on a known-good one (a sha256 next to another word, a 32-hex id,
|
|
the public key in `manifest.rs`, a `.test.mjs` vector). Over the tree it refuses any tracked file named like a key of
|
|
`~/.config/igneum` (with `.next` and `.old-<date>` variants, `*.env`, `.env*`, a bare `env`, `auth.json`,
|
|
`wallets.json`, `igneum-relay-clients.zip`, `igneum-log-key.txt`) and any 64-hex value (optionally `0x`) assigned to a
|
|
name ending in token, key, secret, password or passphrase, outside test files, `proving/fixtures/`,
|
|
`infra/cloud-devnet/results/` and `*.log`. Allowlisted by path with the reason in the script: the OTA public key, and
|
|
the published Hardhat and Anvil accounts in the devnet tools. 5 October 2026: self-test passed, 776 files checked,
|
|
0 hits. Hits are printed with the hex masked.
|