deploy/chain — the phase-1 root chain on three cybernode machines
Runbook for running soft3 node as the proof-of-work root chain (decision
2026-10-15, cyber/launch.md; genesis 2026-11-05) on cyberproxy, deimos and
jupiter, node-to-node over cyber-radio (QUIC/UDP) and the HTTP API behind
the existing nginx. Prepared 2026-10-15 while the chain spec
(soft3/specs/chain.md) is still being written; § assumptions lists what
this runbook takes from the spec and must be re-checked when it lands.
Nothing here starts a service by itself.
Machine facts behind every number: cybernode/servers/<host>/inventory-2026-10-15.md.
files
| file | installs to | role |
|---|---|---|
soft3-chain@.service |
/etc/systemd/system/ |
one unit per chain instance (soft3-chain@pussy, soft3-chain@bostrom) |
chain.env.example |
/etc/soft3/<chain>.env |
the instance parameters (bind, radio port, moniker, thresholds) |
limits.conf.example |
/etc/systemd/system/soft3-chain@<chain>.service.d/limits.conf |
per-machine MemoryMax drop-in |
peers.toml.example |
/etc/soft3/<chain>.peers.toml |
bootstrap peers, format v1 |
nginx-chain-api.conf.template |
/etc/nginx/snippets/soft3-chain-<chain>.conf |
HTTP API behind TLS with an in-flight cap |
ufw-chain.sh |
run by hand | open/close the radio UDP port, logged |
soft3-chain-check |
/usr/local/bin/ |
health check: ExecStartPre gate, cron probe, JSON |
logrotate-soft3-chain |
/etc/logrotate.d/soft3-chain |
rotation of the check/ufw logs |
journald-soft3-chain.conf |
/etc/systemd/journald.conf.d/ |
journal cap for the node's own logs |
../../scripts/chain-fleet.nu |
dev machine | three nodes on one machine, the rehearsal |
layout
| cyberproxy | deimos | jupiter | |
|---|---|---|---|
| role | public edge (cyb.ai TLS), third node | best-provisioned node | validator box, third node |
| public IPv4 | 167.235.28.94 | 195.201.105.229 | 88.198.18.156 |
| state dir | /var/lib/soft3/<chain> on / (18 GB free — watch it) |
/var/lib/soft3 → symlink to /mnt/raid4tb/soft3 (root disk is 88 % full) |
/var/lib/soft3 → symlink to /mnt/nvme4tb/soft3 |
| HTTP bind | 127.0.0.1:7781 (pussy) · 7782 (bostrom) | same | same |
| radio UDP | 7791 (pussy) · 7792 (bostrom) | same | same |
| HTTP public | https://cyb.ai/<chain>/ |
https://deimos.cybernode.ai/<chain>/ |
none (no sudo; local only) |
| MemoryMax | 2G | 16G | 8G (40 GB already in use) |
| sudo | yes | yes | no — owner installs the sudoers line first, or runs the root steps by hand |
7780 stays with the 0.10.0 chaosnet node (spacepussy-test.service); it is untouched.
step by step
A. per machine, any day before genesis (nothing starts)
- Binary:
cargo install soft3 --version <phase-1 release> --lockedascyber(or copy the release binary to/home/cyber/.cargo/bin/soft3). Confirmsoft3 node --helplists--chain --radio --peers; the unit will not work with the 0.10.0 flag set. - Directories:
sudo mkdir -p /etc/soft3 /var/log/soft3-chain && sudo chown cyber:adm /var/log/soft3-chain. On deimos/jupiter firstsudo mkdir -p /mnt/<big>/soft3 && sudo chown cyber:cyber /mnt/<big>/soft3 && sudo ln -s /mnt/<big>/soft3 /var/lib/soft3. - Parameters:
sudo cp chain.env.example /etc/soft3/pussy.env, edit bind/radio/moniker/MemoryMax from the table;sudo chmod 0640 /etc/soft3/pussy.env && sudo chgrp cyber /etc/soft3/pussy.env. - Check script and unit:
sudo install -m 0755 soft3-chain-check /usr/local/bin/,sudo cp soft3-chain@.service /etc/systemd/system/, the MemoryMax drop-in fromlimits.conf.example(value from the table),sudo systemctl daemon-reload. Runsoft3-chain-check --pre pussy— clock, disk and env must be OK. - Logs:
sudo cp logrotate-soft3-chain /etc/logrotate.d/soft3-chain,sudo mkdir -p /etc/systemd/journald.conf.d && sudo cp journald-soft3-chain.conf /etc/systemd/journald.conf.d/ && sudo systemctl restart systemd-journald. - nginx (cyberproxy, deimos): declare the zone once in
http{}(limit_conn_zone $server_name zone=soft3_chain_pussy:1m;), render the snippet (sedline in the template), include it in the 443 server block,sudo nginx -t. Do not reload yet: the upstream is down and the location would answer 502 — reload on genesis day. - Identity and peers:
soft3 node id --home /var/lib/soft3/pussyprints the endpoint id (64 hex). Collect the three ids, fillpeers.toml.example→/etc/soft3/pussy.peers.tomlon every machine (each file lists all three; a node skips itself). - Do not
systemctl enableanything yet.
B. rehearsal (dev machine, then the testnet)
nu scripts/chain-fleet.nu --checkthennu scripts/chain-fleet.nuwith the release binary: three nodes on one machine must answer/statuswithin 60 s and agree on height; on the dev chain the script goes on to assert a late joiner's IBD, a transaction crossing the fleet, a depth-3 fork reorg and a kill-and-restart recovery, all on the consensus kernel withsoft3 mine --backend cpuprocesses doing the work (receipts:audit/chain/net-2026-10-15.md,audit/chain/slice-2026-10-16.md).- Same three steps on the machines with
SOFT3_CHAIN=pussy-rc(the launch allow-list has it) on ports 7783/7793 if a dress rehearsal on real hosts is wanted; tear down withsystemctl disable --now soft3-chain@pussy-rcandufw-chain.sh close pussy-rc.
C. genesis day (2026-11-05)
Order matters only for the peers' first dial; start the best box first.
| T | where | command | verify |
|---|---|---|---|
| T−60 min | all | sudo ./ufw-chain.sh open pussy |
ufw status shows 7791/udp ALLOW IN Anywhere |
| T−30 min | all | soft3-chain-check --pre pussy |
all OK; clocks within ms (timesyncd offsets were −2.8 ms / −71 µs / −19 µs on 10-15) |
| T−15 min | all | put the final genesis.json in /var/lib/soft3/pussy/ |
identical sha256 on the three machines |
| T | deimos | sudo systemctl enable --now soft3-chain@pussy |
journalctl -u soft3-chain@pussy -f shows listening + radio bound; soft3-chain-check pussy |
| T+1 min | jupiter | systemctl --user is not enough for a system unit — root runs systemctl enable --now soft3-chain@pussy |
check as above; /status must show deimos as a peer |
| T+2 min | cyberproxy | sudo systemctl enable --now soft3-chain@pussy |
three peers each |
| T+5 min | cyberproxy, deimos | sudo nginx -t && sudo systemctl restart nginx (restart, not reload — see servers/CLAUDE.md) |
curl -s https://cyb.ai/pussy/status |
| T+10 min | dev machine | cy net add pussy https://cyb.ai/pussy on a cyb body |
the body's networks row shows the height advancing |
| T+1 h | all | soft3-chain-check pussy |
progress OK — height moved |
| T+1 day | — | repeat for bostrom on 7782/7792 |
Rollback at any step: sudo systemctl disable --now soft3-chain@pussy,
sudo ./ufw-chain.sh close pussy, remove the nginx include and restart
nginx. The state directory is kept for post-mortem.
D. operating
soft3-chain-check pussyby hand or from cron every 5 min, output to/var/log/soft3-chain/check-pussy.log(rotated daily × 30).- Logs:
journalctl -u soft3-chain@pussy(journal capped at 1 GB). - Restart:
sudo systemctl restart soft3-chain@pussy— the unit refuses to start when the clock is not NTP-synchronized (ExecStartPre). - Peers file edits need a restart; the file is read at start.
- Thread count is a check: the 0.10.0 chaosnet node reached 1,800 threads on
2026-10-15 because each request took the state mutex for ~12 s and nginx let
abandoned clients keep their upstream thread (incident:
cybernode/servers/cyberproxy/spacepussy-test.md). The snippet'slimit_conn 32and the unit'sTasksMax=2048are the guards.
assumptions taken from the spec in progress
Re-verify every row against soft3/specs/chain.md when it merges; each one
is a parameter or a line in one file above.
| assumption | where it lands |
|---|---|
CLI: soft3 node --chain <id> --home DIR --bind HOST:PORT --radio HOST:UDP --peers FILE --moniker NAME; soft3 node id --home DIR prints the radio endpoint id — implemented on the kernel (soft3#187, slice 2026-10-16): --home is the chain directory (radio.key, genesis.tade, blocks/ = the kernel's store, anchors); --genesis FILE installs the genesis file into it (required for pussy/bostrom until the file is embedded). The node does not mine: miners run soft3 mine --node http://BIND --address NEURON against /template + /submit |
unit ExecStart, harness probe, step A7 |
chain ids in the launch set are pussy, bostrom (and pussy-rc for rehearsal) — from crate/src/node/genesis.rs on branch launch/45-genesis-chain-allowlist; protocol soft3/<chain>/v1, engine cybergraph+bbg |
env SOFT3_CHAIN, harness placeholder genesis |
the radio endpoint id is the node's ed25519 public key, printed as 64 lowercase hex (radio iroh-base PublicKey: Display) |
peers format id |
radio listens on one UDP port given by --radio; direct dial between the three machines, no relay |
ufw rule, peers relay = "" |
HTTP API keeps GET /health and GET /status with height and the bbg root (cyb bodies poll /status every 15 s with a 10 s timeout, cyb/shell/src/worlds/body/networks.rs), bodies limited to 8 MiB (specs/native-node.md) |
nginx snippet, check script |
block cadence: ASERT targets 30 s (chain.md §1.5, half-life 600 s); SOFT3_STALE_SECS=600 = 20 missed blocks, keep unless launch hashrate says otherwise |
env, check progress |
| PoW timestamp rule tolerates clock error of seconds; timesyncd (SNTP, ms-level) is enough, chrony is optional | unit time-sync.target, check clock |
state is $home/blocks/` (headers, bodies, undo journals) + `$home/genesis.tade (a tade file, chain.md §4.1 — not genesis.json); a restart replays every main-chain body on the genesis state and verifies the roots (§3.6) — measured locally in seconds for tens of blocks; budget it for the real height before genesis day |
state dir, TimeoutStopSec, step C T−15 |
the genesis file is produced by soft3 genesis build (specs/genesis.md) and copied byte-identical; scripts/chain-fleet.nu generates a test genesis (soft3 chain genesis-dev: chain dev, floor target POW_LIMIT_BITS, genesis time two hours back) when none is given — never a launch chain |
step C T−15 |