Read this when you are:
- changing how Crabbox names leases, slugs, runs, or claims;
- debugging "why does
crabbox run --id <x>not find this lease?"; - adding a new lookup form (a slug, a provider ID, anything that should resolve to a lease).
Crabbox names every long-lived thing twice: once with a stable canonical ID
that machines compare, and once with a friendly slug that people type. This
page lists each identifier, where it comes from, and how --id lookup resolves
across them.
Canonical lease IDs look like:
cbx_abcdef123456
The format is fixed: the literal cbx_ prefix followed by 12 lowercase hex
characters. newLeaseID mints one from 6 random bytes, and the regex
^cbx_[a-f0-9]{12}$ (isCanonicalLeaseID) decides whether a value is a
canonical ID; anything that fails the pattern is treated as a slug.
The CLI mints a provisional lease ID before calling the broker. The broker may
return a different final ID (when the Worker dedupes a retried request, for
example). When that happens, the CLI moves the local SSH key directory from the
provisional ID to the final ID with MoveStoredTestboxKey (an atomic
os.Rename of the per-lease directory) and re-keys the claim and other
references accordingly.
Provider resources reference the lease ID through a Crabbox label (the label key
is literally lease):
lease=cbx_abcdef123456
Crabbox-created machines also carry a crabbox=true marker label. crabbox list
and crabbox cleanup discover machines by that marker and then read the lease
label to map a provider machine back to a Crabbox lease.
Slugs are friendly, human-typeable lease names. They look like:
blue-lobster
amber-crab
silver-shrimp
By default a slug is generated from a stable hash of the lease ID
(newLeaseSlug), so the same lease always gets the same generated slug. The
vocabulary is deliberately small (14 adjectives x 8 nouns = 112 base
combinations) to match Crabbox's small-fleet model. Lease-creating commands can
request a custom slug with --slug <name>:
crabbox warmup --slug update-flow-smoke
crabbox run --slug update-flow-smoke -- pnpm test:changed
crabbox checkpoint fork chk_abc123def456 --slug update-flow-smoke--slug is creation-time metadata, not a rename. It is honored only when
Crabbox is creating a new lease; existing leases keep their assigned slug.
Slugs are normalized everywhere they are accepted. normalizeLeaseSlug
lowercases, keeps only [a-z0-9], collapses every other run of characters into
a single -, and trims leading and trailing dashes — so Blue_Lobster and
BLUE-LOBSTER both resolve to blue-lobster. A requested slug must contain at
least one letter or digit and is capped at 41 characters after normalization, so
collision suffixes and provider names stay portable.
When a requested or generated slug collides with an existing active lease (a
matching server label or a matching local claim), slugWithCollisionSuffix
appends a 4-hex suffix derived from a per-attempt seed:
blue-lobster-1f3a
Allocation tries up to 20 suffixed candidates before settling. Collisions are rare in normal use — a single user's active leases seldom approach the 112 base slugs.
Each managed lease also gets a per-provider resource name that includes the slug and a hash of the lease ID, so the provider console shows something legible:
crabbox-blue-lobster-7f8a2c1d
This is what appears as the EC2 Name tag, the Hetzner server name, the Daytona
sandbox name, and so on. It comes from leaseProviderName(leaseID, slug); when
the slug is empty the function falls back to crabbox-cbx-abcdef123456 (the
lease ID with _ rewritten to -).
Each crabbox run against a coordinator also gets a durable run handle:
run_abcdef123456
Like lease IDs, run IDs are the run_ prefix plus 12 lowercase hex characters
(newRunID in the coordinator, from 6 random bytes). The coordinator mints the run
record before the lease is acquired, so events can be appended for leasing
failures, sync failures, and command output even when the run never reaches
command-start. A run ID is stable across a single invocation; retrying the same
command produces a new run.
crabbox history, crabbox events, crabbox attach, crabbox logs, and
crabbox results all accept run IDs. Slugs do not resolve to runs — only to
leases.
Reusable leases get a JSON claim file under the Crabbox state directory:
$XDG_STATE_HOME/crabbox/claims/cbx_abcdef123456.json
When XDG_STATE_HOME is unset, the state directory sits next to the user config
directory: ~/Library/Application Support/crabbox/state/claims on macOS or
~/.config/crabbox/state/claims on Linux.
A claim payload looks like:
{
"leaseID": "cbx_abcdef123456",
"slug": "blue-lobster",
"provider": "aws",
"repoRoot": "/Users/alice/Projects/my-app",
"claimedAt": "2026-05-07T07:42:18Z",
"lastUsedAt": "2026-05-07T07:55:12Z",
"idleTimeoutSeconds": 1800
}Claims do three things:
- bind a lease to one repo so wrappers and agents do not silently reuse a lease against a different checkout;
- give
crabbox run --id blue-lobstera slug-to-canonical-ID translation without round-tripping the broker; - power "is this lease still mine?" checks before destructive operations such as
stop,cleanup, andactions register.
A conflicting claim (same lease, different repoRoot) refuses commands by
default with a use --reclaim error; --reclaim overrides the check and
rewrites the claim atomically.
Static SSH leases (provider: ssh) record extra endpoint fields in the claim —
staticHost, staticUser, staticPort, staticWorkRoot, targetOS, and
windowsMode — so the resolver knows the lease bypasses the coordinator and can
reconnect without re-provisioning. Claims may also cache a resolved endpoint
(sshHost, sshPort, tailscaleIPv4, tailscaleFQDN, bridgeURL) and the
pond label once the lease is up.
Per-lease SSH key directories are keyed by lease ID, under the user config directory (not the state directory):
~/.config/crabbox/testboxes/cbx_abcdef123456/id_ed25519
~/.config/crabbox/testboxes/cbx_abcdef123456/id_ed25519.pub
Keys are ed25519 by default; AWS and Azure Windows leases use a 4096-bit RSA
key instead (ensureTestboxKeyForConfig). The provisional-to-final lease ID
move renames the whole directory so the private key, public key, and any
known_hosts entries migrate together. The provider key name registered with
the cloud account is crabbox-cbx-abcdef123456 (providerKeyForLease). Ordinary
coordinator callers cannot override this lease-bound name; custom provider key
names require admin authentication and are retained when a lease is released.
Canonical crabbox-cbx-* names remain reserved for their encoded lease ID even
for admin callers.
AWS reuses an existing lease-bound key only when both its public key material
and provider ownership metadata match the canonical lease ID. Hetzner applies
the same check to an exact-name match. Because Hetzner makes public-key material
account-unique, a differently named identity match is recorded as the lease's
actual shared provider key without cleanup ownership and is retained. Cleanup
requires that persisted ownership decision, re-reads provider ownership
metadata, and leaves legacy, unowned, shared, or custom keys intact.
crabbox <command> --id <value> accepts:
- a canonical
cbx_...lease ID; - a normalized slug —
blue-lobster,Blue Lobster, andBLUE_LOBSTERall resolve to the same lease; - in coordinator mode, the slug as the broker knows it (case-insensitive).
Resolution order:
- Read the local claim store.
resolveLeaseClaimfirst tries the literal identifier as a claim filename, then scansclaims/*.jsonfor any claim whoseleaseIDor normalizedslugmatches. - If a matching claim exists, use its
leaseIDas the canonical handle. - If no claim is found and a coordinator is configured, ask the coordinator to resolve the identifier (slug or canonical ID).
- For static SSH and direct-provider modes, fall back to the provider's
Resolveimplementation onSSHLeaseBackend.
The first source that returns a hit wins. This is why --id blue-lobster works
from any directory once a warmup ran in some other repo — the local claim
translates the slug to a lease ID before the broker is ever involved.
provisional lease ID newLeaseID() before the broker call
final lease ID broker may return a different ID; key dir + claim re-keyed to it
slug computed on first lease creation, stable for that lease
provider name derived from final lease ID + slug
run ID minted per crabbox run when a coordinator is configured
Slugs are not reserved after a lease ends. The next lease that happens to hash to the same base slug will reuse it; the small vocabulary makes that possible but uncommon in practice.
Related docs: