> ## 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.

# Quickstart

> Stand up a control plane, enroll one host, and gate a real SSH login through Custos.

End to end this is: run the control plane, enroll a host, register an SSH key, grant `host.access`.
Each step links to its full reference.

Custos ships a web app alongside the API, and everything here that is not a shell command on a
server — creating the enrollment token, registering a key, granting access — can be done there
instead of with `curl`. This page uses `curl` so each step is unambiguous about what is actually
being asked of the control plane.

<Steps>
  <Step title="Install the control plane">
    Download the latest Linux amd64 control-plane binary:

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

    See [Control plane install](/custos/custos/control-plane/install) for the full procedure.
  </Step>

  <Step title="Generate keys and set the environment">
    ```bash theme={null}
    custoscp gen-keys
    ```

    This prints `CUSTOS_MASTER_KEY`, `CUSTOS_SIGNING_PRIVATE_KEY`, and `CUSTOS_SERVER_TRANSPORT_PRIVATE_KEY`, plus
    `CUSTOS_SERVER_TRANSPORT_PUBLIC_KEY` to configure on clients. Supply those, along with
    `CUSTOS_DATABASE_URL` and `CUSTOS_LISTEN_ADDR`, through your shell, service manager, or deployment
    platform — Custos does not read `.env` files itself. Full list in
    [Configuration](/custos/custos/control-plane/configuration).
  </Step>

  <Step title="Migrate, seed an admin, and serve">
    ```bash theme={null}
    custoscp migrate up
    custoscp create-admin --email you@example.com
    custoscp serve
    ```

    `migrate up` must run before `create-admin`. `create-admin` prints a generated password if you did
    not pass `--password`.
  </Step>

  <Step title="Create an enrollment token">
    Do this in the web app, or against the API directly. Either way you are creating the same
    single-use token carrying the host's managed Unix accounts:

    ```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`. Accounts are declared here and stamped into every snapshot the host
    receives.
  </Step>

  <Step title="Install the daemon on the host">
    ```bash theme={null}
    curl -fsSL https://custosd.tofunmiadewuyi.com/install.sh | sudo bash
    ```

    This runs as root once: it installs `/usr/local/bin/custosd`, creates the `custos` system user and
    `/var/lib/custos`, writes the systemd unit, and wires sshd's `AuthorizedKeysCommand`. Nothing talks
    to the control plane yet.

    Manual and pinned-version alternatives in [Host install](/custos/custos/hosts/install).
  </Step>

  <Step title="Enroll the host and start it">
    ```bash theme={null}
    sudo -u custos /usr/local/bin/custosd enroll \
      --control-plane "$CUSTOS_URL" \
      --token "$ENROLLMENT_TOKEN" \
      --dir /var/lib/custos

    sudo systemctl enable --now custosd
    ```

    Enrollment generates the host's Ed25519 identity key and X25519 encryption key and registers only
    the public halves.

    <Warning>
      Enroll as the `custos` user, not with plain `sudo custosd enroll`. Plain `sudo` writes root-owned
      state under `/var/lib/custos`, and the service runs as `custos`.
    </Warning>
  </Step>

  <Step title="Grant SSH access">
    Register the user's SSH public key with `POST /keys`, then grant them `host.access` on the host with
    `POST /grants` — or do both in the web app. `ssh` in: the login is now gated by Custos.

    If a connected daemon still reports `cached ssh keys: 0`, force a resync with
    `POST /hosts/{id}/refresh`. The response includes `key_count`; `0` means the control plane has no
    active `host.access` grant plus matching public key for that host. See
    [Grant SSH access](/custos/custos/hosts/grant-access).
  </Step>
</Steps>

## Next

* Deliver app secrets to the host with [machine secrets](/custos/custos/guides/machine-secrets).
* Run the control plane as a hardened service: [systemd](/custos/custos/control-plane/systemd).
* Wire up logs, traces, and metrics: [Observability](/custos/custos/operations/observability).

## Local development shortcut

Skip `install` and systemd entirely and run the daemon directly:

```bash theme={null}
custosd run --dir <dir> --socket <path> --secret-socket <path>
```

Set `CUSTOS_ENCRYPTION=off` to disable Custos's additional API payload encryption in local
development. This does not disable HTTPS/TLS or at-rest vault encryption.


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