80 lines
13 KiB
Markdown
80 lines
13 KiB
Markdown
# Igneum protocol specification, section 8: client security
|
|
|
|
Spec version 0.1, 3 October 2026. Status of this section: Decided (3 October 2026, ledger G7 and X6). Nothing here is implemented. This section binds the official client (the one-click Igneum app on Windows, macOS and Linux, and the headless miner it wraps) and the project's release process. It binds no other client: any client is welcome, and a client that ignores this section is not an invalid node, only one the project did not ship.
|
|
|
|
The two findings it answers. An app that auto-updates on ten thousand machines is an admin key over the miners, the wallets the app made and the vote keys (G7). An installer that creates a wallet, holds keys and mines, promoted to people who have never run a miner, is a honeypot for fake copies and a target for every antivirus (X6). Both are true. The rules below name the key, bound it, and make the real app distinguishable from a fake by anyone who looks.
|
|
|
|
## 8.1 Reproducible builds
|
|
|
|
1. Every release of the official client MUST be reproducible: a build from the tagged source on the documented toolchain MUST produce byte-identical binaries on every supported platform.
|
|
2. The repository MUST carry, at the release tag, the hash of every released binary, the toolchain versions, and the command that reproduces the build.
|
|
3. A release whose hashes a third party cannot reproduce is withdrawn and the cause published.
|
|
|
|
## 8.2 The release key
|
|
|
|
1. Every release MUST be signed by the release key. The release key's public half is published in the genesis block and is therefore in every node's copy of the chain; it is also in the repository and on the domain.
|
|
2. The private half is held in hardware by the steward named in the published key policy. It never exists on a networked machine. The policy (who holds it, under which entity and in which jurisdiction, how a signature is produced, who holds the predecessor key of item 5) is published before the client ships to anyone outside the project, which is before the public testnet and not at it (O-8.1; ledger G9 and L1, round 3).
|
|
3. The client MUST refuse any update whose signature does not verify under the release key. A refused update is reported to the user, with the hash it saw, and the running version continues.
|
|
4. There are no silent updates. The client MAY check for a release and MUST show the version, its hash and the reproduction instructions; installation proceeds only when the user accepts it. The client never applies an update while mining without the user's acceptance for that version.
|
|
5. Rotation and revocation (rule of 3 October 2026, ledger G9, round 3). Release keys form a chain K0, K1, K2 and so on, with K0 in genesis. Rotation: a message `(K_n, K_n+1, reason, date)` signed by K_n and published as a transaction in a block. From the first certified checkpoint whose past holds that block (before finality is active, from 43,200 DAA s of depth) the client accepts releases signed by K_n+1 and none signed by K_n. Rotation is the only path to a new key. Revocation: a message `(K_n, reason, date, successor or none)` signed by K_n-1, the previous key, published in a block the same way; it declares K_n lost or compromised. After a rotation the previous key is retired, not destroyed: it is kept in separate custody by a second holder named in the policy, and its only remaining power is to revoke its successor. K0 has no predecessor, so a revocation certificate for K0, signed by K0 at creation and naming a pre-generated K1, is held by the second holder apart from K0. From the block that carries a revocation onward the client MUST refuse every release signed by the revoked key and accepts releases under the named successor; if no successor is named, the client accepts no update at all until the user installs a new client by hand from the repository. A lost key is therefore revocable without its own signature, which the earlier rule (revocation "by its own last signed release") could not do.
|
|
|
|
## 8.3 The client cannot change consensus
|
|
|
|
1. Consensus rules change only through the upgrade path of section 5.7: new code activates when 90% of blue blocks in the signalling window carry the signal. A client release carries code; it carries no activation. The chain, not the app, decides when a rule takes effect, and 90% of miners have to say so in their blocks.
|
|
2. The client MUST NOT set a signalling bit without a user choice shown in the interface, and the default MUST be the choice the user last made, never the release's preference. On first run there is no last choice: the client signals nothing until the user chooses, and the interface shows that nothing is being signalled (rule written 5 October 2026, ledger G10; the control is not yet built in the app).
|
|
3. A release that changed a consensus rule without an activation signal would fork its users off the chain, which is the only thing a hostile release key can do to consensus, and it is visible to everyone within one block.
|
|
|
|
## 8.4 Distribution
|
|
|
|
1. Builds on every platform are notarised or signed with the platform's own mechanism (Apple notarisation, Windows Authenticode, a signed Linux package or AppImage) in addition to the release key.
|
|
2. Downloads are offered only from the project's domain and from the repository's release page. The hash of the file is shown beside every download button. No app store, mirror, torrent or third-party site is an official source, and the domain publishes the list of the only official sources.
|
|
3. Shipping a miner means antivirus products flag it. The app documents this and never asks the user to disable protection; it tells the user how to verify the hash instead.
|
|
|
|
## 8.5 Keys and the seed
|
|
|
|
1. The app creates the user's wallet locally. Before mining starts, the seed phrase MUST be shown and the user MUST confirm it (by re-entering the words or an equivalent check). Mining does not start until the confirmation succeeds.
|
|
2. The app MUST offer a hardware wallet as the destination for earnings, so a user who never wants a seed on the mining machine does not need one.
|
|
3. The seed is never transmitted, never backed up by the project, and never asked for by any update, support route or message. The permanent line, verbatim, appears wherever the app appears: on the download page, in the installer, on the seed screen and in every support channel:
|
|
|
|
**Nobody from Igneum will ever ask for your seed.**
|
|
|
|
4. The vote key of section 3 is a separate key, created by the app and held by the app, and its loss costs the user weight, not coins (W5 of section 3 moves weight to a successor key). The app shows it as a mining key and never as a wallet.
|
|
|
|
## 8.6 Parameters in this section
|
|
|
|
| Parameter | Value | Label |
|
|
|---|---|---|
|
|
| Release key publication | genesis block, repository, domain | Decided |
|
|
| Release key custody | hardware, held by the named steward; the predecessor key with a second holder; policy published before the client ships | Decided; policy Open (O-8.1) |
|
|
| Release key rotation and revocation | rotation signed by the current key, revocation signed by the previous key, both published in a block | Decided 3 October 2026 (ledger G9) |
|
|
| Silent updates | none | Decided |
|
|
| Consensus activation through the client | impossible; 90% of blue blocks signalling (section 5.7) | Decided |
|
|
| Official download sources | the project's domain and the repository release page, hash shown beside the button | Decided |
|
|
| Seed shown and confirmed before mining | required | Decided |
|
|
| Hardware wallet option | required | Decided |
|
|
| The permanent line | "Nobody from Igneum will ever ask for your seed." | Decided, verbatim |
|
|
| Consensus parameters from the environment | none outside devnet and simnet; the override file refused on mainnet | Implemented on `fud-consensus`, pending rollout (8.7) |
|
|
| Params digest in the handshake | BLAKE2b-256 of the effective params; a mismatch is refused | Implemented on `fud-consensus`, pending rollout (8.7, section 2.8) |
|
|
|
|
## 8.7 The node's parameters are the network's (4 October 2026 night, ledger G12 and X18)
|
|
|
|
1. The official client MUST NOT let an environment variable, a setting or an update change a consensus parameter on mainnet or testnet. The node it wraps ignores `IGNEUM_POW_*` outside devnet and simnet and refuses the override file on mainnet (section 2.8); the app passes an override file only to a devnet or simnet node, and the packaged file (`node_override_params`) exists for the devnet's height switches alone.
|
|
2. A node refuses any peer whose consensus params digest differs from its own (section 2.8). The client SHOULD show the digest its node printed at start, so an operator can compare two machines by eye (not built yet); a refused peer is reported by the node's log line, never silently.
|
|
|
|
## 8.8 Proving jobs and the keys (Designed, 5 October 2026, night; ledger X17, O-8.3)
|
|
|
|
Status: Designed. Not implemented. In Ember 0.3.9 the engine process spawns the prover host (`igneum-prove-host`) and the record signer (`igneum-miner sign-record`) itself, under the same OS user, from `app/igneum-app/src/prover.rs`; the payout wallet is `wallet.json` in the app data directory with its permissions locked to that user (`app/igneum-app/src/keys.rs`); the vote keys are derived by `igneum-miner` from the worker label the engine passes it (`app/igneum-app/src/engine.rs`, the label comment above the worker spawn). A guest program proven on that machine therefore runs as a user that can read all three. The rules below change that before the first external job (design document section 6) runs on any client the project ships. They bind shard proving the same way, because a shard proof runs the chain's own guest and a job runs a stranger's.
|
|
|
|
1. The prover holds no key. The process that runs a guest program (the prover host, and on Windows the WSL2 distribution it runs in) MUST NOT hold, read or be able to read the payout wallet, the seed, the vote keys or any release key material. It receives shard or job inputs and returns proof bytes and a statement, nothing else.
|
|
2. A separate OS user or sandbox. The prover host runs under its own OS user (Linux, macOS) or inside a WSL2 distribution with no mount of the app data directory (Windows), with no read access to the engine's data directory and no write access outside its own work directory. Where the platform offers a sandbox (macOS App Sandbox, a Linux user namespace or seccomp profile, Windows AppContainer), the client uses it in addition to the user split, never instead of it.
|
|
3. One local socket, one kind of message. The engine exposes a signer to the prover over a local socket (a Unix domain socket, a named pipe on Windows) that accepts exactly one request: sign a proof record (the 274-byte record of section 7.7 item 1) or an external-job payout claim, for a shard or job the engine itself handed to this prover, with the payout address the user configured. The signer refuses any other message, any record for work the engine did not hand out, any payout address other than the configured one, and any request rate above what the machine can prove. It never signs a transaction, a vote, a signalling bit or a release.
|
|
4. The vote key stays with the engine. Voting (section 3) and signing records are the engine's work; the prover never sees the vote key. The app shows the vote key as a mining key and never as a wallet (8.5 item 4).
|
|
5. The escape test (O-8.3). Before the first external job runs on a shipped client, and again at every release that touches the prover, the project runs a hostile guest program on the phase 4 devnet: one that tries to read the app data directory, the wallet file, the process environment and the engine's memory, to open outbound connections, and to send the signer a transaction, a vote and a record for work it was not handed. The test passes when every attempt fails and is logged, the wallet file is byte-identical before and after, no outbound connection other than the proof submission opens, and the signer's refusal count equals the number of hostile requests. A release whose escape test has not passed on every platform it ships for does not ship with proving on by default.
|
|
6. What the user is told. The client states on the proving tile and in Settings that the prover runs apart from the wallet and holds no key, naming the OS user or sandbox it runs under and the date its escape test last passed; the phone app repeats the statement (`docs/design/phone-app.md` 4.1, Isolation).
|
|
|
|
| Parameter | Value | Label |
|
|
|---|---|---|
|
|
| Keys readable by the prover process | none | Designed |
|
|
| Prover isolation | separate OS user, or a WSL2 distribution without the data directory; a platform sandbox in addition | Designed |
|
|
| Signer interface | one local socket; proof records and payout claims for handed-out work only; nothing else signed | Designed |
|
|
| Escape test | hostile guest program on the phase 4 devnet, before the first external job and at every prover release | Designed (O-8.3) |
|