Khromosome Validator Operator Guide (chain 777001, public beta)

Audience: an experienced Ethereum node operator who has never seen Khromosome before. Read the whole guide before you apply (APPLICATION.md).

Honest status, 2026-10-04. The network is a public beta on a testnet-grade genesis. 16 validators were in genesis. 8 are active, and all 8 run on one host operated by the founder (opensolara). The other 8 (indices 8–15) were exited after a July 2026 network split. A second full node (openclaw-vps) follows the chain without validating. You would be the first independent operator. That is the point of this programme, and it is also why the steps below include several that the founder has to complete before you can join.

Testnet KHROME has no monetary value and will not carry over to mainnet. Mainnet is a fresh genesis (docs/raise/tokenomics-v2.md §3).


1. Network facts (read from the live beacon node, 2026-10-04)

Item Value
Chain ID / network ID 777001
Consensus Standard Ethereum PoS (Casper FFG + LMD-GHOST). No custom consensus
Clients in production Reth v1.8.0 (ghcr.io/paradigmxyz/reth:v1.8.0) + Lighthouse v8.2.2 (sigp/lighthouse@sha256:9a62bb8705455136e1cf96613460960f80dc2faa9d75b5c16a3bfcc9210dcdd1)
Slot / epoch 12 s / 32 slots (≈ 6.4 min); finality lags about 2 epochs
Genesis time 1779709290 (2026-05-25 UTC)
Genesis validators root 0x0bafbe731b2a76a84656a89c4ecfd6b410dee12803d23fb47548856ea3f4270b
Fork versions GENESIS 0x10000777, ALTAIR 0x20000777, BELLATRIX 0x30000777, CAPELLA 0x40000777, DENEB 0x50000777 (current), ELECTRA 0x60000777 scheduled at epoch 2,000,000,000 (i.e. not active)
EL forks Shanghai and Cancun at genesis; pragueTime is set far in the future (Prague not active)
Deposit contract 0x4242424242424242424242424242424242424242 (standard deposit contract bytecode, injected by the genesis generator)
Stake per validator 32 KHROME (MIN_ACTIVATION_BALANCE = MAX_EFFECTIVE_BALANCE = 32 KHROME; ejection at 16)
Activation churn MIN_PER_EPOCH_CHURN_LIMIT 4, CHURN_LIMIT_QUOTIENT 65536, so at most 4 activations per epoch
Deposit processing Pre-Electra Eth1-data voting: ETH1_FOLLOW_DISTANCE 2048 blocks, EPOCHS_PER_ETH1_VOTING_PERIOD 64
Exit rules SHARD_COMMITTEE_PERIOD 256 epochs (≈ 27 h) after activation before a voluntary exit is accepted; MIN_VALIDATOR_WITHDRAWABILITY_DELAY 256 epochs
Public endpoints RPC https://rpc.khromosome.xyz, WS wss://ws.khromosome.xyz, explorer https://explorer.khromosome.network

The deposit path has never been used on this chain. The head block's eth1_data.deposit_count is 0: all 16 validators came from the genesis state, and no deposit has ever been made through the contract. The first external activation is therefore also the first live test of deposit processing on 777001. We will do it with one validator first (§6).


2. Hardware and hosting

From docs/l1/03-infrastructure.md §2. These are minimums for a validator node running EL + CL + VC:

Resource Minimum Notes
CPU 4 cores / 8 threads
RAM 16 GB (32 GB preferred) Reth ~4–8 GB, Lighthouse BN ~2–4 GB
Disk 512 GB NVMe Not SATA, not HDD. Reth is IOPS-sensitive
Network 1 Gbit, ≥ 20 TB/mo or unmetered Public IPv4 with TCP+UDP 9000 and TCP+UDP 30303 reachable
OS Ubuntu 24.04 LTS (what the runbooks assume) Docker or systemd; both are covered below

