Skip to content

Edge node (verifying light client)

A light node is a role, not a device. It runs the verifying light client (krypton-edge-light, NODE_MODE=light) — not a full node and not a validator. It holds no state and no key; it points at the remote CL + EL RPC fleet and verifies every read before serving it. An edge node is just a light node run at the edge (a home or personal box). This page covers install → deploy → operate on the public testnet (chain-id 473374).

A light node is not a Raspberry Pi

The light node is plain software — the krypton-edge-light Rust client. A Raspberry Pi 4/5 is the reference appliance (a flashable, preconfigured SD-card image) and the most turnkey way to run one, but it is one supported host, not the definition. The same client runs on anything you build the binary for — an x86 mini-PC, another ARM SBC, a VM, or a container. The Pi path is described first because it's turnkey; see Run on non-Pi hardware for everything else.

What you get — and what it is NOT

A verifying light client: every eth_* answer is checked against the BLS-verified consensus header and the committed EVM state root before you see it — a malicious or down upstream RPC can withhold but cannot forge. It is not a validator (no staking, no keys, no rewards), not a full node (holds no state), and not a security boundary for anyone but its own operator. The honest one-line trust statement: "a self-hosted node that verifies the chain, trusting a checkpoint and 2/3 of the validator set."

Hardware — the reference appliance

The numbers below are the Raspberry Pi appliance baseline (the SD-card image). They are not a floor on the role: the light client itself needs very little (see Run on non-Pi hardware) — these are the appliance's image guards.

ComponentSpecNotes
BoardRaspberry Pi 4 or 5first boot refuses < 4 GB
RAM8 GB strongly recommendedlight mode runs in well under 2 GB
StorageUSB3 UASP SSD, 256 GB+optional in light mode — see below
NetworkWired Ethernet
CoolingActive coolingsustained I/O

No SSD needed in light mode

The image marks a USB3 SSD required because the default full-pruned mode writes MDBX chaindata that destroys SD cards. In light mode the SSD requirement is dropped — the light client holds no chaindata, and the full-node SSD preflight guard is not on the light path. The RAM floor (< 4 GB refused) still applies. The Pi 4/5 8 GB + USB3 SSD line is the appliance baseline; for a light-only deployment the SSD is optional. Full matrix: Hardware specs.

Install (Raspberry Pi appliance)

On a Raspberry Pi the light node ships as a flashable arm64 SD-card image — the turnkey appliance, not the Docker-Compose stack the other roles use. (Prefer different hardware? Skip to Run on non-Pi hardware.) The image manual (hardware rationale, flash steps, krypton-edge.conf, light-mode internals, security) is the primary reference — edge-node/README.md — and the testnet-join layer is in docs/EDGE-NODE-DEPLOYMENT.md.

Get the image. Both node images are pinned by digest (EL bera-reth v1.3.3, CL cross-built beacond-arm64 — upstream publishes no arm64 beacond). Light mode also needs the prebuilt krypton-edge-light-arm64 binary baked in.

  • CI artifact (recommended): trigger the edge-image workflow — it cross-builds arm64 beacond, pins both images by digest, builds with usimd/pi-gen-action, and uploads the artifact.
  • Local build: Linux only.

macOS cannot build the image

pi-gen needs Linux loop devices, which do not work in Docker Desktop on macOS. Build on a Linux host or VM. The arm64 cross-build of beacond is slow under emulation.

Then flash the .img.xz (Raspberry Pi Imager / dd / Etcher) and edit krypton-edge.conf on the FAT boot partition before first boot (next section). First boot validates the prebuilt light binary against its baked sha256 manifest, generates a per-device Engine-API JWT and SSH host keys, disables the full-node unit, and enables krypton-edge-light.service.

Run on non-Pi hardware

The SD-card image is a packaging of the light client, not the client itself. The verifying client is a single Rust binary, krypton-edge-light (edge-node/krypton-edge-light/) that runs anywhere you can build it — an x86-64 mini-PC, another ARM SBC, a VM, a laptop, or a container. There is no Pi-specific logic in the verification path; the Pi image just bakes in a cross-compiled arm64 build and wires up systemd + a firewall for you.

To run a light node off-Pi:

  1. Build the client for your target. CI ships only the arm64 build (for the appliance); build others with cargo, e.g.
    bash
    cd l1/edge-node/krypton-edge-light
    cargo build --release                                   # host-native
    # or cross-compile, e.g. cargo build --release --target x86_64-unknown-linux-gnu
  2. Provide the same config the appliance reads from krypton-edge.conf — the identical KRYPTON_* keys (see Configure below), as environment variables or an env-file, plus the signed krypton-checkpoint.json.
  3. Run the client:
    bash
    krypton-edge-light serve        # honours KRYPTON_CL_RPC / KRYPTON_EL_RPC / …
  4. Supply your own service + hardening. You don't get the image's systemd unit, nftables rules, or on-device secret generation for free — mirror them: run as a non-login user with NoNewPrivileges/ProtectSystem=strict, keep KRYPTON_LISTEN on loopback, and default-drop inbound. The appliance security model is the template.

