Overalls.

An AI-first developer platform that you own.

Guide Self-host
Design Architecture
Source GitHub · MIT Appearance

Provision a host

The bootstrap tool, nix run .#host, turns a rented server in rescue mode into a NixOS host that installs from a reviewed plan and then updates itself. It never trusts a new SSH key automatically and never formats a disk you haven't approved.

Commands
discover · plan · install · enroll · verify
Inventory
fleet/hosts/NAME/host.json, plus an optional configuration.nix for roles

1. Discover

Get the rescue system's Ed25519 host key through a trusted channel and put it in its own known-hosts file. Then record what the machine has:

nix run .#host -- discover --platform hetzner --name example \
  --target root@HOST --known-hosts /private/rescue-known-hosts \
  --identity /private/operator-key --output /private/example.candidate.json

Use --platform ec2 for EC2 bare metal. Discovery won't overwrite an existing file. It suggests a network configuration (DHCP on EC2, interface addresses on Hetzner) for you to review.

2. Review and declare

Copy the candidate to fleet/hosts/NAME/host.json and finish it by hand:

  • Set reviewed to true once you've checked the hardware, firmware and networking.
  • Choose storage.system by disk ID: two physical disks on Hetzner, or the existing root EBS volume on EC2. On EC2, list the local NVMe IDs in storage.guests.
  • Give identity.hostId a persistent, nonzero eight-digit hex value. Leave identity.sshKeys empty for now.
  • Set budgets.guestCpuCores, guestMemoryMiB and arcMaxMiB, and on Hetzner a reviewed storage.quotaBytes.
  • Add your Git server's known-host lines to git.knownHosts. Private keys never go in the declaration.

If the host will run Lilly, declare that role now (see enroll hosts), then commit and push to main. Planning only accepts a commit that has been pushed to main.

3. Plan

nix run .#host -- plan --host example --revision FULL_COMMIT \
  --target root@HOST --known-hosts /private/rescue-known-hosts \
  --identity /private/operator-key --output /private/example.plan.json

The plan pins the declaration, the authenticated target key, the firmware, the revision, the exact system, disk-layout and kexec paths, and the list of disks it will erase. Read it. Everything is built before anything destructive happens.

4. Install

nix run .#host -- install --plan /private/example.plan.json --erase PLAN_SHA256 \
  --target root@HOST --known-hosts /private/rescue-known-hosts \
  --identity /private/operator-key

The installer kexecs into a pinned image, compares the disk inventory again and formats under a lock. It refuses if a device is mounted or a pool is imported. The new host key is generated on the host; only its public half comes back, recorded in PLAN.installed.json. Keep that file. An interrupted install never resumes formatting silently.

5. Enroll and verify

Record the installed public key in identity.sshKeys and in a new known-hosts file. Add the Git credential reference secrets/NAME-git.enc.json to credentials, then prepare the encrypted Git bundle. Use your own account name from nix/users.nix in place of YOU:

nix run .#host -- enroll --host example --deploy-key /private/shared-git-key \
  --root . --target YOU@HOST --known-hosts /private/installed-known-hosts \
  --identity /private/operator-key

Push the updated declaration and ciphertext, then activate that exact commit and check it:

nix run .#host -- enroll --host example --revision ENROLLED_FULL_COMMIT \
  --install-receipt /private/example.plan.json.installed.json \
  --target YOU@HOST --known-hosts /private/installed-known-hosts \
  --identity /private/operator-key
nix run .#host -- verify --target YOU@HOST \
  --known-hosts /private/installed-known-hosts --identity /private/operator-key

From here the host polls main every ten seconds, with jitter. Changes to the platform, firmware, disks or identity are rejected by ordinary updates; those need deliberate maintenance.

Full reference: docs/host-provisioning.md