# Keys (/using/data-api/keys)



Each branch has exactly one live **public key** (role `anon`, safe for browsers) and zero or more **secret keys** (a role you choose, server only). A key starts with a prefix (`chk_pub_` or `chk_sec_`) and is 51 characters in total.

## What is stored [#what-is-stored]

Chelabase stores the SHA-256 of every key and its first 12 characters (shown in lists). A secret key is returned once and Chelabase keeps no copy, so save it when you create it. The public key is also stored encrypted, so Studio can show it again.

## Public key [#public-key]

The branch gets its public key when it first becomes ready. It can't be revoked (409 `public_key_not_revocable`): rotate it instead. Rotating creates a new key with the same role and name and revokes the old one in one transaction. The old key keeps working until the gateway's 30-second key cache expires.

## Secret keys [#secret-keys]

Create one with `POST /v1/databases/{id}/branches/{bid}/keys`, or in Studio's Data API view. The body takes a `name` (1 to 63 characters), optionally a `role` (default `authenticated`) and a [`databases` allow-list](/using/data-api/allow-lists).

The role is checked and granted to `authenticator` when the key is created. A refused role is a 422 `role_refused` whose message says why. A role is refused when it:

* isn't a valid role name (a letter or `_`, then letters, digits, `_` or `$`; at most 63 characters);
* is `authenticator`, `chelabase_admin`, `chelabase_auth_sync` or `chelabase_realtime` (roles Chelabase owns), or any `pg_*` role;
* is a member's personal login role;
* doesn't exist in the branch's database;
* is a superuser, or a member (at any depth) of a superuser role, `neon_superuser`, `chelabase_admin`, `pg_execute_server_program`, `pg_read_server_files` or `pg_write_server_files`;
* is owner-level: it has `CREATEROLE`, `CREATEDB` or `REPLICATION` (`the role <name> has CREATEROLE, CREATEDB or REPLICATION (owner-level roles can't back a secret key; use a role without CREATEROLE, CREATEDB, REPLICATION or neon_superuser: BYPASSRLS is allowed)`).

Any other role can back a key, with or without a password: the usual one is a `NOLOGIN` role made for the data API. You need Developer access and a role you may use; a `LOGIN` role needs Admin. Otherwise the answer is 403, naming the role you'd need. A role that is, or is a member of, a role with the members hub's "all rows" policy is refused with 422 `exposes_data_api`.

A role with `BYPASSRLS` can back a secret key: requests with that key read and write past row-level security, like a service role. The key list marks such a key with `bypasses_rls: true` (Studio shows "bypasses RLS"); the mark follows the role, so it changes within a few seconds when `BYPASSRLS` is granted or taken away in SQL.

The same rules hold on every request. A role made owner-level in SQL after its key was made (`ALTER ROLE … CREATEDB`, say, or a `GRANT` that reaches `neon_superuser` or one of the system roles above) stops working: requests get a 403 `role_not_allowed`, within about 30 seconds of the change, until the attribute is taken away again. The key list marks such a key with `role_blocked: true`.

A secret key belongs to the role itself, not to its name: Chelabase stores the role's identity (its `pg_roles` OID) with the key. A key works only while the role with that identity exists under the key's role name. If the role is dropped, or dropped and created again under the same name, the key stops working at once (requests get a 503 `unavailable`: `the key's role <name> can't be used right now`), and the next role sync revokes it. A role renamed in SQL takes its keys with it. A reset or restore (a merge's reset too) brings back roles from another point in history, where the same identity can belong to a different role: a key survives it only if its role there has both the same identity and the same name, and is revoked otherwise, so it never passes to another role.

Rotating a secret key keeps its name and its role, and checks the role on the branch's compute again, as creation does. If the role was renamed, the key follows the rename and rotation checks the new name. A refused or missing role is a 422 `role_refused`, and so is a role that was dropped and created again since the key was made (create a new key for it); a branch that isn't ready is a 409 `branch_not_ready`; a worker that can't be reached is a 503 `worker_unavailable` (or a 504 `timeout`), and a branch whose roles are being changed at that moment a 503 `unavailable` with `Retry-After`. The old key stays live whenever rotation fails.

SQL can't drop a key's role (or drop and create it again in one transaction) until its keys are revoked. A reset or restore rewinds the grant, so the worker's bootstrap grants each live secret key's role again. A role that is gone or now refused is skipped with a warning, and its key stops working.

## Revocation [#revocation]

Revoking a key takes effect within 30 seconds, the gateway's cache lifetime. A key that was never valid is cached for 30 seconds too. Cache misses (lookups that reach the orchestrator) are limited per client IP address; see [Limits](/using/data-api/limits).

## Lifecycle [#lifecycle]

Keys belong to the branch, not to its timeline, so reset and restore keep them. Deleting a branch deletes its keys.