Diversity requests (they affect selection; see APPLICATION.md): - Hosting provider: today every founder node is on Contabo. Any other provider (Hetzner, OVH, AWS, bare metal, home lab with a good uplink) improves the network's resilience. - Region / jurisdiction: non-US preferred for the first operators. - Client: Reth + Lighthouse is the only pair proven on this chain. A Geth + Prysm template exists (node/clients/docker-compose.geth-prysm.yml) but is unvalidated. If you want to run it, plan it with us as a separate, deliberate test.


3. Get the genesis files and peers

3.1 The genesis set (published by the founder; not in this repo)

The chain runs from five files that the founder holds on the production hosts at /root/khrome-node/testnet/:

File SHA-256 (production copy, 2026-10-04)
genesis.json (EL) db43e9941046216088c18d96896fd47a75be49fe35c93ac548b9e355b640e9f4
config.yaml (CL) 34908657b7b5832d15d11a307d149158f808316336e234c464356a5d6524a14f
genesis.ssz (CL state) 53c5bc3f7fe3492c0021a80366545c8b4246048e5a6b4d6cf6f54fbdc57e5a2f
deploy_block.txt d9b2aefb1febe2dd6e403f634e18917a8c0dd1a440c976e9fe126b465ae9fc8d
deposit_contract_block.txt 9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa

Do not use docs/l1/genesis/genesis.json or docs/l1/genesis/config.yaml from this repo. Those are the original templates, with placeholder allocations and MIN_GENESIS_TIME: 0. They do not produce the live chain.

Founder action F1: publish these five files at a stable HTTPS URL (referred to below as $GENESIS_URL) and confirm the hashes above. Until that happens, the founder sends them to you directly.

sudo mkdir -p /opt/khrome/testnet && cd /opt/khrome/testnet
for f in genesis.json config.yaml genesis.ssz deploy_block.txt deposit_contract_block.txt; do
  curl -fsSLO "$GENESIS_URL/$f"
done
sha256sum *        # must match the table above exactly. Stop if any differs
openssl rand -hex 32 | sudo tee /opt/khrome/jwt.hex >/dev/null && sudo chmod 600 /opt/khrome/jwt.hex

3.2 Peers (the honest blocker)

The founder's nodes currently peer over a private Tailscale tailnet (100.x addresses). Their ENR and enode advertise tailnet IPs, and validator hosts have no public p2p inbound by design (docs/l1/03 §4). An outside operator cannot reach them as things stand.

Founder action F2: one of the following: - (preferred) stand up a public sentry/bootnode: a non-validating Reth + Lighthouse node with public p2p ports and no validator keys. Publish its ENR and enode. Validator hosts stay closed; the sentry relays gossip. - or invite each operator into the tailnet with an ACL limited to tag:khrome-validator p2p ports (30303 and 9000) only.

You will receive: - BOOT_ENR: a beacon ENR (enr:-…) - BOOT_PEER_ID + multiaddr: for --libp2p-addresses / --trusted-peers - EL_ENODE: an enode://…@host:30303

Production uses static peering on both layers (reth --trusted-peers, Lighthouse --libp2p-addresses + --trusted-peers). After the July 2026 split we treat static peering as mandatory: discovery alone is not enough on a network this small.


4. Run the node

This mirrors the founder's production containers (host networking, the same images, the same flags), with your own IP and fee recipient. PUBLIC_IP is your host's public IPv4. FEE_RECIPIENT is an EVM address you control (it collects priority fees from blocks you propose).

export PUBLIC_IP=203.0.113.10                  # yours
export FEE_RECIPIENT=0xYourAddress             # yours. Never 0x3333…3333
export BOOT_ENR='enr:-...' BOOT_PEER_ID=16Uiu2... BOOT_MULTIADDR=/ip4/x.x.x.x/tcp/9000/p2p/16Uiu2...
export EL_ENODE='enode://...@x.x.x.x:30303'

# Stable EL identity: keep the p2p key on the host (see node/scripts/recreate-reth.sh:
# a rotating discovery secret silently broke trusted peering on 2026-05-31).
openssl rand -hex 32 | sudo tee /opt/khrome/p2p-secret >/dev/null && sudo chmod 600 /opt/khrome/p2p-secret