Everything downstream — the C1–C7 verification pipeline, the :8645 verified RPC, the checkpoint trust anchor, the anti-eclipse and staleness gates — is identical to the Pi path. Only the delivery (your binary + your service vs. a flashed image) differs.

Deploy

Reachability prerequisite (read this first)

The endpoints the Pi verifies against must be Pi-reachable

A default Kurtosis enclave maps ephemeral host ports and advertises container-internal IPs — unreachable from a Pi on your LAN. The remote CL RPC (:26657) and EL JSON-RPC (:8545) the Pi verifies against — the testnet full-node fleet from RPC node — must be on a fixed, routable host:port whose advertised address is a real IP (LAN, Tailscale, or public). This is the single most common cause of "node starts but finds nothing." Confirm before relying on it:

bash
nc -vz <cl-host> 26657      # CometBFT RPC
nc -vz <el-host> 8545       # EL JSON-RPC

Those endpoints must serve CometBFT RPC (/status, /commit, /block) for headers and the SSZ block, and EL eth_getProof (EIP-1186) for proof verification — exactly what the RPC / L5 fleet and the services provide.

Configure krypton-edge.conf (light mode, testnet)

Edit on the boot partition (KEY=VALUE, no spaces around =; strict whitelist — never sourced or eval'd):

ini
NODE_MODE=light
MONIKER=my-krypton-edge
SSH_PUBKEY=ssh-ed25519 AAAA...
KRYPTON_CL_RPC=http://<cl-host>:26657      # remote CometBFT RPC (comma-list for anti-eclipse)
KRYPTON_EL_RPC=http://<el-host>:8545       # remote EL JSON-RPC (comma-list for failover)
KRYPTON_CHAIN_ID=krypton-473374            # the CometBFT chain-id STRING (NOT 473374) — see below
KRYPTON_CHECKPOINT_PUBKEYS=<ed25519 hex,...>
KRYPTON_CHECKPOINT_K=1                      # quorum k (k-of-n)
KRYPTON_LISTEN=127.0.0.1:8645              # where verified RPC is served (note :8645, not :8545)
KRYPTON_MAX_STALENESS_SECS=120             # reject-stale-head bound

…plus a signed krypton-checkpoint.json on the boot partition.

Two different "chain-id" values — do not confuse them

  • KRYPTON_CHAIN_ID is the CometBFT chain-id string (used in BLS sign-bytes and the checkpoint message). It is not 473374. The from-source raw genesis path names it krypton-473374; a Kurtosis-launched net names it beacon-kurtosis-473374. Do not guess — read it verbatim:
    bash
    curl -s http://<cl-host>:26657/status | jq -r .result.node_info.network
  • 473374 (0x7391e) is the numeric EVM chain-id — what the verified eth_chainId returns and what wallets connect with. You do not put it in KRYPTON_CHAIN_ID. See Networks & chain IDs.

Checkpoint trust anchor (required in light mode)

The client bootstraps from a weak-subjectivity checkpoint signed by a k-of-n quorum of known publisher keys — the bootstrap trust root. Produce it with ops/publish-checkpoint.sh pointed at a testnet CometBFT RPC, drop it on the boot partition as krypton-checkpoint.json, and set KRYPTON_CHECKPOINT_PUBKEYS / KRYPTON_CHECKPOINT_K. First boot fails closed if any required light-mode key or the checkpoint file is missing.

Optional anti-eclipse

List ≥2 independent CL endpoints in KRYPTON_CL_RPC and set KRYPTON_MIN_SOURCES=2 to require multi-source head agreement. An optional Bitcoin-anchor gate (KRYPTON_BTC_ANCHOR_BUNDLE) adds defense-in-depth on top of k-of-n.

Operate

Verify it's tracking — and verifying

Watch the service, then query the verified RPC on KRYPTON_LISTEN (:8645, distinct from a full node's :8545):

bash
journalctl -u krypton-edge-light -f

# numeric EVM chain-id — must be 0x7391e (= 473374):
curl -s 127.0.0.1:8645 -d '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}'
# -> {"jsonrpc":"2.0","id":1,"result":"0x7391e"}

# a verified balance (proven against the anchored state root, not trusted):
curl -s 127.0.0.1:8645 -d '{"jsonrpc":"2.0","id":1,"method":"eth_getBalance","params":["0x<addr>","latest"]}'

The served subset is eth_getBalance, eth_getTransactionCount, eth_getCode, eth_getStorageAt, eth_blockNumber, eth_chainId, plus verified eth_call. Each read runs the verification pipeline: BLS commit verify against the pinned set (C1) → header bind → SSZ EVM state-root anchor (C2) → eth_getProof MPT verify (C3) → staleness gate (C6).

Confirm it rejects a lie, not just returns a number

Point KRYPTON_CL_RPC at ≥2 endpoints with KRYPTON_MIN_SOURCES=2 to exercise the anti-eclipse gate. A tampered value fails the C3 proof check and a head older than KRYPTON_MAX_STALENESS_SECS fails C6 closed — you will see the rejection in journalctl -u krypton-edge-light. Liveness against one honest endpoint alone does not exercise the rejection path.

Monitoring signals

SignalHowHealthy
Service upjournalctl -u krypton-edge-light -fbootstraps from checkpoint, then serves
Verified chain-ideth_chainId on :86450x7391e
Verified headeth_blockNumber on :8645climbing
Rejection pathlogs on a stale/lying upstreamC3/C6 rejections appear

Upgrade & rollback

On the Pi appliance, upgrades are a re-flash of a newer image (which re-pins the EL/CL/light-binary digests), not a digest bump in .env. Re-flash, then re-apply your krypton-edge.conf + krypton-checkpoint.json on the boot partition. Off-Pi, upgrade by replacing the krypton-edge-light binary and restarting your service. Either way there is no key and no consensus weight, so a re-flash or restart is low-risk — the client re-bootstraps from the checkpoint.

Troubleshooting

SymptomLikely causeFix
Runs but never bootstraps; no verified headCL/EL endpoints not reachable from the Pi (ephemeral Kurtosis ports / internal IPs)Use a fixed-port, routable endpoint; nc -vz <host> 26657 / 8545
First boot aborts: missing RPC / chain-id / pubkeys / checkpointRequired light-mode keys/files unset (fail-closed)Set them and drop krypton-checkpoint.json on the boot partition
Wrong sign-bytes / chain-id mismatchKRYPTON_CHAIN_ID is the numeric 473374 instead of the CometBFT stringSet the string from /status .result.node_info.network, verbatim
Checkpoint rejectedquorum not met / unknown keys / wrong height–hashEnsure ≥ K configured pubkeys signed the same (height, hash); re-merge
Reads fail with a staleness errorhead older than KRYPTON_MAX_STALENESS_SECS, or upstream eclipsedConfirm upstream is at the live head; add a second source + KRYPTON_MIN_SOURCES=2
First boot aborts on RAMboard < 4 GBUse a Pi 4/5 with ≥ 4 GB (8 GB recommended)
exec format errorwrong-arch artifactThe image and light binary must be arm64; rebuild via CI

More in Troubleshooting.

Security

  • Secrets generated on-device at first boot — Engine-API JWT, SSH host keys (and a random admin password if the account is locked). Nothing is baked into the image.
  • Verified RPC is loopback by default (KRYPTON_LISTEN=127.0.0.1:8645).
  • nftables default-drop input: loopback + established + ICMP, SSH from RFC1918 LAN only.
  • Service hardening: runs as the non-login krypton user with NoNewPrivileges, ProtectSystem=strict, ProtectHome, PrivateTmp, and a single ReadWritePaths=/var/lib/krypton. It makes only outbound RPC calls.
  • The light binary is sha256-verified against a baked manifest before its service is ever enabled.

RPC_BIND=lan does NOT expose the verified RPC

RPC_BIND=lan opens the full-node port (8545), not the light client's 8645. The verifying RPC on KRYPTON_LISTEN stays loopback-only regardless. To reach it from another LAN host, change KRYPTON_LISTEN to a LAN bind and add your own nftables rule, or front it with an SSH tunnel.

Status — devnet-validated, testnet wiring is operator-supplied

The light client (C1–C7) is built and was live-validated end-to-end against the devnet (beacon-kurtosis-80087), not yet against a live public testnet. The software supports a 473374 light deployment, but a turnkey 473374 bundle/checkpoint does not ship yet — you supply the endpoints, the chain-id string, and a checkpoint produced from the live testnet. A live, reachable 473374 RPC fleet (see RPC node) is the hard prerequisite. See Networks & chain IDs.

See also

Operator docs. Testnet chain-id 473374; mainnet 47337 (gated on external audit). Not financial advice.