> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tofunmiadewuyi.com/custos/llms.txt
> Use this file to discover all available pages before exploring further.

# Install and enroll a host

> Install custosd, wire sshd, and enroll the host with the control plane.

## 1. Create an enrollment token

As an admin, on the control plane, create a token carrying the Unix accounts this host will manage:

```bash theme={null}
curl -sS -X POST "$CUSTOS_URL/enroll-tokens" \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"label":"app-01","accounts":["deploy"],"ttl_hours":1}'
```

Save the returned `token`. It is single-use, consumed in the same transaction that creates the host.
The accounts you declare here are copied to the host on enroll and stamped into every snapshot it
receives.

## 2. Install the daemon

The installer is the normal path. It resolves the latest release, downloads the right
Linux/architecture tarball, installs `/usr/local/bin/custosd`, creates the `custos` system user,
creates `/var/lib/custos`, writes the systemd unit, and wires sshd's `AuthorizedKeysCommand`.

```bash theme={null}
curl -fsSL https://custosd.tofunmiadewuyi.com/install.sh | sudo bash
```

Pin a version:

```bash theme={null}
curl -fsSL https://custosd.tofunmiadewuyi.com/install.sh \
  | sudo CUSTOS_VERSION=v1.2.3 bash
```

### Self-hosted release artifacts

For a private source repo, publish only the daemon release artifacts to a public static base and
point both the installer and the control plane at it:

```bash theme={null}
curl -fsSL https://custosd.tofunmiadewuyi.com/install.sh \
  | sudo CUSTOS_RELEASE_BASE=https://your-host.example.com/releases/custosd bash
```

```bash theme={null}
export CUSTOS_RELEASE_BASE=https://your-host.example.com/releases/custosd   # control plane
```

The base must contain `latest.txt` plus versioned files such as
`v1.2.3/custosd_v1.2.3_linux_amd64.tar.gz` and `v1.2.3/custosd_v1.2.3_checksums.txt`. The daemon
release workflow bakes this base into `dist/install.sh` when the `CUSTOS_RELEASE_BASE` Actions
variable is set.

### Manual binary download

Downloading the binary alone is for manual testing and debugging. It is enough to run `enroll`, but
it configures neither sshd nor systemd.

```bash theme={null}
VERSION="$(curl -fsSL https://custosd.tofunmiadewuyi.com/releases/custosd/latest.txt)"
curl -fL -o custosd.tar.gz "https://custosd.tofunmiadewuyi.com/releases/custosd/${VERSION}/custosd_${VERSION}_linux_amd64.tar.gz"
tar -xzf custosd.tar.gz
sudo install -m 0755 ./custosd /usr/local/bin/custosd
```

If you already have a `custosd` binary on the host, run the installer step from it directly:

```bash theme={null}
sudo ./custosd install
```

<Note>
  sshd refuses an `AuthorizedKeysCommand` unless the binary and every parent directory are root-owned
  and not group- or world-writable. That is why the binary must live in `/usr/local/bin` and not, say,
  `~/bin`. The installer handles this; a hand-rolled layout has to respect it.
</Note>

## 3. Enroll

```bash theme={null}
sudo -u custos /usr/local/bin/custosd enroll \
  --control-plane "$CUSTOS_URL" \
  --token "$ENROLLMENT_TOKEN" \
  --dir /var/lib/custos
```

This generates the host's Ed25519 identity key and X25519 encryption key and registers their public
halves with the control plane. The private halves never leave the machine.

<Warning>
  Do not run this as plain `sudo custosd enroll`. That writes root-owned state under
  `/var/lib/custos`, and the service runs as `custos`. Always pass an absolute `--dir`: under `sudo`,
  `~` expands to the wrong home and the state ends up unreadable by the run user.
</Warning>

Custos enforces one active host per machine, keyed on a hash of `/etc/machine-id`. A second enroll
from the same machine is rejected with `409` until the existing host is revoked.

## 4. Start

```bash theme={null}
sudo systemctl enable --now custosd
sudo systemctl status custosd
sudo -u custos /usr/local/bin/custosd status --dir /var/lib/custos
```

If sshd was not wired automatically, the installer prints the drop-in config to create manually.
After adding it:

```bash theme={null}
sudo sshd -t && sudo systemctl reload ssh
```

Custos writes its drop-in to `/etc/ssh/sshd_config.d/70-custos.conf` and never edits the main sshd
config.

## Next

* [Grant SSH access](/custos/custos/hosts/grant-access) to make the host usable.
* [Machine secrets](/custos/custos/guides/machine-secrets) to deliver app secrets to it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.