Provisioning codes
A provisioning code is the one-time credential that pairs a new host with your tenant. You issue it from the dashboard and hand it to the host administrator. They redeem it once with chrlctl, the Churchill Host CLI. After one redemption it is spent.
What a provisioning code is
Section titled “What a provisioning code is”A code is one string that starts with chrl1-, followed by lowercase letters and digits in groups of six joined by dashes. For a typical control-plane hostname it is about 160 characters long:
chrl1-aeabky-3iovzg-g2djnr-wc4zly-mfwxa3-dffzrw-63nlce-hsn53i-xp3usa-pfhme4-zldxla-nwreys-jdebi3-u4mwzf-7q7zkb-qmcgjh-zidugn-5symas-wrrskc-2ynv4n-ekwgvb-q2a5fp-osa6d4It is long because it carries everything chrlctl needs to reach your control plane, not only a secret:
| Field | What it is |
|---|---|
| Control plane address | The hostname of your control plane’s bootstrap endpoint, and the port when it is not the default 62203. |
| Server key pin | A SHA-256 fingerprint of the public key your control plane presents on that endpoint. chrlctl refuses any server whose key does not match it. |
| Tenant ID | Your tenant. A code minted by one control plane is refused by every other one. |
| One-time secret | 80 random bits. This is the only secret in the code. The control plane keeps a SHA-256 digest of it and nothing else. |
| Checksum | Four bytes computed over everything above. One mistyped character fails it. |
How to handle one:
- Copy and paste it. The code is built to be pasted, not read aloud or retyped. If it is retyped anyway, the checksum catches a single wrong character:
chrlctlreportsprovisioning code: checksum mismatchbefore it opens any connection, and the code is not consumed. - Case, dashes, and whitespace do not matter.
chrlctlaccepts uppercase, strips the dashes, and ignores line breaks, so a code that wrapped in a terminal or an email still redeems. - It is tied to the control plane that minted it. The address and key pin inside the code point at one bootstrap endpoint. If that endpoint’s certificate is rotated, every unredeemed code stops working, because its pin no longer matches. Issue a new code.
Every code is:
- Single-use. It is redeemed exactly once, atomically. A second attempt is refused as already redeemed.
- Short-lived. It expires after the lifetime you pick at issue time, whether or not anyone used it.
- Scoped. It is bound to a tenant, an environment, and an IPv4 network. Redemption from outside the scope is refused.
- Shown exactly once. The dashboard displays the plaintext code at issue time and never again. Churchill keeps only a SHA-256 digest of the code’s secret, so nobody (including support) can read your code back later.
Issuing a code
Section titled “Issuing a code”Codes are issued from the registration wizard. See Registering an application for the full flow. The code is its first step.
- Open Protected apps and select + Register application. The wizard opens on the provisioning step.
- Fill in the scope: the tenant, the environment, and the source CIDR scope. The scope is the network the host must redeem from.
- Pick a code lifetime: 15 min, 1 hour, or 8 hours. 15 minutes is the default. You can also attach an optional change ticket reference.
- Select Issue provisioning code. The code appears once, in full, with a Copy code button and a
single use · 15 min TTLnote under it. Copy it now and pass it to the host administrator. The dashboard will not show it again.
Issuance is recorded on the audit chain: who issued the code, its tenant and environment, and the lifetime.
The host administrator redeems the code by running chrlctl register --code chrl1-aeabky-3iovzg-…-osa6d4 on the host, pasting the whole code. See Running chrlctl.
Lifecycle and status
Section titled “Lifecycle and status”The wizard shows the code’s current state as a pill next to the code itself:
| Status | Meaning |
|---|---|
| awaiting host | Issued and live. The code waits for redemption while its lifetime counts down. |
| redeemed once | A host redeemed the code. It is spent and can never pair another host. |
| code expired | The lifetime ran out before any host redeemed it. The code is dead. |
| code revoked | The code was revoked before redemption. The code is dead. |
You do not need to refresh. The wizard watches for the redemption and advances on its own once the host pairs.
Abandoning the wizard does not revoke the code. An unredeemed code stays live until its lifetime runs out, so treat it as sensitive until then. To kill a live code early, revoke it from the register, below.
The register
Section titled “The register”The Provisioning codes page (in the sidebar’s Governance group) is the register of every code in your tenant. It includes codes minted outside the console. A count next to the title shows how many codes are in the register, and a KPI row totals them by state: issued, redeemed, expired, and revoked.
The Register table shows one row per code. The plaintext is never stored, so the Code column shows the code’s SHA-256 digest.
| Column | What it shows |
|---|---|
| Code | The SHA-256 digest of the code. |
| State | issued, redeemed, expired, or revoked. |
| Tenant and Environment | The scope the code was issued under. |
| CIDR scope | The network the code can be redeemed from. |
| Expires | When the code’s lifetime ends. |
| Redeemed by | The address the redeeming host paired from, and when. A dash until redemption. |
| Issued by | The operator who issued the code. A code issued outside the console reads “outside console”. |
| Ticket | The optional change-ticket reference. |
| Actions | Revoke… for codes that can still be revoked. |
An empty register reads “No provisioning codes in the register.”
Revoking a code
Section titled “Revoking a code”Select Revoke… on the code’s row. The confirmation asks you to type the code’s SHA-256 digest, then revoke. Revocation is recorded on the audit chain. The button is disabled for a code that cannot be revoked, and its hover text says why (for example “a redeemed code cannot be revoked”).
Security properties
Section titled “Security properties”A provisioning code is a pairing credential and nothing more. The host generates its own identity key during registration. No host keys, certificates, or bundles ride on the code: its address, key pin, and tenant ID are public routing information, and its one-time secret is spent on first use and expires with the code. A leaked code buys an attacker one visible registration attempt. That attempt appears in the dashboard and still has to survive CAB review, where it will be refused.
The scope check has a deliberately forgiving edge: redemption from outside the code’s network scope is refused without consuming the code. A mistyped VPN route or a host on the wrong interface does not burn the code. Fix the network and redeem again from inside the scope.
If a code dies before the host pairs
Section titled “If a code dies before the host pairs”An expired or revoked code cannot be revived. Issue a new one: return to Protected apps, start the registration again, and hand the fresh code to the host administrator. There is nothing to clean up on the host side. A code that was never redeemed left no trace there.
If the pill still reads awaiting host but chrlctl reports a refusal, check the network scope first. A scope mismatch is the one refusal that leaves the code alive and retryable.