Skip to main content

crewship credential

Manage credentials (API keys, tokens, secrets) used by AI agents.
Alias: crewship cred

Subcommands

Provider pools (account groups)

crewship credential pool … manages provider pools — the Account groups of the Credentials page: a named set of provider accounts of one provider and one billing mode, in priority order. The UI says account group, the API and CLI say pool; they are the same object (API reference). A pool is a definition only — creating one does not bind it to an agent, grant its members or enable failover yet — and every subcommand, reads included, requires OWNER or ADMIN. A scoped CLI token additionally needs credentials:write for create, update and delete (list and get need no scope); the scope does not bypass the role requirement. Every pool carries a revision. pool get prints it; pool update and pool delete send it back as the quoted If-Match header through --revision, so an edit made against a definition another administrator has since changed is refused with 412 rather than overwriting it.

crewship credential pool create

Create a provider account set. Requires workspace OWNER or ADMIN access. This stores a definition only: it does not grant agents access, assign accounts, or enable runtime failover. Secret values are never returned.

crewship credential pool list

List up to 100 definitions in the current workspace. Requires OWNER or ADMIN. JSON output contains items and next_cursor; pass a non-null cursor to fetch the next page. No secret values are included.

crewship credential pool get

Show a definition and its member IDs and priorities. Requires OWNER or ADMIN in the pool’s workspace. Use the exact pool ID, not its display name.

crewship credential pool update

OWNER/ADMIN only. Scoped CLI tokens additionally require credentials:write; that scope does not bypass the role requirement. Read the current revision with pool get, then submit the complete replacement name and membership. Provider and authentication mode cannot change. Retained members keep their round-robin history. Changes grant no account access. If another administrator edits first, the server returns 412: inspect the new definition before retrying rather than overwriting it.

crewship credential pool delete

OWNER/ADMIN only. Scoped CLI tokens additionally require credentials:write; that scope does not bypass the role requirement. Retires the definition and prevents future selection. It does not delete or revoke provider accounts or remove membership history. Retired names stay reserved; choose another name for a new definition. Pools are not yet bindable to agents in this increment; active execution/binding protection must be integrated before runtime assignments are enabled.

crewship credential list

List all credentials in the workspace.
Output columns: ID, NAME, TYPE, TIER, STATUS, AGENTS, SOURCE The SOURCE column shows who owns the row: user (operator-created via UI/CLI), system (auto-managed rows minted by manifest dispatch), or agent. When a row is provisioned for a service, the service slug is suffixed (e.g. system (github-bot)). Without --limit, the paged request uses a page size of 100. If more results remain, the next cursor is printed to stderr. With --offset the command pages by position instead and ends the human formats with showing 101–112 of 112.

crewship credential get

Show credential details. The credential value is never displayed.
Output fields: ID, Name, Type, Provider, Status, Scope, Created. For a provider login (a PROVIDER_LOGIN, or an AI_CLI_TOKEN / API_KEY of a model provider) seven more rows follow: Mode, Plan, Owner, Expires, Refresh (not supported (<mode>), or the status with the last / next time and any error), Delivery (env CLAUDE_CODE_OAUTH_TOKEN, file .codex/auth.json, …) and Pays for (N agents, M crews).

crewship credential access <id>

Show your server-calculated access to the credential’s metadata and actions. The command never retrieves the secret. With a CLI token, reveal is denied: the actual reveal route requires an interactive session, fresh login, a reason and an audit write. A conditional result is eligibility, not a promise that a later mutation will pass its live checks.

crewship credential dependents <name-or-id>

List the visible, saved routines that statically declare this credential’s type (credentials_required, credential_ref.type, or {{ secrets.<type> }} in supported steps and hooks), with a present-time selection estimate. Output columns: SLUG, NAME, CURRENT SELECTION — where CURRENT SELECTION is would_resolve if the runtime’s current ACTIVE selection points at this credential, another_credential if a different matching row wins, or unavailable if no row qualifies.
References are configured intent, not recorded run use — per-run credential use is not recorded, and dynamic agent/script lookups are not included. Hidden routines are excluded for non-management roles. The same view backs the credential detail sheet’s dependency list, and the API route carries the visibility_limited and recorded_use: "not_attributed" fields the table summarises.