docker run -d --name khrome-reth --restart unless-stopped --network host \
  -v khrome-reth-data:/data -v /opt/khrome/testnet:/testnet:ro \
  -v /opt/khrome/jwt.hex:/jwt.hex:ro -v /opt/khrome/p2p-secret:/p2p-secret:ro \
  ghcr.io/paradigmxyz/reth:v1.8.0 node \
  --chain /testnet/genesis.json --datadir /data --full \
  --p2p-secret-key /p2p-secret \
  --authrpc.addr 127.0.0.1 --authrpc.port 8551 --authrpc.jwtsecret /jwt.hex \
  --http --http.addr 127.0.0.1 --http.port 8545 --http.api eth,net,web3 \
  --metrics 127.0.0.1:9101 \
  --port 30303 --nat "extip:$PUBLIC_IP" --trusted-peers "$EL_ENODE"

docker run -d --name khrome-lh-bn --restart unless-stopped --network host \
  -v khrome-lh-data:/data -v /opt/khrome/testnet:/testnet:ro -v /opt/khrome/jwt.hex:/jwt.hex:ro \
  sigp/lighthouse:v8.2.2 lighthouse beacon_node \
  --testnet-dir /testnet --datadir /data \
  --execution-endpoint http://127.0.0.1:8551 --execution-jwt /jwt.hex \
  --http --http-address 127.0.0.1 --http-port 5052 \
  --metrics --metrics-address 127.0.0.1 --metrics-port 5054 \
  --listen-address 0.0.0.0 --port 9000 --discovery-port 9000 \
  --enr-address "$PUBLIC_IP" --enr-udp-port 9000 --enr-tcp-port 9000 --disable-upnp \
  --boot-nodes "$BOOT_ENR" --libp2p-addresses "$BOOT_MULTIADDR" --trusted-peers "$BOOT_PEER_ID" \
  --suggested-fee-recipient "$FEE_RECIPIENT"

The systemd equivalents are in docs/l1/04-deploy/systemd/. Use those units with --testnet-dir /etc/khrome/testnet, replacing the placeholders <BOOTNODE_ENR> and <FEE_RECIPIENT>.

Sync. Lighthouse syncs from genesis (there is no public checkpoint-sync endpoint today; the founder can expose one read-only later). The chain is young (about 950k slots), so a full sync from genesis is practical.

Check that you are synced before importing any key:

curl -s 127.0.0.1:5052/eth/v1/node/syncing | jq          # is_syncing:false, is_optimistic:false
curl -s 127.0.0.1:5052/eth/v1/node/peer_count | jq       # connected ≥ 1 (ideally ≥ 2)
curl -s 127.0.0.1:5052/eth/v1/beacon/states/head/finality_checkpoints | jq
curl -s -X POST 127.0.0.1:8545 -H 'content-type: application/json' \
  --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
# compare with https://rpc.khromosome.xyz. Head should match within a slot or two

Firewall: open only TCP+UDP 9000 and TCP+UDP 30303. Keep 8545, 8551, 5052, and every metrics port bound to 127.0.0.1. Never expose the Engine API (8551).


5. Generate validator keys (offline, for a custom chain)

You generate and keep your own keys. The founder never sees your mnemonic or keystores, and you should never send them to anyone.

Option A (recommended): Lighthouse validator-manager. It reads the chain's own config.yaml, so the deposit signature is computed for Khromosome's fork version and you do not have to type it in by hand:

# on an offline machine with the same genesis set copied over
lighthouse validator-manager create \
  --testnet-dir /opt/khrome/testnet \
  --first-index 0 --count 1 \
  --eth1-withdrawal-address 0xWITHDRAWAL_ADDRESS \
  --suggested-fee-recipient 0xYourAddress \
  --output-path ./khrome-validators
# → validators.json (keystores, for import) and deposits.json (public deposit data)

Option B: ethstaker-deposit-cli (v1.x, the maintained successor to staking-deposit-cli) with a custom-chain setting. --devnet_chain_setting takes network_name, genesis_fork_version, exit_fork_version, and an optional genesis_validator_root.

