Enroll hosts and images
A Lilly host only runs guests once Marshall knows about it. Enrolling registers the host as disabled, issues it a revocable credential encrypted to that host, and leaves it to you to enable scheduling.
- Needs
- An installed host with its receipt, and administrative SSH to both the host and a Marshall host
- Credential
- Five years by default, revocable at any time, rotated explicitly
1. Declare the Lilly role
Add this to the host's host.json before installing, so the base system includes the identity helper:
"lilly": {
"controlPlane": "https://marshall.example.com",
"egressInterface": "eth0",
"credentialTtlSeconds": 157680000,
"overheads": {"cores": 1, "memoryBytes": 1073741824, "storageBytes": 1073741824}
}
Host names for Lilly hosts can be at most 26 characters. Don't set Lilly host or pool IDs by hand; they're generated.
2. Prepare the enrollment
Deploy the Marshall release and its migration first. Then, from your checkout:
nix run .#host -- prepare-enrollment --host NAME --root . \ --target admin@installed-host --marshall-target admin@marshall-host \ --identity /private/admin-key --known-hosts /private/known-hosts \ --install-receipt /private/host-plan.installed.json \ --recovery-key /private/recovery-key
This registers a disabled host and writes the NAME-git and NAME-lilly ciphertext, plus public registration data in lilly.binding. It doesn't commit, push or enable anything. If it's interrupted, run the identical command again; its journal resumes the work without issuing a second credential.
3. Activate and enable
Push the changes and activate the commit with nix run .#host -- enroll --revision …, as in provisioning. Once the host reports ready, enable it from a Marshall host with the operator CLI:
bin/marshall eval 'Marshall.Operations.CLI.main()' show NAME bin/marshall eval 'Marshall.Operations.CLI.main()' enable HOST_UUID
Operator commands run as a separate process with PHX_SERVER=false, never through a serving node's console. They print only public identities.
4. Guest images
Images are Nix builds with immutable sha256: digests. The fleet catalog in fleet/guest-images.nix lists the images each host stages. Pre-created standby disks make creation fast for images marked standby = true:
[
{ name = "NixOS"; package = kittensPackages.guest; standby = true; }
{ name = "Ubuntu 26.04 LTS"; package = kittensPackages.ubuntuGuest; standby = true; }
{ name = "Windows Server 2025"; package = kittensPackages.windowsGuestAgents; standby = false; }
]
The Windows image isn't byte-identical from build to build, so build it once and nix copy it to every host before adding its entry. Build a customised Linux image with mkUbuntuGuest, or the NixOS guest builder with extraModules. Pin Ubuntu packages with nix run .#ubuntu-lock. Set services.marshall.defaultImageId to choose the default for new machines.
nix build .#ubuntu-guest
ls result/
# kernel initrd rootfs.ext4 manifest.json
5. Porsche, the SSH gateway
Porsche authenticates to Marshall with a gateway token that you prepare and configure using the operator CLI. Before you move Porsche onto port 22, move administrative SSH to another port (the reference fleet uses 2222).
bin/marshall eval 'Marshall.Operations.CLI.main()' gateway-prepare /private/porsche.token bin/marshall eval 'Marshall.Operations.CLI.main()' gateway-configure edge-one /private/porsche.token
Full reference: docs/provisioner-enrollment.md
Full reference: docs/ubuntu-guests.md
Full reference: docs/porsche-operations.md