# Pull secrets in a deploy

Load a deployment's secrets from the vault when the process starts, with a vault key in production.

This example loads the storefront's secrets when its server starts. In production it pulls with a
vault key that can read only the storefront's production secrets; in staging it pulls with an
ordinary API key. Each pull is one `GET` request that returns a JSON map of name to value.

## Before you begin

- The storefront is deployed to `production` and `staging`, and its secrets are set. See
  [Secrets vault and CLI](https://docs.qa.clankership.net/get-started/concepts/secrets-vault.md#manage-secrets).
- You are an admin or owner of the workspace, to create the vault key.

## 1. Create a vault key

A vault key (`ctvk_…`) names the products and production environments it may pull. Create one for
the storefront in production, on the website under **Secrets vault › Vault Keys**, or with the CLI:

```bash
clanker vault key create --name "Storefront runtime" --product storefront --env production
```

```json
{
  "id": "Vk8MtR3pLx6N",
  "name": "Storefront runtime",
  "productIds": ["Pr4GtY9sLm3B"],
  "environmentIds": ["En1MwX6cTb7F"],
  "secret": "ctvk_7HqP2mXv9LsR4tWc8NbK3dFj6YgZ1aQe"
}
```

Copy `secret` now; it is never shown again. Store it in your hosting platform's secret store as
`CLANKER_VAULT_KEY`, next to the deployment that uses it.

## 2. Pull when the process starts

[Pull an environment's secrets](https://docs.qa.clankership.net/api/secrets/pull.md) with the key as a bearer token:

```bash
curl --fail-with-body \
  https://api.clankertest.com/v1/workspaces/acme/products/storefront/environments/production/secrets/pull \
  -H "Authorization: Bearer $CLANKER_VAULT_KEY"
```

```json
{
  "DATABASE_URL": "postgres://storefront@db.acme.dev:5432/storefront",
  "SESSION_SECRET": "9c1f…"
}
```

Unset names are left out, and a deployment with nothing set returns `{}`.

In a Node server, load the values into `process.env` before anything reads them:

```typescript
// load-secrets.ts: import this first in the server's entry point.
// DEPLOY_ENV is your own setting: production or staging.
const environment = process.env.DEPLOY_ENV ?? "staging";
const key =
  environment === "production" ? process.env.CLANKER_VAULT_KEY : process.env.CLANKER_API_KEY;

const response = await fetch(
  `https://api.clankertest.com/v1/workspaces/acme/products/storefront/environments/${environment}/secrets/pull`,
  { headers: { Authorization: `Bearer ${key}` } },
);
if (!response.ok) {
  const error = await response.json();
  throw new Error(`Could not pull secrets (${response.status} ${error._tag}): ${error.message}`);
}
Object.assign(process.env, (await response.json()) as Record<string, string>);
```

From a shell entry point, export each value with `jq`, which quotes it for the shell:

```bash
eval "$(curl --fail-with-body -sS \
  https://api.clankertest.com/v1/workspaces/acme/products/storefront/environments/production/secrets/pull \
  -H "Authorization: Bearer $CLANKER_VAULT_KEY" \
  | jq -r 'to_entries[] | "export \(.key)=\(.value | @sh)"')"
exec node server.js
```

Pull once when the process starts, not on every request.

## Staging and other non-production environments

Vault keys cannot pull non-production values. Pull staging with a user API key, an account API key
or a CLI login instead:

```bash
curl --fail-with-body \
  https://api.clankertest.com/v1/workspaces/acme/products/storefront/environments/staging/secrets/pull \
  -H "Authorization: Bearer $CLANKER_API_KEY"
```

On a developer's computer, the CLI does the same with the saved login:

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

## When a pull fails

The pull answers with an [error](https://docs.qa.clankership.net/api/errors.md) and no values. Fail the start instead of running
without secrets.

| Status | `_tag`         | Why                                                                                                                                  |
| ------ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `401`  | `Unauthorized` | The key is missing, wrong or revoked                                                                                                 |
| `403`  | `Forbidden`    | An ordinary key pulled production, or a vault key pulled non-production, or the vault key does not name this product and environment |
| `404`  | `NotFound`     | No such product or environment, or the product is not deployed there                                                                 |
| `409`  | `Conflict`     | The product or the environment is archived                                                                                           |

Every production pull, including a refused one, is recorded in the vault's audit log with the key
that made it, never the values.

## Rotate the key

Create a new key with the same scope, deploy it, then revoke the old one:

```bash
clanker vault key list
clanker vault key revoke Vk8MtR3pLx6N
```

The next pull with a revoked key fails with `401`.