Critical: recent ethstaker-deposit-cli releases default to --compounding (0x02 credentials). Compounding credentials are an Electra feature, and Electra is not active on 777001. Always pass --regular_withdrawal (0x01) and --amount 32. A 0x02 deposit on a Deneb chain is not something to experiment with on someone else's stake.

./deposit new-mnemonic --num_validators 1 \
  --regular_withdrawal --amount 32 \
  --withdrawal_address 0xWITHDRAWAL_ADDRESS \
  --devnet_chain_setting '{"network_name":"khromosome","genesis_fork_version":"10000777","exit_fork_version":"40000777","genesis_validator_root":"0x0bafbe731b2a76a84656a89c4ecfd6b410dee12803d23fb47548856ea3f4270b"}'

Notes: - Deposit signatures use GENESIS_FORK_VERSION 0x10000777. If a tool signs with Ethereum mainnet's 0x00000000, the deposit is silently ignored by the beacon chain, and the 32 KHROME is lost to the contract. Verify deposits.json has fork_version 10000777 before anything is submitted. - Voluntary exits are signed with the CAPELLA fork version (0x40000777) under EIP-7044. - Withdrawal credentials: use 0x01 (execution address). On the testnet the 32 KHROME stake is provided by the founder (§6), so the withdrawal address is agreed in writing before the deposit: either a founder-designated address (the stake goes back to the foundation on exit) or your own address under a disclosed delegation. Withdrawal credentials cannot be changed after the deposit. - Write the mnemonic on paper or steel, keep it offline, and keep two physical copies (docs/l1/04-deploy/KEYS.md §2).

Import (on the validator host, after sync). If you used Option B (keystore files), use account validator import as shown. If you used Option A (validators.json), start the VC below with --http --http-address 127.0.0.1 first. Then run lighthouse validator-manager import --validators-file validators.json --vc-url http://127.0.0.1:5062 --vc-token <api-token.txt>. Check the exact flags against lighthouse validator-manager import --help for v8.2.2.

docker run --rm -it --network host -v khrome-vc-data:/data -v /opt/khrome/testnet:/testnet:ro \
  -v "$PWD/khrome-validators":/in:ro sigp/lighthouse:v8.2.2 \
  lighthouse account validator import --testnet-dir /testnet --datadir /data --directory /in
