Signing a spork database
Part of Board instructions. How to check a spork database, sign its sporks again with board keys and install the result on the node of this machine.
hedgehog util spork-db works on the file and never asks a node. It treats every spork type alike, so it
also signs and installs the storage, ledger and communication sporks, which a node run without the hidden
--preview switch keeps in spork.db but neither acts on nor publishes. The board key comes from
-k, --key-file or $HEDGEHOG_BOARD_KEY, and --network-keys names the keys the release is built
with. A file that holds a board keys spork is judged by the keys its chain makes active.
The steps
hedgehog util spork-db sign spork.db.bak --output once.db --key-file <key file of member 1>hedgehog util spork-db sign once.db --output twice.db --key-file <key file of member 2>hedgehog util spork-db validate twice.db- Stop the node, run
hedgehog util spork-db import twice.db, then start it again.
flowchart LR
A["spork.db.bak<br/>signed by keys the board<br/>no longer holds"] -->|"sign<br/>key of member 1"| B["once.db<br/>signed once"]
B -->|"sign<br/>key of member 2"| C["twice.db<br/>signed and co-signed"]
C -->|validate| V{"valid: yes?"}
V -->|no| X["find the reason in the<br/>lines validate prints"]
V -->|yes| I["stop the node<br/>import twice.db<br/>start the node"]
I --> K["the old spork.db is kept<br/>as a backup beside it"]
I --> P["the node publishes the sporks,<br/>and every peer takes them"]
The two members never need to be on one machine: member 1 hands once.db to member 2, who signs it with
their own key. Neither file holds a key.
Mind these when following the steps:
validate spork.db.bakendsvalid: noand exits with 1 before the signing; that is expected, as the board keys of today did not sign it.signnever overwrites its output, so deleteonce.dbortwice.dbbefore running a step again.- Run
importas the user the node runs as, or with the sameXDG_DATA_HOME. Run as another user it installs into that user’s data directory, which the node never reads, and printsbackup: none, the node holds no spork database. The full path of the installed file shows only in the last line,imported: <path>; check that it is the data directory of the node. - A refused
importprints the reason after the report, where it made one: read the line aftervalid: yesorvalid: no. A refusal with exit code 2 is followed by the usage. - The node logs nothing by default. Start it with
-vvto see the warning on sporks of itsspork.dbthat two current board keys did not sign, which it leaves out and keeps inspork.db.unsigned-<epoch seconds>.
The three commands are:
| Command | Arguments | What it does |
|---|---|---|
validate |
<file> |
Prints each spork with its digest, signers, log length, whether the board signed it and whether its content holds, then valid: yes or valid: no |
sign |
<file> --output <new file> and a board key |
Writes a new file with each spork signed or co-signed |
import |
<file> |
Installs the file as spork.db in the data directory of this machine |
validate exits with 0 when two current board keys signed every spork and the content of each holds, with 1
when not, and with 2 when the file cannot be read. A line looks like
MINT_SUPPLY: made 2026-10-01T09:00:00Z, digest 7b1e04aa9d2c8f31, signer 44f21a0c9b7e6d18, co-signer 7c0e35b9a1d24f68, log entries 0, signed by the board, content valid
and a type that the file does not hold is VESTING_STORAGE: absent. A signer that is no active board key
reads not a board key, a spork that only one key signed reads co-signer none and not signed by the
board, a spork that only switched-off keys signed reads signed by board keys that are switched off, and
content that does not hold reads content not valid: <reason>. It ends valid: yes or valid: no.
sign needs an --output file that does not exist yet, and refuses a file or a link there with
--output <file> exists and is not overwritten. It judges every spork and builds its text before it shows
or signs any, then prints each spork in full and signs it. A spork that two active board keys signed is left
as it is and printed as MINT_SUPPLY: signed by the board, left as it is. A spork that they did not sign
becomes a first version of the same data, made now, and is printed as MINT_SUPPLY: a first version of its
data, made at 2026-10-23T10:00:00Z and signed by 44f21a0c9b7e6d18. A first version that one active board
key signed is co-signed and printed as MINT_SUPPLY: co-signed by 7c0e35b9a1d24f68. The command exits with
0 and prints output: <file>. A refusal exits with 2 and, when the new output file was made, leaves no
output file.
A spork signed once but not as a first version, such as a proposal over a stored spork, previous data that was never stored, or a signature log, is refused:
Nothing was signed: the MINT_SUPPLY spork is signed once and is no first version of its data, which is all this command co-signs; co-sign a proposal over a stored spork with gridspork-cosign
A second run with the key that signed is refused with
Nothing was signed: the MINT_SUPPLY spork cannot be signed: A second, different network key has to co-sign the spork
Content that does not hold, text that cannot be shown as it is, and a first version stamped ahead of the
clock of this machine are refused the same way, with Nothing was signed: and the reason.
A board keys spork in the file is signed like any other: its first version holds the chain of the file, and the two keys that sign it are keys that chain makes active.
What the file holds is judged as history the network already carried, not as a change, so its entries are not checked as new mints are. A mint storage entry at a height of the old chain was paid by that chain; the ledger mints by time, to ledger accounts.
import checks the file in this order, and installs nothing when a check fails:
- Its board keys carry on the chain of board keys of the node, else it would take the node back to keys
that the board switched off, or onto a fork:
Nothing was imported: the board keys of <file> do not carry on the chain of board keys of the node, which would take the node back to keys that the board switched off, or onto a fork, exit 1. - It holds no less of the chain of board keys than this machine’s record
(
board-keys-<16 hex of the root>.seen), and no chain that parts from it:The file <file> holds ..., exit 2. - It prints the same lines as
validate, and the last must bevalid: yes:Nothing was imported: two current board keys did not sign every spork of <file>, or the content of one does not hold, exit 1. - No other process holds
spork.db.lock:Nothing was imported: the spork database in <data directory> is in use: a node runs on it, or an import; stop the node of this machine first, exit 2.
It then copies the spork.db it replaces to spork.db.<epoch seconds> and prints backup: <path>, or
backup: none, the node holds no spork database, installs the file as a node reads it from the wire, prints
imported: <path> and exits with 0. A file that holds more of the chain than the record updates the record
after the install; if that write fails, the command prints
The chain of board keys of <file> was not recorded: <reason> and still exits with 0. A file that cannot be
read or installed ends it with exit code 2.
A node of an earlier release takes no lock, so stop it before an import all the same: it keeps its sporks in memory and writes them over the file when it saves. The command prints the reminder first:
note: stop the node of this machine before the import, as it keeps its sporks in memory and writes them over the file when it saves
A file that is not a spork database, holds a time too far in the past or the future to be read, holds a spork
in the slot of another type, or holds an amount beyond the bounds of the wire ends any of the three commands
with exit code 2 and Cannot read the spork database <file>: <reason>. For a misplaced spork the reason is
the MINT_STORAGE slot holds a spork that is no MINT_STORAGE spork; for an amount it is the MINT_SUPPLY
spork holds an amount beyond the bounds of the wire.
Installing on one node is enough: a node that starts with the database publishes its sporks every three minutes and whenever a connection comes up; a peer with no spork of a type takes the first version, and one with another first version takes the newer by its time stamp. Peers hold the sporks about two minutes after the node starts again.
In the source
application/src/main/java/org/unigrid/hedgehog/command/util/SporkDbCommand.java,SporkDbValidate.java,SporkDbSign.java,SporkDbImport.javaandSporkDatabaseFile.java: checking, signing again and installing a spork databaseapplication/src/main/java/org/unigrid/hedgehog/model/spork/BoardKeysAuthority.javaandBoardKeysRecord.java: installing a chain and the record it must carry onapplication/src/main/java/org/unigrid/hedgehog/model/spork/SporkDatabaseLock.java: the lock a running node holds onspork.dbapplication/src/main/java/org/unigrid/hedgehog/model/spork/content/SporkContent.java: the content rules a spork is checked by