Architecture
Enforce first. Sign second. Log everything.
nklave is a trust boundary between an untrusted validator client and your signing keys. Every request runs the same four-step lifecycle before a key is ever consulted — and every decision is sealed into a verifiable checkpoint chain.
UNTRUSTED TRUST BOUNDARY (nklave) CUSTODY
┌──────────────┐ ┌───────────────────────────┐ ┌────────────┐
│ Validator │ sign │ 1. policy.evaluate(ctx) │ │ Signing │
│ client │ ──────▶ │ fork-allowlist │ │ keys │
│ (Lighthouse │ req │ rate-limit │ │ keystore │
│ Teku Prysm │ │ custom policies │ sig │ YubiHSM │
│ Lodestar) │ │ 2. slashing-protection DB │ ───▶ │ CloudHSM │
│ │ ◀────── │ 3. append to audit log │ only │ GCP KMS │
└──────────────┘ Allow │ 4. request signature ─────┼──if──┘ │
Refuse │ │ allowed
└───────────────────────────┘ Refused reqs
│ never reach
▼ the keys.
┌───────────────────────────┐
│ Merkle-root checkpoint │
│ every 60s, operator-signed│
└───────────────────────────┘ The request lifecycle
Policy layer evaluates
The policy layer runs first — fork-allowlist, rate-limit, and slashing rules — resolving the request to an explicit Allow or Refuse(code). A refusal stops here; the key is never reached. This is the extension point that carries new guardrails as restaking adds signing constraints.
Slashing-protection DB is consulted
Double-vote / surround-vote for attestations, double-proposal for blocks (and (height, round) for CometBFT). Safety state is monotonic and cannot be rolled back by host tampering.
Decision is logged
The allow or refuse decision, with a deterministic reason code, is committed to the append-only log atomically with the slashing-protection DB update.
Key is requested — only if allowed
Only an allowed request reaches the custody backend. A refused request never touches the keystore or HSM, so enforcement holds even on a compromised host.
Why the log is trustworthy
Decisions are appended to a newline-delimited JSON log and sealed into Merkle-root checkpoints every 60 seconds, each signed by a separate operator key kept apart from any validator key. Running
nklave log verify --from 0 --to latest
re-walks every checkpoint, recomputes each Merkle root, and confirms the operator-key signatures. A non-zero exit means the log was edited — tampering is detectable, not just discouraged.
Five minutes from zero to signing through nklave.
Run the container, import your slashing history, and switch your signer URL.