shred -u khrome-validators/*keystore* 2>/dev/null   # remove the transferred copy

docker run -d --name khrome-lh-vc --restart unless-stopped --network host \
  -v khrome-vc-data:/data -v /opt/khrome/testnet:/testnet:ro sigp/lighthouse:v8.2.2 \
  lighthouse validator_client --testnet-dir /testnet --datadir /data \
  --beacon-nodes http://127.0.0.1:5052 --suggested-fee-recipient "$FEE_RECIPIENT" \
  --enable-doppelganger-protection \
  --metrics --metrics-address 127.0.0.1 --metrics-port 5064

Start the VC before the deposit is processed, so it is ready the moment the validator activates.


6. How an outside validator is activated: the actual path

Khromosome is an unmodified Ethereum beacon chain, so activation works exactly as it does on Ethereum. There is no allow-list, and no admin key can activate or reject a validator. Anyone who sends a valid 32-KHROME deposit to the deposit contract becomes a validator once the deposit is processed. The founder's role is practical, not a permission:

Step Who What
1 You Run a synced node (§4) and generate keys (§5). Send the founder only deposits.json, which is public data (pubkey, withdrawal credentials, signature, deposit_data_root).
2 Founder (F3) Supply the stake. The entire testnet KHROME supply sits in the founder's Trezor, so there is no other source of 32 KHROME. Either (a) the founder submits your deposit directly from his wallet, or (b) he sends you 32 KHROME + gas and you submit it yourself. With (a), your keys never leave you, which is the intended split.
3 Whoever submits Call the deposit contract: cast send 0x4242424242424242424242424242424242424242 "deposit(bytes,bytes,bytes,bytes32)" <pubkey> <withdrawal_credentials> <signature> <deposit_data_root> --value 32ether --rpc-url https://rpc.khromosome.xyz (values from deposits.json, 0x-prefixed).
4 Protocol The beacon chain waits ETH1_FOLLOW_DISTANCE (2048 EL blocks, ≈ 6.8 h). Proposers then vote the new deposit root in through Eth1-data voting (a majority within a 64-epoch period, ≈ 6.8 h). The deposit is included in a beacon block, and the validator enters the activation queue (≤ 4 per epoch). It becomes active a few epochs after its eligibility epoch is finalized. Expect roughly 14 hours or more from deposit to active_ongoing, based on the live parameters.
5 You Watch status: curl -s 127.0.0.1:5052/eth/v1/beacon/states/head/validators/<pubkey> \| jq .data.status moves through pending_initialized → pending_queued → active_ongoing.

Founder action F4: dry run first. Because deposit_count is 0, the founder should first activate one new founder-owned validator through this exact path on a separate host. That proves Eth1-data voting and deposit inclusion work on 777001 before an operator's deposit relies on them.

Exited validators cannot be revived. Indices 8–15 are withdrawal_done. In Ethereum PoS an exit is permanent: a validator that has exited can never become active again, and a new deposit to an exited pubkey only adds balance that is then swept to its withdrawal address. Every new operator needs new keys and a new deposit. Never reuse those pubkeys.

Liveness math. Finality needs more than 2/3 of active stake attesting. Today that is 8 validators, all on one host. With you added (say 4 validators), the founder holds 8/12. Taking either party offline would leave the other below 2/3, so finality would stop (blocks would still be produced). Real fault tolerance needs at least 3 operators, with none holding 1/3 or more. That is the target topology in docs/superpowers/specs/2026-06-29-C-decentralization-credible-neutrality-design.md §2.


7. Rewards: what you will (and will not) earn

Be clear-eyed. On the testnet there is no meaningful income from validating.


8. Slashing protection: read twice


9. Monitoring

Minimum checks, scripted every minute and alerted to you:

Check Command / metric Alert when
Beacon synced /eth/v1/node/syncing is_syncing or is_optimistic true for more than 5 min
Peers /eth/v1/node/peer_count connected < 1
Finality /eth/v1/beacon/states/head/finality_checkpoints finalized epoch flat for more than 4 epochs
Your validator /eth/v1/beacon/states/head/validators/<index> status ≠ active_ongoing or balance falling
EL head eth_blockNumber vs rpc.khromosome.xyz lag > 3 blocks
Disk df > 80%

Prometheus endpoints (localhost only): Reth :9101, Lighthouse BN :5054, VC :5064. The founder's detector scripts/monitor/khrome-watch.py watches stalls and privileged actions. The public validator dashboard is node/validators/index.html.


10. Operator commitments (what we ask of you)

These are targets for a public beta, not a paid SLA. They become contractual only in a written mainnet operator agreement.


11. Founder-side checklist (must be done before the first outside operator)

# Action Why
F1 Publish the 5-file genesis set + SHA-256 at a stable URL Operators cannot start a node without it
F2 Public sentry/bootnode (no keys) or tailnet invites with p2p-only ACL; publish ENR / peer-ID / enode Current peering is tailnet-only
F3 Fund each approved operator's 32-KHROME deposit (submit their deposits.json) All testnet KHROME is in the founder's Trezor
F4 Dry-run one founder-owned deposit activation end to end deposit_count is 0; the path has never been used
F5 KhromeChain.setValidator(operatorAddr, true) per operator Needed for ValidatorRewards.enroll()
F6 Fund + start() testnet ValidatorRewards (optional) It is currently empty and not started
F7 Expose the beacon API read-only at beacon.khromosome.xyz (Caddy, GET-only allow-list) The public dashboard needs it
F8 Replace the production fee recipient 0x3333…3333 with a real address Production BN/VC use this placeholder today
F9 Update node/validators/operators.json when each operator activates Keeps the public dashboard honest