Home-lab onboarding
How a person at home — call them provider@home — joins the
OpenInfra fleet and starts billing usage from a customer like
coledex.com.
This document assumes the operator has already brought up the
control plane (deploy/local-pilot/control-plane-up.sh on a LAN, or
the Hetzner CX22 stack in docs/HETZNER-MIGRATION.md). What follows
is the provider side.
0. Topology
Section titled “0. Topology” [provider@home] [control plane VPS] ──────────────── ─────────────────── - openinfra-agent (systemd) - api-server (gRPC :9092) - docker daemon - postgres - wg-quick@openinfra ───────WireGuard────▶ wg0 (10.77.0.1) - LUKS2 /var/lib/openinfra - OCI registry (TLS via Caddy) - Caddy on :443 / :80The provider host has no inbound exposure. Everything reaches it through the WireGuard tunnel it initiates outbound to the CP. That is why the install script does not ask for a public DNS name for the home host.
1. Operator side — mint an invite
Section titled “1. Operator side — mint an invite”Before the provider runs anything, the operator mints a single-use invite tied to the provider’s expected payout wallet:
curl -sS -X POST https://cp.coledex.com/api/v1/admin/host-invites \ -H "Authorization: Bearer $ADMIN_JWT" \ -H "Content-Type: application/json" \ -d '{"ttl_hours": 24, "note": "provider@home — Kuma"}' \ | jq -r '.token'# → returns the plaintext token EXACTLY ONCE. Send it to the# provider over a secure channel (Signal, encrypted email).The operator also pre-allocates a WireGuard IP for this provider
(e.g. 10.77.0.42) and hands the provider:
- the invite token
- the WG IP they should claim
- the CP’s WG public key (printed by
wg show wg0 public-keyon the CP) - the CP’s WG endpoint (
cp.coledex.com:51820)
2. Provider side — encrypted data partition
Section titled “2. Provider side — encrypted data partition”A dedicated, LUKS2-encrypted partition for the agent’s data dir is
required for any host that may end up running customer secrets.
Pick a partition (the example uses /dev/sdb1) and run:
sudo deploy/home-lab/setup-luks2.sh /dev/sdb1You’ll be prompted for a passphrase (twice). The script:
- formats the partition as LUKS2 with argon2id KDF (~1 s unlock cost on a typical desktop, ~100 ms on a server)
- creates an ext4 filesystem inside the container, labelled
openinfra-data - adds
/etc/crypttaband/etc/fstabentries withnofailso a detached/failed disk doesn’t lock the host out at boot - mounts at
/var/lib/openinfrawith0700perms
The script is idempotent: re-running on an already-LUKS2 device reuses the container and only refreshes crypttab/fstab.
Headless hosts: on first boot you’ll be prompted for the LUKS passphrase on the console. If the host has no console, set up a keyfile or use
systemd-cryptenroll --tpm2-device=autoto bind the unlock to the TPM — that’s a per-host policy decision left to the operator and is intentionally out of scope here.
3. Provider side — install the agent
Section titled “3. Provider side — install the agent”sudo deploy/home-lab/install-agent.shThe installer is interactive by default and asks for:
- Control plane public hostname (e.g.
cp.coledex.com) — used to download the agent binary - Control plane gRPC endpoint — default
10.77.0.1:9092(the CP’s address inside the WG tunnel) - Invite token — from step 1
- Solana payout pubkey — your wallet for receiving the OINFRA SPL rewards
- WireGuard CP endpoint / pubkey / your assigned IP — from step 1
It then:
- Downloads
openinfra-agent-linux-<arch>from the CP’s/dist/path (override withOPENINFRA_AGENT_URL=...) - Generates a WireGuard keypair under
/etc/wireguard/, writesopeninfra.conf, enableswg-quick@openinfra - Writes
/etc/openinfra/agent.env(mode 0600) withINVITE_TOKEN,SOLANA_PAYOUT_PUBKEY,CONTROL_PLANE,DATA_DIR - Drops a hardened systemd unit (
ProtectSystem=strict,ReadWritePaths=/var/lib/openinfra,NoNewPrivileges=yes) - Enables and starts
openinfra-agent.service
At the end it prints your WireGuard public key. Send it to the
operator so they can add a [Peer] entry on the CP’s wg0.
4. Operator side — add the WG peer
Section titled “4. Operator side — add the WG peer”On the CP:
sudo wg set wg0 \ peer <provider-wg-pubkey> \ allowed-ips 10.77.0.42/32# persist across rebootssudo wg-quick save wg0Within seconds the provider’s agent should complete its first
Heartbeat and the host row in postgres flips to online.
5. Verification
Section titled “5. Verification”On the provider host:
sudo systemctl status openinfra-agentsudo journalctl -u openinfra-agent -f --since "5 min ago"sudo wg show openinfraExpected log lines (Block K — host_id persistence):
INFO agent starting control_plane=10.77.0.1:9092 ...INFO connected to control plane addr=10.77.0.1:9092INFO registered with control plane host_id=<uuid> onchain_payout=trueINFO agent running, sending heartbeats every 30sOn a subsequent restart you’ll see the host_id reused (no fresh
invite needed):
INFO resuming with persisted host_id (skipping Register) host_id=<uuid>On the CP:
curl -sS https://cp.coledex.com/api/v1/admin/hosts?limit=100 \ -H "Authorization: Bearer $ADMIN_JWT" | jq '.hosts[] | select(.hostname=="...")'6. Recovery
Section titled “6. Recovery”| Symptom | Action |
|---|---|
| WG handshake never completes | check operator added your pubkey to wg0; check cp.coledex.com:51820/UDP is open on the CP host |
| Agent logs “invite already used” | the host_id file is gone or was never persisted — operator must mint a fresh invite; the old one is dead |
| LUKS unlock prompt loops on boot | the disk wasn’t found — boot will eventually proceed thanks to nofail, then look at journalctl -u systemd-cryptsetup@openinfra_data |
| Agent runs but workloads never assign | check the provider’s payout pubkey matches what the operator registered with the invite — mismatched pubkeys land in solana:<pubkey> namespaces the CP doesn’t know about |
7. What this onboarding flow does not do (yet)
Section titled “7. What this onboarding flow does not do (yet)”- Automatic WG peer registration: the provider’s pubkey is printed to the operator and added manually. The proto-level Register-with-pubkey-exchange path is on the platform roadmap (ARCHITECTURE.md “WG outbound” follow-up).
- Auto-update of the agent: scoped under ARCHITECTURE.md → “Agent self-update mechanism (Post-beta)”.
- Multi-user / shared-machine setups: the agent runs as root. Hardening for shared hosts (per-workload user namespaces) is Phase 6 work.