crewship credential refresh

Force a provider login’s access token to be refreshed now.
Only a subscription login with a stored refresh token — a ChatGPT auth.json imported as --type PROVIDER_LOGIN — can be refreshed; a Claude setup-token or an API key answers 400. The server refreshes such logins on its own 24 h before expiry and before a run start with less than 48 h left, so this is for forcing one: after a login was marked needs_relogin and re-imported, or to see whether the seat still answers. Prints the login’s rows (see get) on success; 409 while another refresh of the same login is in flight. Provider accounts are OWNER/ADMIN-only, so a MANAGER gets not found here, not forbidden. See Provider logins.

crewship credential create

Create a new credential. The value is validated against the provider API before saving.
Using --value exposes the credential in the process list. Use --value-stdin for secure input:
Credential values are validated against the provider API automatically. If validation fails, you are prompted to confirm saving. OAuth tokens (sk-ant-oat*) and SECRET type credentials skip validation.

An OpenAI-compatible endpoint

A self-hosted or internal gateway that speaks the OpenAI wire format is stored as one credential: the endpoint and the key together, under provider OPENAI_COMPAT.
The two halves travel together because either one alone is useless: the sidecar has nowhere to dial without the URL, and the endpoint refuses the call without the key. They are stored as a single {baseURL, apiKey, headers} object, which is the same shape an authenticated ENDPOINT_URL credential already used. The key is delivered to the agent’s sidecar, which writes it into the outbound request; it is not written into the agent’s environment. crewship provider route show OPENAI_COMPAT prints the route it will take. Five consequences to know before you create one:
  • The endpoint must carry auth material of some kind to be isolated. A bearer token, custom headers, or both — any of these routes the credential through the sidecar, which writes them into the outbound request so they never enter the container. An endpoint with no auth at all stays unrouted: there is nothing to isolate, and routing it would put a failure in front of a path that works. crewship crew inspect reports which of the two a run got.
  • Calls bill at $0. OPENAI_COMPAT has no rate row — Crewship cannot know what your endpoint charges — so cost-ledger rows for it are zero. crewship provider route list marks it (unpriced) for exactly this reason. A $0 spend line for these calls is the truth, not a cheap month.
  • An endpoint granted to one agent answers only for that agent. The crew’s sidecar holds one store, but every credential in it carries the crew members it was granted to, and a model call is served only a credential the calling agent holds — resolved from that agent’s own route token, not from anything the request can claim. Give two members two different OPENAI_COMPAT credentials and each reaches its own endpoint with its own key. A member the crew holds a credential for but this one was not granted is refused with 503 no credential available rather than handed a teammate’s (#2052). A crew holding no credential at all for a provider is unchanged: the three vendor providers still forward the call for the agent to authenticate itself, which is what they did before credentials existed. Scope follows the grant you made, so nothing here asks you to declare it twice. A credential linked to the crew, or bound at crew or workspace scope, reaches every member and stays available to all of them; one granted to named agents — crewship credential assign — reaches only those. A grant that happens to cover every member of the crew is the same thing as a crew-wide one, and is treated as such. One case is deliberately still crew-wide: a local-model endpoint an agent picks up from the workspace default rather than from a grant of its own. The default is the newest ENDPOINT_URL credential in the workspace that no agent has been granted — a row granted or bound to one agent is skipped, so it will not become the crew’s by accident — but the default itself applies to every agent, and is delivered that way. Grant an endpoint to the agents that should have it if you want it scoped.
  • One active credential set per crew sidecar. Agents with the same resolved credentials safely reuse it: each request carries a self-verifying, crew-bound route token for attribution. A key rotation or a different set forces a restart. If an older concurrent run then sends a request, its credential fingerprint no longer matches and the sidecar returns 403/503 instead of serving it from a set the run was never launched with. Two overlapping agents in one crew whose credential sets differ may still need a retry after that restart — the restart is per credential set, and only the last one to start is live. What changed with #2052 is the failure mode of the request that does get through: it is answered from the caller’s own grants or refused, never from a teammate’s. The fingerprint is what carries that guarantee, and crewshipd always has one: the internal token it is keyed on is filled in at startup from your auth.internal_token, else derived from ENCRYPTION_KEY, else a fresh random for the boot. There is no configuration that turns this off, so nothing here asks you to switch it on. It matters only if you run a sidecar Crewship did not launch. A sidecar started without a fingerprint accepts a correctly signed route token from any agent in the container, because there is no configuration identity to compare against — a forged or malformed token is still rejected. The server logs a one-time sidecar credential isolation is fail-open warning if it ever reaches that state itself; that would mean a build bypassing the normal startup path, so report it rather than trying to reconfigure around it.
  • The endpoint and the key are one stored object, so a full-value rotate replaces both. Pipe the whole object in rather than passing it on the command line:
    To rotate only the key and keep the endpoint, use the field-by-field form. The server merges the fields you send over the stored value and leaves the rest alone, which is what you want for a routine key rotation:
    --auth-token-stdin rather than --auth-token, for the same reason --value-stdin exists: an argument is readable by anything that can see the process table while the command runs, and it lands in your shell history. The --auth-token flag still works and is fine for a throwaway value, but do not use it for a key you intend to keep. Secrets are never readable, so the merge has to happen server-side — the CLI cannot read the current key back in order to resend it.
The credential is not probed on create. The create path is reachable with authentication alone, so dialling a caller-supplied host from it would be an SSRF; the command says so instead of printing a green tick over a check that never ran:
Once it is saved, crewship credential test-stored self-hosted-llm does dial it (#2043). Two separate things make that safe, and only the second is a network control: the path is role-gated, so an arbitrary caller cannot reach it, and the dial itself goes through the guarded probe — the address is checked after DNS resolution, so a link-local or cloud-metadata target is refused even when the URL is perfectly well-formed, and redirects are never followed. A URL that passed create-time validation is not thereby a safe one to dial; shape validation says nothing about where a name resolves. It reports reachability — DNS, TLS, a wrong path prefix, a gateway serving no model list — and deliberately not authentication: the probe sends no apiKey and no custom headers, because whoever can edit the credential can repoint baseURL, and a Test button that posts the stored secret to that host would be a way to exfiltrate it. A key that is present but wrong shows up on the first agent call. --base-url on any other provider is a local validation error, exit 2:
OpenRouter needs none of this — it is an ordinary API key against a fixed upstream:

crewship credential update

Update a credential. Only changed flags are sent.
On a PROVIDER_LOGIN, --value / --value-stdin is a re-login: the server splits the new value into its parts again (access token, refresh token, plan, expiry), drops the old parts and the refresh state, and keeps the credential’s id and every assignment — the way to bring back a seat that reached needs_relogin. The provider and type cannot change in the same request (400 re-login must keep the existing provider and type); the billing mode is inferred from the value’s shape as on create.

crewship credential delete

Delete a credential.
Agents that depend on this credential lose access immediately. Prompts for confirmation unless --yes is passed.

crewship credential assign

Assign a credential to an agent. The credential is injected as an environment variable into the agent’s container.

crewship credential unassign

Remove a credential from an agent.

crewship credential field

Manage custom fields in addition to a credential’s main value. The parent has the alias fields; subcommands are list, set, and remove (rm and delete are aliases for remove). Secret field values are never returned.
field set flags are --value, --value-stdin, --plain, and --ordinal. Keys must be lower_snake_case. Without --plain, a field is encrypted and shown only as (secret) by field list; --plain is for identifiers such as regions, account IDs, and hosts.

crewship credential bind

Bind a credential to an environment variable slot in one scope. The credential’s name is the account (github-acme); the slot is what the agent reads (GH_TOKEN). Keeping them apart is what lets one workspace hold ten GitHub accounts — ten crews can each bind GH_TOKEN to a different one.
No scope flag means workspace scope. --crew and --agent are mutually exclusive: a binding has exactly one scope. Within a scope a slot points at exactly one credential — binding a slot that is already taken returns 409 Conflict instead of replacing the existing row. Binding the same slot in a different scope is not a conflict.

crewship credential bindings

The command also accepts the alias crewship credential binding-list. List bindings in the workspace.
An empty list does not mean agents receive nothing: a credential with no binding is delivered under its own name, which is the pre-binding behaviour.

crewship credential unbind

Remove a binding, by id or by naming the slot and scope.

crewship credential resolve

Show the slot map one agent will actually boot with, and which rule produced each row. Values are never shown.
Sources, most specific first: agent_grant (an explicit credential assign), agent_binding, crew_binding, workspace_binding, crew_link (no binding — delivered under the credential’s own name).

crewship credential login

Sign in to a model provider with a device code and store the login as a PROVIDER_LOGIN credential owned by you. The command prints a one-time code and a URL; open the URL in any browser, sign in to the account that pays for the model, enter the code, and the command finishes when the server has stored the login. The server — not the CLI — polls the provider, so the sign-in survives a closed terminal (--no-wait) and a server restart. Only OPENAI (ChatGPT Plus / Pro / Business, for the Codex adapter) offers this today; the stored login is delivered to Codex agents as a rendered auth.json whose refresh token never leaves the server — see the Codex CLI guide.
Exit codes: 0 when the credential was created (its details are printed the way credential get prints them); 2 when the provider refused the sign-in or the credential could not be stored; 3 when the 15-minute code expired before it was entered.

crewship credential login-status

Show the state of a device-code sign-in: pending, complete (with the credential id), expired or denied (with the reason).

crewship credential test

Test a credential value against the provider API without saving it.

crewship credential test-stored

Re-test an already-saved credential against the provider API without re-supplying the value. Useful for verifying that a rotated key still works, or that a long-lived token has not been revoked upstream.
Returns success if the live API call returns valid: true; otherwise the error message from the provider is printed. Crewship maintains upstream probes for API-key credentials from Anthropic, OpenAI, Google, OpenRouter, Cursor, Factory, GitHub, GitLab and Vercel, plus any ENDPOINT_URL credential. For API keys, a pass means the key itself works. For an OpenAI PROVIDER_LOGIN in subscription mode, Crewship stores the OAuth access token extracted from a Codex auth.json and reconstructs the login file for the CLI. That token is not an OpenAI API key. This command reports not checked rather than sending it to the API-key endpoint; the same applies to older OpenAI AI_CLI_TOKEN auth blobs. A PROVIDER_LOGIN in api_key mode retains the provider’s API-key probe; an Anthropic subscription setup-token retains its separate OAuth probe. agent credential-readiness checks whether an unprobed login would reach a particular agent, and a controlled CLI run is still needed to prove it works. OPENAI_COMPAT is probed here too (#2043), and it is the one that answers a different question. Because its endpoint is operator-supplied, this command asks whether the host is reachable and serving a model list — it sends no apiKey and no custom headers, so a pass means the endpoint is live and correctly addressed, not that the key is accepted. A wrong key surfaces on the first agent call. For every other provider there is nothing to call — the agent talks to that API directly and Crewship holds only the secret. Those credentials report not checked rather than valid, and exit 0:
The distinction matters when scripting a pre-flight check: a “not checked” credential is neither known-good nor known-bad, so treat it as unverified rather than as a pass.

crewship credential rotate

Requires OWNER/ADMIN role, or the credential.rotate capability (grantable to a MANAGER/MEMBER so an oncall user can rotate a leaked token without full vault access). The new value is capped at 64 KiB. Issue a new value for the credential. The old value is preserved on the rotation row for the grace window (max 7d) so in-flight agents that cached the old key can still fall back during their run, then the old value is scrubbed. The cobra default for --grace-seconds is 0, but the flag is only sent when you explicitly set it. Omit the flag and the server applies its standard 24h grace. Pass --grace-seconds 0 to force an immediate cutover (no overlap). Pass any positive value (up to 604800 / 7d) to set a custom window.

crewship credential rotations

List rotation history for a credential.
Output columns: ID, STATUS, ROTATED_AT, EXPIRES_AT, GRACE_S, OLD_GONE, ROTATED_BY.

crewship credential rotation-cancel

End an ACTIVE rotation’s grace window immediately and scrub the old value. EXPIRED / CANCELLED rotations are no-ops on the server side (idempotent 200).
Scrubbing the old value ends the overlap window early — any in-flight agent still using the cached old key loses its fallback immediately.

crewship credential audit

Show the full credential timeline — the same view the detail Sheet’s Audit tab uses. Useful for grep-ing for ROTATE / TEST / REVOKE events without scraping the UI.

crewship credential default-env-var

Print the conventional env var name for a provider (GH_TOKEN, GITLAB_TOKEN, VERCEL_TOKEN, …). Useful when scripting credential assign and you don’t want to memorise every provider’s convention.
This is a suggestion, not a rule. Crewship accepts any provider string; one it has no convention for simply prints nothing and exits non-zero, and you pass --env-var-name yourself. Every name below is the variable the vendor’s own CLI or SDK documents reading.
DOCKER, TERRAFORM, ANSIBLE, CUSTOM_CLI and NONE are recognised providers with no entry: the docker CLI authenticates through ~/.docker/config.json rather than an env var, and HCP Terraform’s token variable is host-suffixed (TF_TOKEN_<hostname>), so any single value here would be invented.
Storing the credential is only half the job — the CLI that reads it has to exist in the crew’s container. crewship crew credential-readiness reports which of a crew’s credentials are missing their tool.

crewship credential reveal

Disclose a credential’s plaintext value.
Try rotate first. Most reasons to reveal a secret are really reasons to put a new secret somewhere. crewship credential rotate shows the new value once while the old one drains through its grace window, so nothing currently in use is ever exposed. Reveal is the fallback for when you need the value that is already deployed.
This command asks for your password. It cannot use your stored CLI token: the server refuses API tokens, internal/sidecar tokens and every non-interactive caller on this endpoint, because a token in a config file is indistinguishable from one held by a compromised agent or a leaked CI secret. The command signs you in interactively — creating a real session you can see in Settings → Sessions — and calls the endpoint with that. It also refuses to run without a terminal, before making any network call, so a CI job gets an actionable error instead of a password prompt it can never satisfy. The value goes to stdout and nothing else does, so it redirects and pipes cleanly. The credential name, its classification and the id of the journal entry now recording the disclosure go to stderr. A reveal is refused unless all of these hold:
  • an OWNER has enabled reveal for the workspace (crewship credential reveal-policy --enable)
  • your membership carries the credentials:reveal capability — the admin preset includes it and an OWNER or ADMIN with no explicit capability set holds it by default, but an explicit set is authoritative: revoked is revoked, and a MANAGER holds it only when granted
  • you are MANAGER or above; a MANAGER is additionally limited to credentials in their own crews
  • the credential is not SEALED (see sensitivity)
  • your --reason is at least 20 characters and not a generic placeholder
  • the session is fresh — created within the last 5 minutes. The interactive sign-in this command performs satisfies it; refreshing a page or a token does not
  • your account is not still on the seed default password — change it in account settings first, then sign in again
  • the reveal can be written to the tamper-evident audit chain — if that write fails the command gets a 500 and no value
A credential in another workspace returns “not found”, not “forbidden”: the existence of an id in someone else’s tenant is not disclosed. The endpoint has its own per-IP rate limit (http.cred_reveal_per_min, default 3/min), and unlike the general API bucket, authenticated CLI callers are not exempt from it.

crewship credential reveal-policy

Show or set the workspace credential-reveal switch. Reveal is off for every workspace — including newly created ones — until an OWNER turns it on.
Reading requires MANAGER or above; MEMBER and VIEWER get a 403, because whether a tenant has reveal enabled is useful to an attacker choosing a target. Changing it requires OWNER. Both directions are written to the tamper-evident audit chain before taking effect: if the audit write fails, the switch does not move.
Enabling the switch grants nobody reveal on its own. Each person still needs the credentials:reveal capability on their membership. The recommended corporate default is two people holding it, not the whole admin team.

crewship credential sensitivity

Set a credential’s classification. It is the first thing the reveal gate consults and the only layer no role can override.
Raising a classification is a MANAGER+ action and takes effect immediately — it only ever removes reach, so it needs no ceremony, and a control that is annoying to tighten stops being used. Lowering requires OWNER or ADMIN and is written to the audit chain first: it hands out a key that did not exist a moment earlier, and it is the cheap move for an attacker holding an admin session. SEALED has no escape hatch on purpose. To get a usable value out of a sealed credential, rotate it — crewship credential rotate mints a new one and shows it once. Existing credentials are STANDARD, which is safe only because the workspace reveal switch defaults to off.
RESTRICTED behaves like STANDARD today. The four-eyes second-approver flow that will distinguish them is deferred, but classifying now still pays off — that gate will key off this field.