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.
| Component | Spec | Notes |
|---|---|---|
| Board | Raspberry Pi 4 or 5 | first boot refuses < 4 GB |
| RAM | 8 GB strongly recommended | light mode runs in well under 2 GB |
| Storage | USB3 UASP SSD, 256 GB+ | optional in light mode — see below |
| Network | Wired Ethernet | |
| Cooling | Active cooling | sustained 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-imageworkflow — it cross-builds arm64 beacond, pins both images by digest, builds withusimd/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:
- 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 - Provide the same config the appliance reads from
krypton-edge.conf— the identicalKRYPTON_*keys (see Configure below), as environment variables or an env-file, plus the signedkrypton-checkpoint.json. - Run the client:bash
krypton-edge-light serve # honours KRYPTON_CL_RPC / KRYPTON_EL_RPC / … - 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, keepKRYPTON_LISTENon 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:
nc -vz <cl-host> 26657 # CometBFT RPC
nc -vz <el-host> 8545 # EL JSON-RPCThose 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):
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_IDis the CometBFT chain-id string (used in BLS sign-bytes and the checkpoint message). It is not473374. The from-source raw genesis path names itkrypton-473374; a Kurtosis-launched net names itbeacon-kurtosis-473374. Do not guess — read it verbatim:bashcurl -s http://<cl-host>:26657/status | jq -r .result.node_info.network473374(0x7391e) is the numeric EVM chain-id — what the verifiedeth_chainIdreturns and what wallets connect with. You do not put it inKRYPTON_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):
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
| Signal | How | Healthy |
|---|---|---|
| Service up | journalctl -u krypton-edge-light -f | bootstraps from checkpoint, then serves |
| Verified chain-id | eth_chainId on :8645 | 0x7391e |
| Verified head | eth_blockNumber on :8645 | climbing |
| Rejection path | logs on a stale/lying upstream | C3/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
| Symptom | Likely cause | Fix |
|---|---|---|
| Runs but never bootstraps; no verified head | CL/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 / checkpoint | Required light-mode keys/files unset (fail-closed) | Set them and drop krypton-checkpoint.json on the boot partition |
| Wrong sign-bytes / chain-id mismatch | KRYPTON_CHAIN_ID is the numeric 473374 instead of the CometBFT string | Set the string from /status .result.node_info.network, verbatim |
| Checkpoint rejected | quorum not met / unknown keys / wrong height–hash | Ensure ≥ K configured pubkeys signed the same (height, hash); re-merge |
| Reads fail with a staleness error | head older than KRYPTON_MAX_STALENESS_SECS, or upstream eclipsed | Confirm upstream is at the live head; add a second source + KRYPTON_MIN_SOURCES=2 |
| First boot aborts on RAM | board < 4 GB | Use a Pi 4/5 with ≥ 4 GB (8 GB recommended) |
exec format error | wrong-arch artifact | The 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
kryptonuser withNoNewPrivileges,ProtectSystem=strict,ProtectHome,PrivateTmp, and a singleReadWritePaths=/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
- The fleet it talks to: Services and the RPC / L5 fleet.
- Quick start and Prerequisites for the full-node roles.
- Choosing a node type.