# Secrets vault and CLI

Keep each product's secrets per environment, manage them with the clanker CLI, and pull them when a deployment starts.

A product needs credentials, such as database URLs and API tokens, that differ by environment.
Secret names belong to a product; secret values belong to one product and environment pair, and
can be set and pulled while that pair is deployed and active. A running deployment pulls its values
with one HTTP request when it starts. There is no SDK; the call is plain HTTP.

## Install the CLI

The `clanker` CLI manages secrets, products and planning, and runs your tests. It brings its own
Node, Playwright and Chrome, about 320 MB, and updates itself in the background.

```bash
# macOS and Linux
curl -fsSL https://clankertest.com/install.sh | sh
```

```powershell
# Windows PowerShell
irm https://clankertest.com/install.ps1 | iex
```

It installs into `~/.local/bin` and `~/.local/share/clanker` (Windows: `%LOCALAPPDATA%\Clanker`)
and never edits your shell profile; if `~/.local/bin` is not on your `PATH`, the installer prints
the line to add. The Clanker Ship plugin starts the same install on its first agent session. On
Linux, `clanker install-deps` installs the system libraries Chrome needs.

## Sign in

```bash
clanker auth login
```

`clanker auth login` prints a sign-in URL and a code and opens your browser. Approve only the code
your own terminal shows. `--no-browser` prints the URL without opening it. The CLI refreshes its
login on its own; `clanker auth whoami` shows who is signed in, and `clanker auth logout` revokes
the saved login. **Settings › Security › CLI logins** lists and revokes logins too.

For automation, set `CLANKER_API_KEY` to a user or account API key, or save one with
`clanker auth login --api-key-stdin`. The variable wins over a saved login. Logins live in
`$XDG_CONFIG_HOME/clanker` (default `~/.config/clanker`) with owner-only permissions. The CLI never
accepts a vault key as a login.

## Link a repository

```bash
clanker link acme --product storefront
```

`clanker link` checks that you belong to the workspace and writes `clanker.json` at the repository
root with the workspace's ID, which stays the same when its slug changes. `--product` also records
the product the repository's tests cover. Commit the file so teammates and CI use the same
workspace. Commands read the nearest `clanker.json`, so they work from any subfolder;
`--workspace <slug>` overrides it for one command, and `clanker unlink` removes it. Without a slug,
`clanker link` lists your workspaces and asks for one.

## Names and values

- A name is a valid environment variable name: letters, digits and underscores, starting with a
  letter or underscore, at most 255 characters and unique on the product, such as `DATABASE_URL`.
- A value is a string of at most 64 KiB. `null` and `""` both clear it, which keeps the name and
  records an unset version. Whitespace is kept as sent, and a field you leave out keeps its value.
- Values are encrypted at rest with AES-256-GCM, each version with its own data key. They never
  appear in logs or audit records.
- The website shows an X where a deployed environment has no value for a name, so you can see what
  is missing.

## Who can do what

| Actor                          | Write non-production | Write production | Pull non-production             | Pull production                                 |
| ------------------------------ | -------------------- | ---------------- | ------------------------------- | ----------------------------------------------- |
| Member                         | Yes                  | No               | With their API key or CLI login | No                                              |
| Admin or owner                 | Yes                  | Yes              | With their API key or CLI login | No                                              |
| Account API key (member)       | Yes                  | No               | Yes                             | No                                              |
| Account API key (admin, owner) | Yes                  | Yes              | Yes                             | No                                              |
| Vault key                      | No                   | No               | No                              | Yes, for the products and environments it names |

Viewers write nothing. Production values never come back from management calls: an admin who sets
one cannot read it back. A member's write that includes production targets still writes the
non-production ones and reports the production ones as `skipped`. Deleting a name that has a
production value set needs an admin or owner. Agents have no secrets tools on MCP; they use the
CLI or the API as their person.

## Manage secrets

Secret values come in through stdin, never as an argument. Commands print JSON and exit `0` on
success, `2` when a request or the usage fails, and `3` when a bulk set or generation skipped some
targets.

```bash
clanker vault secret add --product storefront --name DATABASE_URL
printf %s "$STAGING_DATABASE_URL" | clanker vault secret set --product storefront --env staging --name DATABASE_URL
clanker vault secret ls --product storefront
clanker vault secret ls --product storefront --env staging --values
clanker vault secret clear --product storefront --env staging --name DATABASE_URL
clanker vault secret rename PRIMARY_DATABASE_URL --product storefront --name DATABASE_URL
clanker vault secret delete --product storefront --name PRIMARY_DATABASE_URL
```

Repeat `--product` and `--env` to set one value on several pairs at once; every pair must be
deployed, a product that lacks the name gets it, and up to 200 pairs go in one transaction. Bulk
setting is a CLI and API feature; the website edits one product at a time.

