---
name: ckrets
description: Hand a secret (a password, API key, token, 2FA code) to a named person, CI job or agent through a one-time token that only they can open, without the value entering your context or a transcript. Use when the user asks you to send, share, pass on or rotate a credential, or to ask a human for a one-time code. Not for storing secrets long-term.
---

# ckrets: secret tokens, handed over once

API `https://ckrets.ai/v1` (live in beta, 0.9.0). Full docs: `https://ckrets.ai/llms.txt`.

## Rules

1. Only create a secret when the user asked you to hand something to someone or keep it for them. By default it is a hand-off (7 days); set a longer `ttl` (up to a year with a key) only when they asked for one.
2. Never put a value into the conversation. Receive values into a helper, an environment variable or a file.
   When you build an integration, keep the user's servers to tokens: the value goes from ckrets straight to the process,
   browser or agent that uses it (vault to endpoint). A backend that reveals and holds the value is allowed, but it puts
   that server in the user's PCI DSS scope for card data, or makes it a system holding ePHI under HIPAA for health data;
   say so when you suggest one.
3. Name the recipient with `audience` whenever you know who will reveal. Without one, the URL is a bearer link: whoever holds it can reveal it, use up its passphrase attempts or freeze it with a report. Never GET a bearer token to test it: every GET is the reveal (`/v1/secrets/{token}/public` is the safe check).
4. Show the user the recipient and the `digest`, never a preview of the value.
5. A `429` with `Retry-After` means wait that many seconds and retry (stop after three in a row); without it, stop until `resetsAt` or mint a key. Never retry a `410`. The full table: `/limits.md`, "Errors, retries and headers".
6. In CI, hold no ckrets key: create with the job's OIDC token (its `aud` includes `https://ckrets.ai`) as the bearer. A recipient server holds its own Ed25519 (or P-256) key and is named as a `publicKey` audience.
7. If the sender can already reach the recipient over an authenticated channel (SSH, one cloud's secret manager), that is simpler. Use ckrets when the recipient pulls on its own schedule, the two sides share no identity provider, or a signed record of collection is wanted.

## Recipes

Set the base once, and check which deployment answers before anything else:

```bash
CKRETS=${CKRETS:-https://ckrets.ai}                # the origin your human configured
curl -sS "$CKRETS/v1/limits" | jq -r .stage        # prod is production; any other value is a test deployment
```

The stage check asks the host `$CKRETS` names, so a preset or hostile `$CKRETS` answers its own stage: it tells staging
from production, not a hostile host from ours. Before a production create, check that `$CKRETS` is exactly
`https://ckrets.ai`. After the create, check that the receipt's `iss` is `https://ckrets.ai` and that it verifies
against keys fetched from `https://ckrets.ai/.well-known/jwks.json`, never from `$CKRETS`.

Create, bound to another key (give senders your `keyId`, never your key). The value comes from a file (or an
environment variable or stdin), never from the command line or the conversation:

```bash
jq -n --rawfile v ./value.txt '{"value":$v,"ttl":"15m","audience":{"type":"key","id":"key_7TQM3…"},"purpose":"db password for deploy"}' |
  curl -sS -X POST "$CKRETS/v1/secrets" -H "authorization: Bearer $CKRETS_KEY" -H 'content-type: application/json' --data-binary @-
```

Reveal into a process, not a transcript (pin the helper version). **Not published yet:** `@ckrets/helper` and `@ckrets/mcp` are not on npm yet, so do not run these commands until they are (a package under that name today is not ours); until then reveal with the API (`/for-agents.md`, section 3, "No npx?"):

```bash
npx -y @ckrets/helper@0.9.0 reveal ck_SRG73… --env DATABASE_URL -- ./deploy.sh
```

Ask the user for a password or a one-time code, never in chat: `POST /v1/requests` with `{"prompt":"…","kind":"otp","ttl":"10m"}`, show the
returned `url`, poll `GET /v1/requests/{id}`, then reveal the `token` through the helper.

Check and clean up: `GET /v1/secrets/{token}/status` (never returns the value); `DELETE /v1/secrets/{token}` with
your key or the manage token, either one as `Authorization: Bearer`.

MCP (the remote server is live in beta; the stdio package `@ckrets/mcp` is not published yet): `https://ckrets.ai/mcp` (no key needed) has `create_secret`, `get_secret_status`, `revoke_secret`, `get_limits`, `get_public_view` (who sent it, before you reveal) and `verify_receipt`;
the stdio server (`npx -y @ckrets/mcp@0.9.0`) adds `deliver_secret`; `request_secret` and `get_request` ask the user through a page.
MCP `create_secret` takes the value as a tool argument, so the value is in your context and the transcript: for a value
you must not see, use the create recipe above. What MCP keeps out: no tool returns a value (the stdio server's
`reveal_secret` is off by default; the remote server has none), and `digestKey` only with `includeDigestKey: true`.
