Skip to content

plexctl bootstrap-token

Synopsis

plexctl bootstrap-token is the CLI surface for the per-Project BootstrapToken aggregate. It wraps the four /v1/projects/{project_id}/bootstrap-tokens endpoints — IssueBootstrapToken, ListBootstrapTokens, GetBootstrapTokenMetadata, and RevokeBootstrapToken — so an operator can mint, page, inspect, and retire the single-use bearer credentials a fresh Node or Bridge presents on its very first call to plexsphere.

The plaintext is shown exactly once on the issue response. In text mode the CLI prefixes the plaintext with the one-time-plaintext banner (# WARNING: this is the only time this plaintext will be displayed) on stdout; in json / yaml mode the typed BootstrapTokenIssueResponse body is rendered verbatim so a script consumer keeps the structured fields. The list and get subcommands return metadata only — the plaintext is never returned again by the API after issuance.

For the bounded-context reference (state machine, plaintext format, Argon2id parameters, audit contract) see ../../contexts/identity/bootstrap-tokens.md. For the curl-based runbook that predates this CLI surface see ../../how-to/enrolment/issue-a-bootstrap-token.md.

Invocation

text
plexctl bootstrap-token <subcommand> [flags]

The four subcommands are enumerated below.

plexctl bootstrap-token issue

Mints a new BootstrapToken via POST /v1/projects/{project_id}/bootstrap-tokens and renders the plaintext exactly once. The --ttl flag carries a Go duration (5m, 1h, 24h); the server enforces the [300, 86400] seconds range and rejects out-of-range values with 400 Bad Request.

plexctl bootstrap-token list

Pages the per-Project token roster via keyset cursor. The response body carries metadata only — id, project_id, kind, env_prefix, issued_at, expires_at, consumed_at, revoked_at, issued_by_user_id — and the CLI projects an extra STATE column derived from the optional terminal timestamps so an operator can spot a consumed or revoked row at a glance.

plexctl bootstrap-token get

Returns the metadata view of a single token by --token-id. Useful for confirming a redemption landed (consumed_at populated) or a revoke took effect (revoked_at populated).

plexctl bootstrap-token revoke

Marks a BootstrapToken revoked. The server stamps revoked_at = now on the row and emits a single audit Entry per call. The SQL layer's Revoke is idempotent (gates on revoked_at IS NULL) but the audit middleware writes one Entry per call so re-revocation by the same operator is still visible in the stream.

Flags

plexctl bootstrap-token issue

FlagTypeRequiredDefaultDescription
--projectUUIDyesOwning Project UUID.
--kindenumyesRedemption surface: node | bridge. The Validator on the redemption side rejects a bridge token presented at the Node endpoint with ErrKindMismatch.
--env-prefixstringyesEnvironment segment encoded into the plaintext. MUST match ^[a-z]+$; the server enforces the same regex.
--ttldurationyesToken lifetime (e.g. 5m, 1h). Bounded server-side by [5m, 24h].

plexctl bootstrap-token list

FlagTypeRequiredDefaultDescription
--projectUUIDyesOwning Project UUID.
--limitintnoserver defaultMaximum items per page. 0 lets the server pick.
--cursorstringnoContinuation token from a previous call's next_cursor.

plexctl bootstrap-token get

FlagTypeRequiredDefaultDescription
--projectUUIDyesOwning Project UUID.
--token-idUUIDyesBootstrapToken UUID (matches the Location header returned by issue).

plexctl bootstrap-token revoke

FlagTypeRequiredDefaultDescription
--projectUUIDyesOwning Project UUID.
--token-idUUIDyesBootstrapToken UUID.

Persistent flags inherited from root

--server, --profile, --token-file, --output, --yes, --reveal-secrets. The --reveal-secrets flag is inert for this command — the plaintext is always rendered exactly once on issue because the server never returns it twice. See plexctl.md for the canonical persistent-flag list.

Exit codes

plexctl collapses every failure into the taxonomy below; the single source of truth is exitCodeFor in ../../../cmd/plexctl/app.go.

CodeReachableMeaning
0yesThe API returned the expected status (201 for issue, 200 for list / get, 204 for revoke).
1yesRuntime / API error: transport failure, unexpected status code, or a malformed response body.
2yesFlag-parse / misconfiguration: missing required flag, malformed UUID, unknown --kind value, or non-positive --ttl.
3yesMissing or insecure credentials, or 401 Unauthorized from the API.
4yesPermission denied: 403 Forbidden (the operator lacks project:manage for issue/revoke, or project:deploy for list/get).
64noNot reachable — bootstrap-token is fully implemented and never returns *NotImplementedError.

Examples

Issue a Node token (text mode shows the one-time plaintext)

shell
export PLEXSPHERE_URL="${PLEXSPHERE_URL:-https://localhost:8080}"

plexctl bootstrap-token issue \
  --server     "${PLEXSPHERE_URL}" \
  --project    0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a0a0 \
  --kind       node \
  --env-prefix prod \
  --ttl        1h
text
# WARNING: this is the only time this plaintext will be displayed
psb_prod_aebagbafaydqqbrhibbsa3kqaq_node_xxxxxxxxxxxxxxxxxxxxxxxxxx

token_id:   0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a0a1
issued_at:  2026-05-01T10:00:00Z
expires_at: 2026-05-01T11:00:00Z

Capture the plaintext immediately and hand it out-of-band to the redeeming Node. The plexsphere API itself is the only surface that ever holds the plaintext; copying it through chat, email, or shell history defeats the single-use guarantee.

Issue a Bridge token as JSON for a CI pipeline

shell
plexctl bootstrap-token issue \
  --server     "${PLEXSPHERE_URL}" \
  --project    0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a0a0 \
  --kind       bridge \
  --env-prefix staging \
  --ttl        15m \
  --output     json
json
{
  "token_id":   "0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a0a2",
  "token":      "psb_staging_aebagbafaydqqbrhibbsa3kqaq_bridge_xxxxxxxxxxxxxxxxxxxxxxxxxx",
  "issued_at":  "2026-05-01T10:00:00Z",
  "expires_at": "2026-05-01T10:15:00Z"
}

The token field appears in the response body once. Pipe the output into a sealed-secret pipeline and never persist the JSON document to disk.

List outstanding tokens for a Project

shell
plexctl bootstrap-token list \
  --server  "${PLEXSPHERE_URL}" \
  --project 0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a0a0
text
ID                                    PROJECT                               ENV      KIND    ISSUED_AT             EXPIRES_AT            STATE
0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a0a1  0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a0a0  prod     node    2026-05-01T10:00:00Z  2026-05-01T11:00:00Z  live
0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a0a2  0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a0a0  staging  bridge  2026-05-01T10:00:00Z  2026-05-01T10:15:00Z  consumed

Permission denied on revoke (exit 4)

shell
plexctl bootstrap-token revoke \
  --server   "${PLEXSPHERE_URL}" \
  --project  0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a0a0 \
  --token-id 0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a0a1
# stderr: plexctl: 403 Forbidden: insufficient_relation (need project:manage or project:deploy)
echo $?  # 4

Cross-references