| Operation                  | Request (under `/v1/workspaces/{slug}`)                    | Reference                                                            |
| -------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------- |
| Every product's secrets    | `GET /vault`                                               | [List every product's secrets](https://docs.qa.clankership.net/api/secrets/list-all.md)                |
| Define a name              | `POST /products/{product}/secrets`                         | [Create a secret](https://docs.qa.clankership.net/api/secrets/create.md)                               |
| Read, rename, set or clear | `GET`, `PATCH /products/{product}/secrets/{name}`          | [Update a secret](https://docs.qa.clankership.net/api/secrets/update.md)                               |
| Delete a name              | `DELETE /products/{product}/secrets/{name}`                | [Delete a secret](https://docs.qa.clankership.net/api/secrets/delete.md)                               |
| Set across products        | `POST /vault/assignments`                                  | [Set a secret across products and environments](https://docs.qa.clankership.net/api/secrets/assign.md) |
| Generate values            | `POST /vault/generations`                                  | [Generate a secret value](https://docs.qa.clankership.net/api/secrets/generate.md)                     |
| History                    | `GET …/secrets/{name}/environments/{environment}/versions` | [List a secret's versions](https://docs.qa.clankership.net/api/secrets/list-versions.md)               |
| Restore a version          | `POST …/secrets/{name}/environments/{environment}/restore` | [Restore a secret version](https://docs.qa.clankership.net/api/secrets/restore-version.md)             |

Reads return non-production values only. An update can send `expectedVersions`, the version you
last saw in each environment you change; if one has moved on, nothing is saved and the request
fails with `409`. Writes take an optional `requestId` so a retry never writes twice; see
[Idempotent requests](https://docs.qa.clankership.net/api/idempotent-requests.md).

## Generate values

The vault can create a value on the server instead of taking one. Each selected environment gets
its own value, shared by every selected product there, so no value crosses environments. The value
is not shown unless you ask with `--show` (`reveal: true`), and then only in the response that
created it; for production, only admins and owners see it.

| `--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); `--no-lowercase`, `--no-uppercase`, `--no-digits`, `--no-symbols` | At least one of each class kept |
| `uuid`     | None                                                                                     | A random version 4 UUID         |
| `ed25519`  | None                                                                                     | A PKCS#8 PEM private key        |

```bash
clanker vault secret generate --product storefront --env staging --env production --name SESSION_SECRET --type token --bytes 32
clanker vault secret generate --product storefront --env staging --name DB_PASSWORD --type password --length 24 --no-symbols --show
clanker vault secret generate --product storefront --env production --name SIGNING_KEY --type ed25519
```

Password symbols are `-_.~+=`, which need no quoting in shells, dotenv files, JSON or URLs. An
`ed25519` value also returns its SPKI PEM public key, which every member can read, production
included. Setting a value by hand removes the generator and the public key. PEM values span several
lines, so quote them in dotenv files and shell variables.

## History and restore

Each write adds a version to that environment's history, recording who wrote it, when, and how:
set, cleared, generated or restored. History is newest first, 100 versions per page.

```bash
clanker vault secret history --product storefront --env staging --name DATABASE_URL
clanker vault secret history --product storefront --env staging --name DATABASE_URL --values
clanker vault secret restore 3 --product storefront --env staging --name DATABASE_URL
```

`--values` adds the old plaintext for non-production environments; production refuses it. A
restore writes a new version with the old value, generator and public key, and records which
version it restored. Members restore non-production versions; admins and owners also restore
production versions, without seeing them.

## Vault keys and pulls

A deployment pulls its values with [Pull an environment's secrets](https://docs.qa.clankership.net/api/secrets/pull.md), which returns
a JSON map of name to value. Unset names are left out, and a pair with nothing set returns `{}`.
A pull is refused while the product or environment is archived, or when the product is not
deployed there.

- **Non-production pulls** take a user API key, an account API key or a CLI login, and refuse vault
  keys.
- **Production pulls** need a vault key (`ctvk_…`) that names both the product and the production
  environment. Every production pull, including a refused one, is recorded in the vault's audit log.

Admins and owners create vault keys under **Secrets vault › Vault Keys** or with the CLI. The key's
value is shown once:

```bash
clanker vault key create --name "Storefront runtime" --product storefront --env production
clanker vault key list
clanker vault key update KEY_ID --name "Storefront runtime" --product storefront --env production
clanker vault key revoke KEY_ID
```

```bash
clanker vault pull --product storefront --env staging
```

A revoked key, a removed deployment or an archived product fails the next pull. Pull once when the
process starts, not on every request; see [Pull secrets in a deploy](https://docs.qa.clankership.net/get-started/examples/pull-secrets-in-a-deploy.md).
