Skip to main content

crewship credential

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

Subcommands


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.

crewship credential get

Show credential details. The credential value is never displayed.
Output fields: ID, Name, Type, Provider, Status, Scope, Created.

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. Four 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.
  • 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 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 silently sending the prompt to the other endpoint or key. This is fail-closed, not full concurrent multi-tenant storage: two overlapping agents in one crew that require different endpoints may need a retry after the restart. Scope heavily concurrent, different endpoints to separate crews until #2052 adds independent per-agent stores. 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.

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 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 Anthropic, OpenAI, Google, OpenRouter, Cursor, Factory, GitHub, GitLab and Vercel, plus any ENDPOINT_URL credential. Each of those authenticates against the provider, so a pass means the key itself 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 — being OWNER or ADMIN is necessary but not sufficient, and the capability is in no role bundle
  • 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 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.