Secure Boot from Zero
Minimal footprint validator provisioning. A freshly provisioned server receives only Docker, the agent container, and one API key. No secrets, no configs, no application code. Zero-trust onboarding with encrypted secret delivery and a 10-phase hard-fail verification sequence.
The previous bootstrap model writes all secrets to .env before the agent even contacts JILHQ.
The provisioning script generates passwords, JWT secrets, Redis credentials, MPC encryption keys, and killswitch secrets - then writes them all as plaintext environment variables directly to disk. The agent only authenticates with JILHQ after Docker Compose starts, meaning there is a window between server creation and successful handshake where every secret sits unencrypted on the filesystem.
If the server is compromised before boot completes - via a supply chain attack on the hosting provider, a malicious SSH key, a rogue base image, or even a timing attack during provisioning - the attacker gets everything in plaintext: database passwords, JWT signing keys, MPC encryption material, and consensus credentials. There is no recovery path short of rotating every secret across the entire fleet.
The old model treats provisioning as a trusted phase. Secure Boot from Zero treats provisioning as adversarial by default: the server is untrusted until it proves its identity, receives encrypted secrets, and passes every verification gate.
Identity-only bootstrap. Encrypted secret delivery. 10-phase hard-fail verification.
A freshly provisioned server receives only Docker, the agent container, and one API key (its identity credential). No secrets, no configs, no application code, no database passwords. The bootstrap .env contains nothing that would be valuable to an attacker - just enough information for the agent to identify itself and contact JILHQ.
The agent boots, authenticates with JILHQ by proving possession of its API key (via SHA256 hash - the key never crosses the wire), receives approval, and then gets all secrets delivered encrypted with NaCl secretbox (xsalsa20-poly1305). Only after decrypting and verifying integrity does the agent proceed through the remaining verification phases. Every single phase is a hard-fail gate: if any phase fails, the boot sequence halts immediately and no services start.
At boot time, the server knows nothing except who it is (NODE_ID) and where to call home (JILHQ_URL). All operational secrets - database passwords, JWT keys, MPC material, signing keys - arrive encrypted after identity verification. A compromised server at boot time yields zero usable credentials.
Minimal Bootstrap .env
The bootstrap .env file contains only identity and connectivity variables. Compare the old approach (20+ secrets in plaintext) with the new minimal footprint.
Old Approach - Full .env at Boot
New Approach - Identity-Only .env
| Variable | Example | Purpose | Sensitive? |
|---|---|---|---|
| NODE_ID | jil-validator-de | Unique node identifier in JILHQ registry | No |
| ZONE_ID | DE_BAFIN | Primary compliance zone assignment | No |
| AUTHORIZED_ZONES | DE_BAFIN | Comma-separated zones this node can process | No |
| EXTERNAL_IP | 46.225.116.22 | Public IP for peer discovery and DNS | No |
| JILHQ_URL | https://hq.jilsovereign.com | Fleet management server endpoint | No |
| REGISTRY_URL | 167.235.150.16:5000 | JILHQ Docker image registry | No |
| PULL_MODE | true | Enables pull-based provisioning with HQ | No |
| PORT | 8055 | Agent HTTP listener port | No |
| JIL_API_KEY | hq_ak_... | Identity credential for HQ authentication | Low* |
The API key is an identity credential, not an operational secret. It proves the node's identity to JILHQ but cannot be used to access databases, sign transactions, decrypt MPC shares, or perform any privileged operation. Even if leaked, an attacker cannot bootstrap a rogue validator without also passing image digest verification, key challenge-response, and obtaining a time-limited authorization token from JILHQ.
10-Phase Secure Boot Sequence
Every phase is a hard-fail gate. If any phase returns an error, the entire boot sequence halts immediately. No services start. No containers launch. The agent reports the failure to JILHQ and enters a dormant retry loop.
AUTHENTICATE - Handshake
The agent sends a POST /v1/fleet/handshake request to JILHQ containing its NODE_ID, ZONE_ID, EXTERNAL_IP, software version, and a SHA256 hash of its API key. JILHQ looks up the node in its registry, verifies the API key hash against the stored (encrypted) key, and returns an approved or rejected status.
Failure mode: Node not found in registry, API key hash mismatch, or node marked as decommissioned. Agent enters 60-second retry loop (max 10 attempts).
FETCH SECRETS - Encrypted Delivery
After authentication, the agent requests its operational secrets via POST /v1/fleet/secrets/:nodeId. The request body contains SHA256(api_key) to prove identity without transmitting the key itself. JILHQ encrypts the full secrets payload using NaCl secretbox (xsalsa20-poly1305) with a symmetric key derived from the API key bytes.
Failure mode: Hash mismatch (identity unproven), decryption failure (key derivation mismatch), or integrity hash mismatch (payload tampered). Agent halts immediately.
REGISTRY AUTH - Docker Login
JILHQ provides registry credentials for the Docker image registry at 167.235.150.16:5000. The agent configures Docker login with the credential. The credential is short-lived.
Failure mode: Registry authentication rejected, credential expired, or Docker daemon unavailable. Agent halts.
FETCH CONFIG - Signed Bundle
The agent fetches its configuration bundle from GET /v1/registry/config/:nodeId. The bundle contains docker-compose.yml, zone-specific policies, validator configuration (validator.toml), and environment variable templates. The entire bundle is HMAC-signed by JILHQ.
Failure mode: HMAC signature mismatch (possible MITM or config tampering), bundle parsing failure, or missing required config keys. Agent halts.
PULL IMAGES - Download from Manifest
Using the authenticated Docker session from Phase 3, the agent pulls every container image listed in the configuration manifest. For full nodes this is 17 images; for compact nodes, 14 images. The pull is all-or-nothing: if any single image fails to download, the entire boot halts.
Failure mode: Network timeout, registry unavailable, image not found, or disk space exhausted. Agent halts and reports which image(s) failed.
VERIFY DIGESTS - SHA256 Hard-Fail
After pulling all images, the agent fetches the pinned digest manifest from GET /v1/registry/images/digests. The agent computes the local digest of each pulled image and compares it against the pinned value. Every single digest must match exactly. A single mismatch means the pulled image is not the same binary that JILHQ approved.
Failure mode: Any image digest mismatch. Agent halts immediately, reports the mismatched image(s) to JILHQ, and refuses to start any containers. HARD FAIL with no override.
VERIFY KEYS - Ed25519 Challenge-Response
JILHQ issues a cryptographic challenge to the agent via POST /v1/fleet/verify-keys. The challenge is a random 32-byte nonce. The agent must sign this nonce with each of its 5 registered key types (ed25519_consensus, hmac_shared, api_key, ssh_keypair, hsm_signing) and return the signatures. JILHQ verifies each signature against the public keys stored in the hq_node_keys table.
Failure mode: Any key type fails verification. Agent halts. A compromised or cloned node cannot pass this gate. HARD FAIL.
GET AUTH TOKEN - 24h Consensus Authorization
After passing all verification gates, the agent requests a consensus authorization token from POST /v1/fleet/authorize-consensus. JILHQ verifies that all previous phases passed, checks that the node is not suspended or decommissioned, and issues a time-limited token valid for 24 hours.
Failure mode: JILHQ refuses authorization (node suspended, quorum policy violation, or manual hold). Agent halts - will not start services without consensus authorization.
START SERVICES - Docker Compose Up
With secrets decrypted, config verified, images verified, keys proven, and authorization obtained, the agent writes the final .env (combining bootstrap variables + decrypted secrets), writes the verified docker-compose.yml, and executes docker compose up -d. The agent monitors container startup and waits for all services to report healthy status.
Failure mode: Docker Compose returns non-zero exit, any container exits unexpectedly, or health checks fail after 120-second timeout. Agent runs docker compose down and halts.
COMPLETE - Handshake Complete
The agent sends POST /v1/fleet/handshake/complete to JILHQ, reporting successful boot with a manifest of running containers, their versions, health status, and the time elapsed for each phase. JILHQ marks the node as consensus-ready in its registry and includes the node in quorum calculations.
Failure mode: Completion handshake rejected (rare - indicates state inconsistency). Agent continues running but JILHQ does not mark node as consensus-ready until manually resolved.
Encrypted Secret Delivery
The secret delivery protocol ensures that operational secrets never cross the wire in plaintext and that only the intended recipient can decrypt them. The encryption is end-to-end: JILHQ encrypts on its side, the agent decrypts on its side, and the shared key is derived from material that both parties already possess (the API key).
Protocol Flow:
1. Agent sends POST /v1/fleet/secrets/:nodeId with body { "apiKeyHash": SHA256(api_key) }
2. JILHQ looks up node, decrypts stored API key using MASTER_ENCRYPTION_KEY (AES-256-GCM)
3. JILHQ verifies SHA256(decrypted_api_key) === request.apiKeyHash - proves identity without the agent transmitting the key
4. JILHQ derives symmetric key: HMAC-SHA256(api_key_bytes, "jil-secrets-v1") - first 32 bytes used as NaCl secretbox key
5. JILHQ assembles secrets JSON, computes SHA256(secrets_json) as integrity hash
6. JILHQ encrypts secrets JSON with xsalsa20-poly1305 (NaCl secretbox) using derived key + random 24-byte nonce
7. Response: { "encrypted": base64(nonce + ciphertext), "integrityHash": sha256_hex }
8. Agent derives same symmetric key locally: HMAC-SHA256(api_key_bytes, "jil-secrets-v1")
9. Agent extracts nonce (first 24 bytes), decrypts ciphertext with NaCl secretbox
10. Agent verifies SHA256(decrypted_json) === response.integrityHash
11. Agent parses secrets JSON and holds values in memory only (never written to disk as separate file)
NaCl secretbox (xsalsa20-poly1305) provides authenticated encryption: the ciphertext includes a MAC that prevents any modification without detection. Combined with the SHA256 integrity hash over the plaintext, this gives two independent layers of tamper detection. The 24-byte nonce ensures that even if the same secrets are delivered twice, the ciphertext is completely different. xsalsa20-poly1305 is also constant-time, preventing timing side-channels during decryption.
| Secret | Purpose | Used By |
|---|---|---|
| POSTGRES_PASSWORD | PostgreSQL database authentication | All DB-connected services |
| REDIS_PASSWORD | Redis cache authentication | compliance-api, fraud-firewall, others |
| JWT_SECRET | JWT token signing and verification | wallet-api, settlement-api, others |
| JILHQ_SHARED_SECRET | HMAC signing for HQ-agent communication | validator-update-agent |
| MPC_ENCRYPTION_KEY | MPC shard encryption at rest | mpc-cosigner |
| KILLSWITCH_SECRET | Emergency pause authorization | consent-killswitch |
| PROOFLAYER_SIGNING_KEY | Proof layer attestation signing | proof-verifier |
| LBP_SECRET | Launchpad backend signing | launchpad-api |
| RAMPS_SECRET | Fiat on/off ramp authentication | ramps-api |
| ADMIN_KEY | Administrative API authentication | settlement-api admin routes |
| VALIDATOR_NAME | Human-readable validator display name | explorer-api, dashboard |
| PG_POOL_MAX | PostgreSQL connection pool size | All DB-connected services |
| ZONE_ID | Verified zone assignment (may differ from bootstrap) | settlement-consumer, compliance-api |
| AUTHORIZED_ZONES | Verified zone list (authoritative from HQ) | settlement-consumer |
Hard-Fail Enforcement
The previous boot model used soft-fail behavior: if a verification step failed, the agent would log a warning and continue anyway. This was pragmatic for early development but unacceptable for production. Secure Boot from Zero enforces hard-fail on every gate - a single failure stops the entire boot sequence.
| Check | Old Behavior SOFT FAIL | New Behavior HARD FAIL |
|---|---|---|
| Bundle signature mismatch | Warning logged, config applied anyway | ABORT - possible MITM or config tampering detected |
| Image digest mismatch | Warning logged, container started anyway | ABORT - unverified images will not be executed |
| Key verification failed | Warning logged, degraded authentication mode | ABORT - cannot prove key ownership, node may be cloned |
| Image pull failure | Warning logged, missing containers skipped | ABORT - cannot run without all required images |
| Auth token refused | Skipped, services started without consensus auth | ABORT - not authorized for consensus participation |
| Docker Compose failure | Error logged, partial service set tolerated | ABORT - all containers torn down, no partial state |
| Secret decryption failure | N/A (secrets were on disk) | ABORT - cannot operate without credentials |
| Integrity hash mismatch | N/A (no integrity verification) | ABORT - secret payload may be corrupted or tampered |
It is always better to refuse to start than to start in an unverified state. A validator that does not boot is safe. A validator that boots with tampered images or unproven keys is a threat to the entire network. Hard-fail enforcement ensures that the network never admits a node that has not passed every single verification gate.
Security Properties
The Secure Boot from Zero protocol provides the following security guarantees.
| Property | Mechanism |
|---|---|
| No secrets on disk at boot | Bootstrap .env contains only identity credentials (NODE_ID, ZONE_ID, JIL_API_KEY). All operational secrets delivered encrypted after authentication. |
| Encrypted secret delivery | NaCl secretbox (xsalsa20-poly1305) with HMAC-SHA256 derived symmetric key. Random 24-byte nonce per request. Authenticated encryption prevents undetected modification. |
| Identity verification | Agent sends SHA256(api_key) to prove identity without transmitting the key. JILHQ decrypts stored key and verifies hash match. Key never crosses the wire. |
| Code integrity | Every pulled Docker image digest is compared against SHA256 digests pinned by JILHQ at build time. Any mismatch halts boot immediately. |
| Key ownership | Ed25519 challenge-response across all 5 key types. Agent must sign a random nonce with each key. JILHQ verifies against registered public keys. |
| Authorization gate | 24-hour time-limited consensus token issued only after ALL verification phases pass. Token must be renewed before expiry. Revocable by JILHQ at any time. |
| Hard-fail enforcement | ANY failed phase halts the boot sequence. No services start. No partial state. No degraded mode. The agent enters a dormant retry loop. |
| Ephemeral registry credential | JILHQ registry credential used for Docker login, short-lived. Credential does not persist on filesystem. |
| Audit trail | Every phase transition logged with timestamp, duration, and result. Failures reported to JILHQ via handshake status update. Full history queryable via /v1/fleet/control/:nodeId/history. |
| Rate limiting | Maximum 3 secret delivery requests per node per hour. Prevents brute-force attempts against the secret delivery endpoint. Exceeded requests return 429. |
Backwards Compatibility
Secure Boot from Zero is fully backwards-compatible with existing validators. No migration or downtime is required for the current fleet. The system detects the provisioning model at startup and adapts automatically.
Legacy Nodes (PULL_MODE=false)
Validators provisioned with the old model already have all secrets on disk. These nodes skip the entire Secure Boot sequence and start services directly using docker compose up with their local configuration. They continue to register with JILHQ via the standard handshake but do not go through Phases 2-8.
Hybrid Nodes (PULL_MODE=true, secrets on disk)
Validators that have PULL_MODE=true but already have secrets written will detect that secrets are already present and skip Phases 2-3 (secret delivery and registry auth). They will still go through Phases 4-10 (config fetch, image pull, digest verification, key verification, authorization, service start, completion).
New Nodes (Minimal .env + PULL_MODE=true)
Freshly provisioned validators with the minimal bootstrap .env go through the complete 10-phase sequence. This is the recommended provisioning model for all new validators going forward.
| Scenario | Phases Executed | Notes |
|---|---|---|
| PULL_MODE=false (legacy) | 1, 10 only | Handshake + completion. Services start from local config. |
| PULL_MODE=true, secrets on disk | 1, 4, 5, 6, 7, 8, 9, 10 | Skips Phases 2-3. Full verification of config, images, and keys. |
| PULL_MODE=true, no secrets (new) | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 | Complete 10-phase Secure Boot. Recommended for all new nodes. |
Existing validators can be upgraded to the Secure Boot model by rotating their secrets through JILHQ, removing plaintext secrets from .env, and rebooting the agent. The next boot will trigger the full 10-phase sequence. This can be done on a rolling basis without affecting network quorum.
API Endpoints
All endpoints are served by JILHQ on port 8054. Authentication is via JIL_API_KEY header or HMAC signature depending on the endpoint. All responses are JSON.
| Endpoint | Method | Phase | Purpose |
|---|---|---|---|
| /v1/fleet/handshake | POST | 1 | Agent authentication - proves identity via API key hash, receives approved/rejected status |
| /v1/fleet/secrets/:nodeId | POST | 2 | Encrypted secret delivery - NaCl secretbox encrypted payload with integrity hash |
| /v1/registry/config/:nodeId | GET | 4 | Signed configuration bundle - docker-compose.yml, policies, validator.toml, HMAC signature |
| /v1/registry/images/digests | GET | 6 | Pinned image digests - SHA256 for every approved container image |
| /v1/fleet/verify-keys | POST | 7a | Issue key verification challenge - random 32-byte nonce for all 5 key types |
| /v1/fleet/verify-keys/response | POST | 7b | Verify challenge response - agent submits signatures, JILHQ verifies against registered public keys |
| /v1/fleet/authorize-consensus | POST | 8 | Issue 24h consensus authorization token - required for validator to participate in consensus |
| /v1/fleet/handshake/complete | POST | 10 | Boot completion - agent reports running containers, versions, health status, phase timings |
| Endpoint | Rate Limit | Window | Rationale |
|---|---|---|---|
| /v1/fleet/secrets/:nodeId | 3 requests | 1 hour | Prevents brute-force secret extraction |
| /v1/fleet/handshake | 10 requests | 10 minutes | Allows retry loop during boot |
| /v1/fleet/verify-keys | 5 requests | 1 hour | Prevents challenge flooding |
| /v1/fleet/authorize-consensus | 3 requests | 1 hour | Token issued once per boot cycle |
Zero secrets on disk. 10 hard-fail gates. Every validator verified before it serves a single request.
Secure Boot from Zero ensures that no validator joins the JIL network without proving its identity, verifying its code integrity, and obtaining cryptographic authorization from JILHQ.