# Secrets vault and CLI

Install the standalone `clanker` CLI with
`curl -fsSL https://clankertest.com/install.sh | sh` (Windows:
`irm https://clankertest.com/install.ps1 | iex`). It brings its own Node,
Playwright and browsers, about 320 MB, into `~/.local` (Windows:
`%LOCALAPPDATA%\Clanker`) and updates itself in the background. The Clanker Ship
plugin starts the same install on its first agent session. Run commands from your
project root. `clanker run` runs that project's specs with the CLI's own
Playwright, so the project doesn't need Playwright installed, and publishes the
run to your workspace; see [Test runs](/guides/test-runs.md).

## Sign in

`clanker auth login` prints a sign-in URL and code and opens your browser.
Approve only the code shown by your own terminal. `--no-browser` prints the URL
without opening it. Login uses a dedicated device authorization grant; the CLI
refreshes its access token automatically. `clanker auth whoami` shows the active
identity, and `clanker auth logout` revokes the saved login. Security settings
also lists and revokes CLI logins.

For automation, set `CLANKER_API_KEY` to a user or account API key, or pipe it to
`clanker auth login --api-key-stdin`. The environment variable takes precedence
over saved credentials. Credentials are stored in `$XDG_CONFIG_HOME/clanker`
(default `~/.config/clanker`) with owner-only file permissions.

User keys inherit their user's current workspace role. Account API keys have
an assigned workspace role: viewer, member, admin, or owner. Their permissions
match those of a user in that role. Account keys are scoped to one workspace
and do not represent a person's profile or personal keys. Admins manage account
keys in Security settings or `/account-api-keys`; only owners manage owner keys.

CLI OAuth tokens use `ctat_`, ordinary keys `ctk_`, and vault keys `ctvk_`.
The API rejects MCP tokens for management and never accepts a vault key as a
CLI login. Production plaintext is accessible only through scoped vault-key pull.

## Link a repository

Run `clanker link <workspace>` once in a repository. It checks that you belong
to the workspace and writes `clanker.json` at the repository root (the folder
holding `.git`), holding the workspace's id:
`{ "workspaces": { "production": "<workspace id>" } }`. The id stays the same
when the workspace's slug changes. `clanker link <workspace> --product <product>`
also records the product the repository's tests cover, which `clanker run`
uses. Commit it so teammates and CI use the same workspace. Without a slug, `clanker link` lists
your workspaces and asks for a number in a terminal; outside a terminal it
prints their slugs and exits 2. Run it again to relink; `clanker unlink` removes
the file.

Workspace commands read the nearest `clanker.json` from the current folder up to
the repository root, so they work from any subfolder. `--workspace <slug>`
overrides the link for one command. With neither, a command exits 2 and says how
to link. Renaming the workspace does not break the link. If you lose access,
commands report that the workspace was not found; run `clanker link` to link
another one.

## Products and environments

Secrets attach to products and their deployments. Create and manage them as
described in [Products and environments](/guides/products-and-environments.md).
A pull works only while the product is deployed to the environment and both are
active. Removing a deployment or archiving either side keeps every secret value;
deploying again or restoring makes the same values available.

`GET/POST /account-api-keys`, `PATCH /account-api-keys/{id}` and
`POST /account-api-keys/{id}/revoke` manage account keys.

## Manage secrets

- `GET /vault` returns active products, the environments they are deployed to, secret names,
  status and version numbers. Non-production values can be returned; production
  values never appear in management responses.
- `GET/POST /products/{product}/secrets` lists or defines names. Create accepts
  `name` and optional `values`, an object keyed by environment slug.
- `GET/PATCH/DELETE /products/{product}/secrets/{name}` reads, renames, sets or
  deletes a definition. A read returns `createdAt`, the product, its deployed
  environments, and `values` keyed by environment slug: each current version's
  number, `isSet`, `createdAt`, `actor`, `generator`, `publicKey` and
  `restoredFrom`, plus `value` for non-production. Patch accepts `newName`, `values`, `expectedVersions`,
  and `requestId`. `expectedVersions` maps changed environment slugs to their
  current version number; stale writes return HTTP 409.
- `POST /vault/assignments` accepts `products`, `environments`, `name`, `value`
  and optional `requestId`. Every selected pair must be deployed.
  Up to 200 pairs can be assigned in one transaction. Responses contain
  `{product, environment, status, version?}` per pair. A member's production
  targets return `status: "skipped"`; eligible non-production targets are written.
- `GET /products/{product}/secrets/{name}/environments/{environment}/versions`
  returns up to 100 versions, newest first. Use `before={lastVersion}` for the
  next page. Each version has `version`, `isSet`, `createdAt`, `restoredFrom`,
  `generator`, `publicKey`, and `actor` (`{ id, type: "user" | "accountApiKey",
name }`). `values=true` includes historical non-production plaintext and is
  forbidden for production.
- `POST .../environments/{environment}/restore` accepts `version` and optional
  `requestId`. It writes a new version and references the restored version.
  Restoring a generated version brings back its generator and public key.

