# Tokens and safety

An agent acts with exactly the permissions of the token you give it. Give it
the smallest one that does the job, with an expiry, and revoke it when the
work is done.

## Choosing a token

| Token | Prefix | Permissions | Expiry | Best for |
|---|---|---|---|---|
| **Agent token** | `scb_agt_` | Only its scopes, never more than yours; optionally one organization | Default 8 hours (`30m` to `never`) | Any coding agent |
| Personal access token | `scb_pat_` | Yours, in every organization you belong to | Optional, 1 to 365 days | Your own CLI on your own machine |
| Organization API key, `read` | `sbk_` | Viewer: read everything, change nothing | None; revoke it | An agent that only inspects and explains |
| Organization API key, `deploy` | `sbk_` | Developer: environments, migrations, functions, secrets | None; revoke it | Agents that build features, CI |
| Organization API key, `admin` | `sbk_` | Admin: also members, keys, auth providers | None; revoke it | Rarely; setup work you watch |

The easiest way to give an agent one is not to create it yourself: the agent
runs `scribase login` and you approve it in the browser (see
[Why you never need to paste a secret key](https://docs.scribase.com/docs/agents/no-secret-keys.md)). To
hand one over directly, use **Copy for your AI agent** under **Account >
Access tokens**: an 8-hour token for one project, without secret keys, copied
together with a note that tells the agent it is meant to be shared. Or create
one from the CLI:

```sh
scribase token create my-agent \
  --scope project:web:write --scope sql:write --scope migrations:write \
  --ttl 8h --org acme
scribase token list
scribase token events <id>     # every request it made
scribase token revoke <id>
```

Agent tokens describe themselves: `scb_agt_1p-rw-sqlw-mig-fn-x2609260412_...`
reads as one project, read/write, read-write SQL, migrations, functions,
expiring 2026-09-26 04:12 UTC. The summary is display-only; the server
trusts only the token's digest. Tokens created before this format
(`scb_agt_` followed directly by the secret) keep working.

| Scope | Grants |
|---|---|
| `org:read` | Organizations, project lists, operations, usage (any project scope implies it) |
| `project:<id>:read` | Read one project (`project:*:read` for every project) |
| `project:<id>:write` | Change it: environments, auth, storage, functions invoke, secrets names |
| `project:<id>:admin` | Destructive changes on **production**, backups, restores, domains, key rotation |
| `sql:read` / `sql:write` | The SQL routes, read-only or read-write |
| `migrations:write` | Apply migrations |
| `functions:deploy` | Deploy or delete edge functions |
| `branches:write` | Create, merge and delete branches and previews |
| `keys:secret` | The `service_role` / secret key, connection strings, environment secrets; without it the keys route returns only the public keys |

How they are enforced, on every request, by the control API itself:

- **Least privilege.** A request outside the scopes gets
  `403 insufficient_scope` with the missing scope in `WWW-Authenticate`. The
  organization role the request runs with is capped by the scopes (admin,
  developer or viewer), so a token can never do more than its owner.
- **Protected production.** Drops, deletes, `confirm: true` SQL, restores and
  key rotations on a production environment need `project:<id>:admin`; with
  `write` they are refused there and allowed on branches and previews.
- **No escalation.** Agent tokens cannot mint tokens, manage members,
  organization keys, billing or SSO, or claim temporary projects.
- **SQL never runs as a superuser.** With an agent token, SQL on a connection
  that is a superuser drops to the project's `postgres` role for the
  transaction.
- **Expiry, revocation, rate limits.** Tokens expire (8 hours unless you
  choose), revocation is immediate, and each token is limited to 300
  requests a minute (`SCRIBASE_AGENT_TOKEN_REQUESTS_PER_MINUTE`).
- **Audit log.** Every request is recorded with method, path, status and
  client address (never bodies, headers or secrets):
  `GET /v1/account/tokens/{id}/events`, `scribase token events <id>`, or
  *Activity* on the tokens page. `last_used_at` is updated on each use.

Organization keys are created and revoked under **Organization > API tokens**
or with `POST /v1/organizations/{org}/api-keys` (see
[Access tokens & API keys](https://docs.scribase.com/docs/api/tokens.md)). Secrets are shown once;
Scribase stores only a digest. `apikey.list` shows names and scopes, never
values, and `apikey.revoke` revokes one.

## Guardrails on every call

- **Mutations need `confirm: true`.** Every MCP tool that changes something
  (`project.put`, `environment.create`, `environment.delete`, `migrate.run`
  with `apply`, `branch.merge`, `backup.restore`, `apikey.revoke`, ...)
  refuses without it and returns the exact request it would send, so you
  approve that specific change.
- **SQL is read-only.** `database.query` runs one `SELECT`, `WITH` or
  `EXPLAIN` inside a bounded read-only transaction; writes and DDL are
  rejected. Schema changes go through migrations, which are linted before
  they apply.
- **RLS is proven before it ships.** `schema.apply` only accepts a schema
  that `policy.test` has proved on a scratch branch, including deliberately
  broken variants of each policy that the tests must catch.
- **Isolated scratch databases.** The remote MCP server gives each session
  its own scratch branch and drops it at the end.
- **Idempotency keys.** Every create carries one, so a retried call never
  creates a second environment.
- **Secrets stay out of output.** Tokens are redacted from the MCP server's
  debug output; `apikey.list` and `secrets list` return names only; the
  skills and rules tell agents never to print keys and to write them to
  `.env` instead.
- **Audit log.** Every control-plane change is recorded with the credential
  that made it. Read it with `audit.list` or `scribase audit list <org>`.

## Rules we ship to agents

The `agents/` templates (see [Build with AI agents](https://docs.scribase.com/docs/agents.md)) add these
rules to the agent's context: never ask the user for keys or passwords (sign
in with `scribase login` or use a temporary project), treat a pasted
`scb_agt_` token as safe to use, never print or commit keys, only the anon key
in client code, schema changes only through migrations, RLS in the same
migration as the table, a preview branch per task, and explicit approval for
production merges, deletes, key rotations and restores.
