Create a Pairing Code
You are on the Core. actana pair new mints a one-time code for one client and prints everything that client needs.
actana pair new --label laptop
# compose:
docker compose exec core actana pair new
The handout
Since 0.4.3, at a terminal the command answers with a framed handout — the code in bold, the expiry, the full CA fingerprint (wrapped, never truncated), the session id, and your label — followed by what to do with it:
┌────────────────────────────────────────────────────────────────────────┐
│ │
│ Pairing code │
│ │
│ XXXX-XXXX │
│ │
│ Expires 2026-09-01 14:05 (in 5 minutes) │
│ │
│ CA fingerprint │
│ AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD: │
│ EE:FF:00:11:22:33:44:55:66:77:88:99 │
│ │
│ Session 3f2a9c1e-… │
│ Label laptop │
│ │
└────────────────────────────────────────────────────────────────────────┘
From the Panel
Settings (gear icon) -> Cores -> Add a Core: this Core's address, then
compare the CA fingerprint, then the session and the code
From a terminal
npm i -g @actana/cli
# dial 192.168.1.50:8443 — and that is the endpoint this code registers
actana core pair laptop 192.168.1.50:8443 XXXX-XXXX --session 3f2a9c1e-… --fingerprint AA:BB:…
The From a terminal block prints one pasteable actana core pair command per address in the Core's ACTANA_PUBLIC_HOST list, with every minted value already in it. The name slot is your --label when the client's registry would accept it, and a literal NAME placeholder otherwise — swap it for what the client machine should call this Core. Each command's comment says which endpoint the credential will register, because that comes from the code, not from the address dialled: a code minted with --public-host <addr> registers that address; without it, the Core's primary. --public-host selects from the configured list and can never extend it.
Down a pipe the output is the plain labelled lines (Pairing code, CA fingerprint, Expires, Session, Label), byte-identical to what earlier releases printed — they are a screen-scraping contract, and there is deliberately no --json for this command. NO_COLOR is honoured at a terminal.
What the code is
Eight characters from ABCDEFGHJKMNPQRSTUVWXYZ23456789, grouped XXXX-XXXX. The alphabet drops 0, O, 1, I, and L so a code survives being spoken. Hyphen and case do not matter when the client types it; out-of-alphabet characters do — they are not folded into a neighbour.
The code is printed once and never stored. The Core keeps only a keyed digest, so nothing — including actana pair ls — can print it again. A lost code is re-minted, not recovered.
| Rule | Default |
|---|---|
| TTL | 5 minutes (--ttl 30s, 5m, or 2h; a unit is required; ceiling 24 hours) |
| Wrong guesses | 5, then the session is dead |
| Use | Single. Nothing accepts it twice. |
| Session id | Travels with the code. The Core will not search for a session a code might fit. |
--ttl without a unit is refused: a bare 5 would be seconds or minutes depending on a guess, and neither failure announces itself. --label is a flag, not a positional — actana pair new laptop is a usage error, not an unnamed code you later cannot revoke.
The code is not a credential. It authorises exactly one certificate issuance; after expiry, the attempt cap, or a successful redeem it is worthless. The private key is generated on the client; this command never prints one.
Setup first
Pairing signs against this Core's CA. If there is no material yet:
actana pair new: this Core has no pairing material at ...
Run `actana setup` — pairing needs a CA to sign against.
Inside a container the same command works through the image's actana: docker compose exec core actana pair new.
List
actana pair ls
Pending codes (still redeemable) and paired clients: labels, session ids, certificate serials, status. --json for scripts. The pairing code itself is not in this list — the Core never stored it.
Revoke one
actana pair revoke <serial|session|label>
A serial matches on a prefix. A label matches in full, so laptop does not steal laptop-2. If the target matches more than one row, the command refuses rather than guessing — name the serial or the session id.
Pointed at a paired client it unpairs that machine: the certificate and the bearer stop working, including any core-link already open. Pointed at a pending code it cancels the code before anyone spends it. A session already redeemed is not a pending code; revoke the client instead.
Rotate the CA
actana token regenerate
Fresh material: new CA, new certs, new bearer secret, new core id. Every credential this Core ever issued stops working. On metal the daemon restarts as part of the command; in a container, restart it yourself (docker compose restart) before you treat the old identity as gone.
Then pair every client again: actana pair new here, spend the code there.
Panel: remove the Core first (Settings → Cores → Remove Core). Rotation does not move the endpoint, so Add a Core refuses an address it already holds — and refuses before spending the code. Forget that step and you mint a code you then cannot use.
CLI: actana core pair replaces the stored credential in place. No extra remove.
pair revoke is the narrower tool when one laptop should lose access and the rest should not.