Secret names use letters, digits and underscores, begin with a letter or
underscore, and contain at most 255 characters. Values are limited to 64 KiB
of UTF-8. `null` and `""` both clear the value; whitespace is preserved. Omitted
fields preserve the current value. Clearing retains the definition and records
an unset version. Restoring an unset version clears the current value.

Members write non-production values; admins/owners write production values.
Deleting a definition with any current production value requires admin/owner.
The website shows an X for unset values. Bulk assignment is available only in
the CLI and API. Each secret's website page shows its versions; members can
show old non-production values one at a time and restore any version they may
write.

Retries of set/clear/restore with the same `requestId` reuse the original result;
reusing an ID for a different value or restore target returns HTTP 409. Keep the
same ID when a network response is uncertain. IDs are scoped to a secret and
environment. CLI writes generate an ID unless `--request-id` is supplied.

```sh
clanker vault secret add --product frontend --name MY_SECRET
printf %s 'example-value' | clanker vault secret set --product frontend --env qa --name MY_SECRET
clanker vault secret ls --product frontend
clanker vault secret ls --product frontend --env qa --values
clanker vault secret clear --product frontend --env qa --name MY_SECRET
clanker vault secret history --product frontend --env qa --name MY_SECRET
clanker vault secret restore 1 --product frontend --env qa --name MY_SECRET
clanker vault pull --product frontend --env qa
clanker vault pull --workspace other-workspace --product frontend --env qa
```

Secret input comes from stdin, never a value argument. Repeat `--product` and
`--env` for bulk set. Commands emit JSON; exit 0 means success, exit 2 means a
request/usage failure, and exit 3 means a bulk assignment or generation skipped
some targets.

## Generate values

`POST /vault/generations` creates values on the server. It accepts `products`,
`environments`, `name`, `generator`, optional `reveal` and optional `requestId`,
with the same targets, limits and roles as `/vault/assignments`: every pair must
be deployed, a missing name is defined, and a member's production targets are
skipped. Each environment gets its own value, shared by every selected product
in that environment, so no value crosses environments.

| `generator.type` | Options (default)                                                               | Stored value                  |
| ---------------- | ------------------------------------------------------------------------------- | ----------------------------- |
| `token`          | `bytes` 16–512 (32), `encoding` `hex`, `base64` or `base64url` (`hex`)          | Random bytes in that encoding |
| `password`       | `length` 8–256 (32); `lowercase`, `uppercase`, `digits`, `symbols` (all `true`) | At least one of each class    |
| `uuid`           | none                                                                            | A random version 4 UUID       |
| `ed25519`        | none                                                                            | PKCS#8 PEM private key        |

Password symbols are `-_.~+=`, which need no quoting in shells, dotenv files,
JSON or URL user info. Use a 32- or 64-byte `token` for an HMAC key. Unknown
options, options for another type, and out-of-range values return 400.

Responses contain `{product, environment, status, version?, publicKey?, value?}`
per pair. `ed25519` results always include the SPKI PEM `publicKey`; versions
store it as non-secret metadata that reads and history return for every
environment class. `value` appears only when `reveal` is `true`, only in the
response that created it, and for production only to admins and owners. A retry
with the same `requestId` returns the original versions and public keys without
values; reusing an ID with different options or targets returns 409. Setting a
value by hand replaces the generator and public key.

PEM values span several lines. Quote them in dotenv files and shell variables.

```sh
clanker vault secret generate --product cli --env qa --env production --name SIGNING_KEY --type ed25519
clanker vault secret generate --product frontend --env qa --name DB_PASSWORD --type password --length 24 --no-symbols --show
clanker vault secret generate --product frontend --product api --env qa --name AUTH_SECRET --type token --bytes 32
```

`--bytes` and `--encoding` apply to `token`; `--length` and `--no-lowercase`,
`--no-uppercase`, `--no-digits` and `--no-symbols` apply to `password`. The CLI
rejects options for another type before sending the request. `--show` sets
`reveal`.

## Vault keys and runtime pull

`GET/POST /vault/keys`, `PATCH /vault/keys/{id}`, and
`POST /vault/keys/{id}/revoke` manage vault keys. Writes require admin/owner.
Create/update accepts `name`, `productIds`, and `environmentIds`, with active
workspace product IDs and production environment IDs. Creation returns `secret`
exactly once. Subsequent responses contain metadata only; storage retains a hash.

```sh
clanker vault key create --name 'Web runtime' --product frontend --env production
clanker vault key list
clanker vault key update KEY_ID --name 'Web runtime' --product frontend --env production
clanker vault key revoke KEY_ID
```

`GET /products/{product}/environments/{environment}/secrets/pull` returns the
current JSON name-to-value map. Unset names are omitted; an all-unset pair
returns `{}`. Production requires a live vault key granting both IDs.
Non-production accepts ordinary API keys or dedicated CLI tokens. Vault keys
cannot pull non-production values. Revocation, detached links and archival are
checked on every pull. Production pull attempts produce redacted audit records.

All API responses use `Cache-Control: no-store`. Invalid inputs return 400,
authentication failures 401, role/grant refusals 403, missing or inaccessible
resources 404, and duplicate names/slugs, archives or stale writes 409.
