One account key, zero per-host secrets
There is a quiet compromise in how most fleets get onboarded into a monitoring tool: you mint one credential and copy it onto every box. It works, and it costs you twice. A credential powerful enough to enroll servers now sits on every server, so any single compromised host leaks something that can reach the whole account. And you have to track which host got which secret, so the moment a machine is re-imaged you get drift, duplicates, and a node count that no longer matches reality. We wanted the opposite: each host ends up holding only a credential scoped to itself, and running the same automation twice does nothing.
That is what the glassmkr-crucible enroll subcommand (Crucible 0.13.21 and later) is for. It is the answer to the practical question every operator asks on day one: what do I drop into my Ansible role, my cloud-init, or my post-install script so a host shows up in monitoring by itself?
What enroll does
You bake a single write-scoped account key (gmk_acct_live_) into your automation, held in a vault or secret manager. Each host runs enroll once. On that one run it:
- Derives a stable machine ID. The DMI product UUID (
/sys/class/dmi/id/product_uuid), falling back to/etc/machine-idwhere DMI is unavailable (many VMs and containers). This is the host's durable identity. - Registers itself with the Dashboard under that ID, or re-attaches to the server that ID already maps to, and applies any tags you pass.
- Receives its own per-server collector key (
gmk_cru_live_), writes that key (and only that key) to/etc/glassmkr/crucible.yamlat mode 0600, then installs and starts the agent.
The account key is used only for that single registration call: enroll itself never writes it to disk. What enroll leaves on the box is the collector key, and nothing else. How your automation hands enroll the account key is a separate question, and the examples below keep it out of the image.
Idempotent by machine ID
Because the server record is keyed by the host's stable machine ID, re-running enroll is safe by construction. A second run on an already-enrolled host maps back to the same server instead of creating a duplicate. Re-image or rebuild a host that keeps the same machine identity and it re-attaches to its existing server, so its history and alert state survive the rebuild rather than starting over as a fresh node and eating another quota slot.
In Ansible this gets even cheaper. An args: creates: /etc/glassmkr/crucible.yaml guard makes the enroll task skip outright on any host that is already enrolled, so a playbook you run nightly does no work on steady-state hosts.
The one thing to watch is cloned images that share a machine ID: they would collapse onto a single server record. Give each instance a distinct machine identity, which cloud-init already does by regenerating /etc/machine-id on first boot.
The security property: the strong key stays in the control plane
Two keys, very different power. The account key enroll needs is write-scoped: it can create servers (that is what enroll uses), and because the scopes are hierarchical it can also list every server in the account, read all of their telemetry and alerts, and update or delete any of them. Only rotating a collector key needs a higher admin scope. That account key lives only in your secret store. The per-server collector key can do exactly one thing: push that one server's telemetry. It is rejected from every account-management endpoint, so it cannot list, create, delete, or read any other server.
So the blast radius of a compromised host is that host: a credential that can push one server's telemetry, not the key that could enumerate, create, or delete the rest of your fleet. The powerful credential never leaves your control plane.
The practical hygiene follows from that. Keep the account key in Ansible Vault or a secret manager. Set no_log: true on the enroll task so it stays out of Ansible's output. Treat cloud-init user-data as sensitive, since many clouds expose it through the instance metadata service. One honest caveat: the account key is passed as a process argument during that single enroll call, so it is not persisted to disk but it is briefly visible to local process listing. The guidance is a secret manager plus no_log, not a claim of invisibility.
Copy-paste
Ansible, install then enroll, idempotent via the creates: guard:
- 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 cloud-init, self-register on first boot. Do not embed the account key in user-data: user-data is readable through the instance metadata service and persists on the instance. Fetch it from your secret manager at boot instead:
#cloud-config
runcmd:
- npm install -g @glassmkr/crucible
# Replace `your-secret-manager get` with your platform's secret fetch
# (aws ssm get-parameter, gcloud secrets versions access, vault kv get, ...).
- 'ACCT_KEY="$(your-secret-manager get glassmkr/account-key)"; glassmkr-crucible enroll --account-key "$ACCT_KEY" --no-verify' Or at the end of any post-install, kickstart, or preseed script. Source the key from your secret store, not a literal in the script: a literal persists on disk and in provisioning logs.
ACCT_KEY="$(your-secret-manager get glassmkr/account-key)"
sudo npm install -g @glassmkr/crucible && \
sudo glassmkr-crucible enroll --account-key "$ACCT_KEY" --tags "prod,web" --no-verify skips the post-registration connectivity check so enroll does not block while the network is still coming up during boot; the agent verifies on its first snapshot a minute later. Installing the agent needs Node.js 24 or later on the host.
Updating the fleet
Onboarding is the first half; keeping the agent current is the other, and it has the same shape. The agent is a global npm package behind a systemd unit, so an update is two commands, and the collector key already on disk is left alone: you are swapping the binary, not re-enrolling.
sudo npm install -g @glassmkr/crucible@<version> && \
sudo systemctl restart glassmkr-crucible Pin an explicit version rather than a floating tag, so every host lands on the same build and a release published mid-rollout cannot split your fleet. Across a fleet it is the Ansible task you already have, with a version pin that makes a re-run on an already-current host a no-op:
- name: Update Crucible to a pinned version
community.general.npm:
name: "@glassmkr/crucible"
version: "<version>"
global: true
state: present
notify: restart crucible
# handlers/main.yml
- name: restart crucible
ansible.builtin.systemd:
name: glassmkr-crucible
state: restarted Without a config-management tool, a fan-out over your inventory does the same thing. Use ssh -n so ssh does not swallow the loop's stdin and stop after the first host:
while read -r host; do
ssh -n "$host" 'sudo npm install -g @glassmkr/crucible@<version> && sudo systemctl restart glassmkr-crucible'
done < hosts.txt Each server reports its collector version to the Dashboard, so after a rollout you can confirm the whole fleet actually moved rather than assume it did. One caveat: a release that adds a new privileged collector action says so in its notes, and hosts running the agent as the unprivileged service user need a one-time glassmkr-crucible init re-run (or wrapper refresh) to grant the new read.
Where this sits
enroll is a convenience wrapper over the documented public API (POST /api/v1/servers plus the agent install), not a new or private surface. Use the raw API directly when your control plane, not the host, should decide identity and hold the collector keys; that version, with a 50-server provisioning loop, is in the Programmatic API guide. When the host should register itself, use enroll. The full how-to, with the Ansible and cloud-init examples above plus the key-lifecycle details, is at Automated fleet onboarding.
Node quota still applies: each enrolled host consumes one node against your plan, with the first three free. The point of enroll is not a pricing change. It is that the credential your hosts hold should be the weakest one that does the job, and that onboarding a fleet should be something you can run twice without thinking about it.