Skip to content

Enroll hosts

A host becomes managed in two steps: it is registered in Praxis, which creates its record and its credential binding, and it is reachable over at least one transport.

Decide the transport first. Transports states what each one supports; the short version is that SSH is the default and is required for interactive terminal sessions, and the agent exists for hosts the control plane cannot reach inbound.

Register a host over SSH

Open Operate > Register System and provide:

  • Hostname that resolves from the control plane, or an IP address.
  • IP address, unique across the fleet.
  • Distribution and version, from the list.
  • Group. Every host belongs to exactly one static group.
  • Credential, the account Praxis connects as.

Praxis runs a connectivity test as part of registration. A failure is shown inline with the SSH error; fix the credential or name resolution and retry rather than saving a host you cannot reach.

After registration the host appears under Operate > All Systems and the first inventory scan begins.

Add many hosts

Use the bulk registration form or a CSV import for the rest of the fleet. Register a handful first and confirm they are reachable and inventoried before importing the whole estate; a credential or escalation mistake is much cheaper to find on three hosts than on three hundred.

Deploy certificate-based identity

Once a host is reachable you can stop using passwords for it. On the host’s detail page choose Deploy CA Trust. Praxis installs the signing CA public key, adds TrustedUserCAKeys to the SSH daemon configuration, and reloads it. From then on each connection is authorised by a freshly signed short-lived certificate. Password authentication keeps working as a fallback.

See SSH and security for rotation and revocation.

Enroll a host with the thin agent

The agent is a single static Go binary that dials out to the broker over a long-lived mTLS WebSocket. Use it for hosts behind NAT or a firewall that the control plane cannot reach.

Install the binary

Download the release tarball for the host architecture and verify it before extracting. Then:

Terminal window
tar xzf praxis-agent-*.tar.gz
cd praxis-agent-*-linux-*
sudo ./install.sh \
--broker-url wss://broker.example.com:8443 \
--backend-url https://praxis.example.com \
--system-id 42

--system-id is the ID of a host you have already registered in Praxis. Install does not start the service and does not create identity material.

Give the host an identity

The agent’s identity is minted by the backend, not supplied by the host, so trust comes from how the certificate request is authorised. There are two paths.

Activation token. An administrator mints a single-use, scoped, time-limited token under Settings > Activation Tokens. On the host, generate a key and request, fetch the CA bundle, redeem the token, and install the returned certificate:

Terminal window
sudo praxis-agent gen-keypair
sudo praxis-agent gen-csr > agent.csr
curl -fsS https://praxis.example.com/agent/ca-bundle -o ca-bundle.json
curl -fsS -X POST https://praxis.example.com/agent/enroll \
-H "X-Praxis-Activation-Token: praxis_XXXXXXXX..." \
-H "Content-Type: application/json" \
-d "{\"system_id\": 42,
\"host_fingerprint\": \"$(cat /etc/machine-id)\",
\"csr_pem\": $(jq -Rs . < agent.csr),
\"hostname\": \"$(hostname -f)\"}" \
> enroll-response.json
jq -r .certificate enroll-response.json > agent.crt
sudo praxis-agent install-cert \
--cert agent.crt \
--bundle ca-bundle.json \
--backend-url https://praxis.example.com \
--broker-url wss://broker.example.com:8443 \
--system-id 42

Sending host_fingerprint makes redemption idempotent for that host, so a re-run does not consume a second use of the token.

Bootstrap over SSH. For a host Praxis can already reach, an administrator posts the certificate request to POST /agent/bootstrap/{system_id}. The backend opens an SSH session to the host as proof of identity and returns the same signed certificate, with no activation token involved. Feed that certificate into the same install-cert step.

install-cert checks the certificate against the local private key before it writes anything, then writes the certificate and both CAs transactionally.

Start it

Terminal window
sudo systemctl enable --now praxis-agent
sudo systemctl status praxis-agent

Confirm from the control plane that the tunnel is up: the host reports agent_status: active and agent_liveness: online once the agent has dialled the broker and completed the handshake.

What lands on the host

PathPurpose
/usr/local/bin/praxis-agentThe binary.
/etc/praxis-agent/config.jsonBroker URL, backend URL, system ID.
/etc/praxis-agent/agent.keyPrivate key, mode 0600.
/etc/praxis-agent/agent.crtAgent certificate.
/etc/praxis-agent/broker-ca.crtBroker CA bundle.
/etc/systemd/system/praxis-agent.serviceThe service unit.

Updating the agent

Updates are operator-triggered; the agent never updates itself. Verify and extract the new tarball, then run sudo ./install.sh over the existing deployment. Configuration and identity material are preserved, so an update does not re-enroll the host and shows up as a brief liveness gap rather than a new enrollment. Rolling back is the same operation against the older tarball.

Confirm enrollment worked

For any host, whichever transport it uses:

  1. Operate > All Systems shows it as Active.
  2. Its detail page lists packages after the first inventory scan.
  3. Last audited advances after the next health check.

A host that registers but never inventories is usually an escalation problem rather than a connectivity one. Work through troubleshooting.

Next