# Automated fleet onboarding

One write-scoped account key, zero per-host secrets. The `glassmkr-crucible enroll` subcommand (Crucible 0.13.21 and later) lets every host self-register, receive its own collector key, and start reporting, with no dashboard clicks in the loop.

## Overview

The single-server flow in [Getting started](/docs/getting-started) creates the server in the dashboard first (to get a collector key), then installs the agent with that key. That is a manual step per host.

For a fleet, `enroll` collapses those two steps into one command that runs on the host itself. You bake a single `write`-scoped account key (`gmk_acct_live_`) into your automation. Each host runs `enroll` once; the command registers the server with the Dashboard, receives that server's own collector key (`gmk_cru_live_`), writes the agent config, and starts the service.

> **Prefer the raw API?** `enroll` is a convenience wrapper over the same endpoints documented in the [Programmatic API quickstart](/docs/programmatic-api) (`POST /api/v1/servers` plus the install). Use the API directly when your control plane, not the host, decides identity and holds the collector keys.

## How enroll works

A single `enroll` run does four things on the host:

1. **Derives a stable machine ID.** It reads the DMI product UUID (`/sys/class/dmi/id/product_uuid`), falling back to `/etc/machine-id` where DMI is unavailable (many VMs and containers). This ID is the host's durable identity.
2. **Registers the server.** It calls the Dashboard with your account key, creating a server keyed by that machine ID (or re-attaching to the existing one), and applies any `--tags`.
3. **Writes only the collector key.** The Dashboard returns a per-server collector key (`gmk_cru_live_`). `enroll` writes that key (and only that key) to `/etc/glassmkr/crucible.yaml` (mode 0600), then installs and starts the `glassmkr-crucible` systemd unit, the same on-disk result as `glassmkr-crucible init`.
4. **Discards the account key.** The account key is used for the one registration call and is never written to disk. See [Where the keys live](#keys).

By default `enroll` verifies connectivity to the Dashboard after registering (the same check `init` runs). Pass `--no-verify` to skip that round-trip so the command returns immediately; the agent still verifies on its first snapshot a minute later. That is the right choice inside a boot sequence, where the network may not be fully up yet (see [cloud-init](#cloud-init)).

## Prerequisites

- A `write`-scoped account key (`gmk_acct_live_`), minted once. See [Programmatic API](/docs/programmatic-api) for how to create one (it needs a step-up password re-auth). Store it in your automation's secret store, not in a machine image.
- Crucible 0.13.21 or later on the host (the release that introduced `enroll`). Installed globally with `npm install -g @glassmkr/crucible`, which needs Node.js 24+.
- Outbound HTTPS on port 443 to `app.glassmkr.com`. No inbound ports.
- Node-quota headroom: each enrolled host consumes one node against your plan quota (3 on Free, your subscribed count on Pro).

## Ansible

Install the agent, then enroll. The `creates:` guard makes the enroll task a no-op on hosts that already have `/etc/glassmkr/crucible.yaml`, so re-running the playbook is safe and cheap.

```
- name: Install the Crucible agent globally
  community.general.npm:
    name: "@glassmkr/crucible"
    global: true
    state: present

- name: Enroll this host into Glassmkr (idempotent)
  ansible.builtin.command:
    cmd: glassmkr-crucible enroll --account-key "{{ vault_glassmkr_account_key }}" --tags "{{ group_names | join(',') }}"
  args:
    creates: /etc/glassmkr/crucible.yaml
  no_log: true
```

- `vault_glassmkr_account_key` is your `gmk_acct_live_` key, held in Ansible Vault. It is passed for the single registration call and never written to a file on the host.
- `group_names | join(',')` tags each host with the inventory groups it belongs to, so the dashboard grouping mirrors your inventory.
- `no_log: true` keeps the key out of Ansible's task output and logs.

## cloud-init

Enroll from `runcmd` so a host registers itself the first time it boots:

```
#cloud-config
runcmd:
  - npm install -g @glassmkr/crucible
  - glassmkr-crucible enroll --account-key "gmk_acct_live_..." --no-verify
```

`--no-verify` keeps enrollment from blocking on a Dashboard round-trip while the network is still coming up during boot. The agent verifies on its first push.

The `npm install` line assumes Node.js 24+ is already on the image. If it is not, install it first (a preceding `runcmd` step, or a base image with Node bundled). And treat cloud-init user-data as sensitive: it often stays readable via the instance metadata service, so pull the account key from a secret manager in `runcmd` rather than hardcoding it wherever your platform supports that.

## Post-install one-liner

Drop this at the end of any post-install, kickstart, preseed, or provisioning script:

```
sudo npm install -g @glassmkr/crucible && \
  sudo glassmkr-crucible enroll --account-key "gmk_acct_live_..." --tags "prod,web"
```

Same effect as the Ansible and cloud-init flows: install the agent, self-register by machine ID, write the collector key, and start the service.

## Idempotent by machine ID

Because the server is keyed by the host's stable machine ID, running `enroll` more than once is safe:

- **Re-runs re-attach, they do not duplicate.** A second `enroll` on an already-enrolled host maps back to the same server record. With the Ansible `creates:` guard, the command is skipped outright once `/etc/glassmkr/crucible.yaml` exists.
- **Re-images map back to the same server.** Rebuild or re-image a host that keeps the same machine identity and it re-attaches to its existing server, preserving history and alert state instead of creating a new node (and consuming another quota slot).
- **Keep machine identity unique per host.** Hosts that share a machine ID would collapse onto one server record. When you clone from a golden image, make sure each instance gets a distinct identity: regenerate `/etc/machine-id` on first boot (cloud-init does this by default) so hosts with no distinct DMI UUID do not collide.

## Where the keys live

The security property of `enroll` is that the powerful credential never touches the host disk. Only a narrowly-scoped, per-host credential does.

| Key | Prefix | Where it lives in this flow | Scope |
| --- | --- | --- | --- |
| **Account key** | `gmk_acct_live_` | Your automation's secret store only (Ansible Vault, a secret manager, a CI secret). Never written to disk by `enroll`. | Account-wide: can create servers across the account. Keep it in the control plane. |
| **Collector key** | `gmk_cru_live_` | `/etc/glassmkr/crucible.yaml` on the host (mode 0600, readable by the non-root `glassmkr` user). | One server's telemetry only. Cannot list other servers, create servers, or read account settings. |

So a compromised host leaks only its own collector key, scoped to that one server's telemetry, not the account key that can enroll the rest of your fleet. If a collector key is exposed, rotate it with `POST /api/v1/servers/{id}/rotate-key`; if the account key is exposed, revoke it from Settings or `DELETE /api/v1/account/keys/{id}`.

Last updated: 2026-07-14. `enroll` was introduced in Crucible 0.13.21.

## Next steps

- [Getting started](/docs/getting-started): the single-server flow, and the two kinds of keys explained.
- [Programmatic API](/docs/programmatic-api): the raw endpoints behind `enroll`, plus a 50-server provisioning loop, rate limits, and the audit log.
- [Configuration reference](/docs/configuration): everything you can set in `/etc/glassmkr/crucible.yaml`.
- [Alert rules](/docs/rules): the 65 rules the Dashboard evaluates on every snapshot.
