ServerKit supports two ways to attach an agent to the panel:
- Pair (recommended) — install the agent once, then claim it from the panel with a 6-character rotating code + your operator passphrase. RustDesk-style.
- Token install (legacy) — generate a registration token in the panel, paste it into a curl/PowerShell one-liner. Still supported.
This document covers the pair flow.
The pre-shared registration token approach has several downsides:
- The token is a long-lived secret embedded in a copy-pasted shell command.
- If anyone observes the install command, they can pre-empt the registration.
- Re-pairing after a wipe means generating a fresh token, repeating the dance.
Pairing replaces this with:
- A per-machine passphrase the operator sets once on the agent host.
- A rotating 6-character pair code the agent displays (CLI banner / tray).
- A public-key fingerprint that's verifiable out-of-band.
The panel claims the agent only after both the code and passphrase match.
After installing the agent (e.g. via the install script, package, or MSI), run:
serverkit-agent pair --server https://panel.example.comYou'll be prompted for a passphrase (4+ characters). Pick something memorable — you only need to type it once on the panel side. The agent then:
- Generates an Ed25519 keypair (encrypted at rest with the machine key).
- Submits its public key + bcrypt'd passphrase to the panel.
- Prints a banner with the rotating 6-char pair code and the public-key fingerprint:
┌──────────────────────────────────────────────┐
│ ServerKit Agent — Pairing │
├──────────────────────────────────────────────┤
│ Pair code: ABC-123 │
│ Fingerprint: 1A2B3C4D5E6F7A8B │
├──────────────────────────────────────────────┤
│ 1. Open ServerKit panel → Add Server │
│ 2. Enter the pair code + your passphrase │
│ 3. Verify the fingerprint matches │
└──────────────────────────────────────────────┘
Waiting for claim...
The pair code rotates every ~5 minutes until the agent is claimed.
Go to Servers → Add Server and select Pair existing agent. Enter:
- The 6-character pair code
- The passphrase you set
- (Optional) A friendly name and group
Verify the fingerprint shown after lookup matches what the agent printed.
Click Pair Agent. The agent receives credentials over its existing
long-poll, saves them, and prints ✓ Pairing successful!.
sudo systemctl start serverkit-agent # Linux
# or use Services.msc on WindowsThe agent connects to the panel and the server flips from "Pending" to "Online".
For Docker, Kubernetes, or unattended provisioning:
SERVERKIT_AGENT_PASSPHRASE='hunter2' \
serverkit-agent pair --server https://panel.example.com --headlessYou still need an operator to claim the code in the panel — the headless flag just suppresses interactive prompts.
If you're testing the panel locally, expose it via ngrok and set:
export SERVERKIT_PUBLIC_URL=https://abcd1234.ngrok.appUse that URL as the --server flag. For self-signed TLS in dev,
SERVERKIT_INSECURE_TLS=true skips cert verification on the agent side
(don't use this in production).
- Pair codes are 6 characters from a 31-symbol alphabet (Crockford-ish base32 minus 0/O/1/I/L). Codes rotate ~every 5 minutes.
- Passphrases are bcrypt'd on the panel before storage.
- Failed claim attempts trigger per-enrollment exponential backoff (60s → 5 min cap) plus per-IP rate limits (5 claims / 10 minutes).
- All claim/lookup events are written to the audit log.
- Enrollments expire 24h after creation; un-claimed records are pruned hourly.
- The agent's keypair is encrypted at rest with a host-stable key derived
from
/etc/machine-id+ hostname (Linux) or hostname + COMPUTERNAME (Windows). The key is intentionally independent of the logged-in user so the Windows service (running asLocalSystem) can decrypt credentials written during user-context pairing — mixing in the Windows username broke this and was removed in agent 1.6.14 (a legacy USERNAME-based key is retained only for one-time migration). Treat the stored key file as a host-equivalent secret; see ../SECURITY.md for the full agent trust model. - Compare the displayed fingerprint side-by-side before claiming. Mismatch
means the wrong agent — abort and re-run
pair.
Treat the stored credential/key file as a host-equivalent secret. Combined with the agent's remote-execution capability, recovering it is equivalent to controlling the host. See ../SECURITY.md for the full agent trust model.
| Endpoint | Purpose | Auth |
|---|---|---|
POST /api/v1/pairing/enroll |
Agent submits pubkey + passphrase | none |
POST /api/v1/pairing/code/refresh |
Agent rotates its pair code | enrollment headers |
POST /api/v1/pairing/code/freeze |
Agent freezes/unfreezes rotation | enrollment headers |
GET /api/v1/pairing/poll |
Agent long-polls for claim (≤25s) | enrollment headers |
POST /api/v1/pairing/lookup |
Panel resolves code → fingerprint | JWT |
POST /api/v1/pairing/claim |
Panel claims an enrolled agent | JWT |
Headers used by the agent:
X-Enrollment-Id: opaque enrollment identifierX-Enrollment-Secret: long-lived secret returned at enroll time
| Pair | Token install | |
|---|---|---|
| Out-of-band step | passphrase typed into panel | token pasted into shell |
| Compromise window | 5 min (rotating code) | 24h (token TTL) |
| Re-pair after wipe | re-run pair |
regenerate token |
| Headless friendly | yes (--headless + env) |
yes (curl one-liner) |
| Recommended? | ✅ | only for legacy/scripted flows |