# Branches (/docs/using/branches)



Every database can have branches: copies of another branch's data, each with its own Neon timeline, endpoint, compute container and `postgres` password. Main is itself a branch, created when the database is provisioned. Branches form a tree: a branch can be created from any other branch.

## Limits and settings [#limits-and-settings]

Both settings are changed with `PATCH /v1/databases/{id}`, or in Studio's database settings.

| Setting                    | Default | Range             | What it does                                                                                                                                                                           |
| -------------------------- | ------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `max_branches`             | 5       | 0 to 20           | The most live normal branches a database may have, not counting main or backup branches. Checked when a branch is created; a create over the cap is refused with `branch_cap_reached`. |
| `history_window` (seconds) | 7 days  | 1 hour to 30 days | How far back you can restore or branch from. It is sent to Neon's storage controller, and shrinking it lets the pageserver remove older history.                                       |

A branch that failed stays listed as `failed` and counts toward the cap until you delete it.

## Create a branch [#create-a-branch]

`POST /v1/databases/{id}/branches` takes a `name`, an optional `parent_branch_id` (main when omitted), and an optional point in time `at` (the parent's present when omitted), given as `{"timestamp": "<RFC 3339>"}` or `{"lsn": "<lsn>"}`.

The create is asynchronous. The branch appears at once as `pending`, moves to `provisioning`, and is `ready` when its compute is up.

A point the history can't serve fails the branch with its reason in `error`:

* outside the window: `outside the history window; the oldest available point is <time>`. That is the time of the oldest commit Neon still keeps, or the source branch's creation time when there is no commit yet (a database younger than its window);
* before the source branch's own history begins (it starts where the source was branched from its parent): `before <source>'s history begins (<time>); branch from <its parent> instead`, or, for a restore backup, `before the history of <backup> begins (<time>)`.

Restoring a branch to such a point fails the same way. Studio's time pickers start at the later of the window's start and the branch's `history_start`, which the API returns (`null` when unknown).

A storage-controller failure that may pass (a 5xx, no answer or a timeout) is retried: the job is delivered 5 times in all, 30 seconds apart, enough for a storage-controller restart, before the branch fails with it. A refusal (4xx) fails at once. Reset to parent and restore retry the same way before they switch timelines.

## Reset to parent [#reset-to-parent]

`POST /v1/databases/{id}/branches/{branch_id}/reset-to-parent` moves a branch's timeline to its parent's latest state. The branch keeps its connection string and password.

## Restore [#restore]

`POST /v1/databases/{id}/branches/{branch_id}/restore` takes a branch back to a point in time on a new timeline. The branch keeps its connection string and its `postgres` password.

Restore also creates a backup branch, `<name>-before-restore-<yyyymmddThhmmssZ>`, that holds the old timeline for recovery. A backup has no compute; to connect to it, create a branch from it. Backup branches never count toward the cap, can't be reset, restored or protected, and are deleted when their branch is deleted.

Restore works the same on main and on a branch of a branch at any depth: the restored timeline gets its own copy of what it reads from every ancestor up to its point, so it no longer depends on them. The backup keeps the old timeline where it was.

A restore right after a storage restart can wait up to 30 seconds per ancestor level in the restore's depth before retrying. A restore that stays stuck fails the branch with a message ending "delete the branch to remove it"; such a branch can only be deleted. A detach that times out is never undone while it may still be running: the job is retried, and the retry finishes the detach. If it is still unresolved 20 minutes after the restore made its timeline, the branch fails with the recorded cause, and nothing is deleted: the branch keeps its old timeline.

## Delete, and the children rule [#delete-and-the-children-rule]

Delete, reset-to-parent and restore are refused (`has_children`) while the branch has live normal child branches. Each child's timeline branches from its parent's, and Neon refuses to delete a timeline that others branch from, so the children go first. Backup branches never count as children. Main can't be deleted or reset.

They are also refused (409 `branch_merge_busy`) while a [schema merge](/docs/using/merging-schema#while-a-merge-runs) holds the branch as its source or target: it is rehearsing or applying, or it failed half applied and waits to be retried or abandoned.

## Passwords on a new branch [#passwords-on-a-new-branch]

* The owner `postgres` gets its own password on every branch.
* Every other role keeps the parent branch's password, unless the parent is **protected**.
* From a protected branch, a child gives every role in its own database that has a password a fresh generated, revealable one. That covers roles made in Studio, through the API or in SQL, MD5 ones and roles without `LOGIN` included, and also a role the parent has dropped since the child's point in time.

For roles in general, see [Login roles](/docs/using/login-roles).

## Protected branches [#protected-branches]

Protect a branch in Studio's branch settings, or with `PATCH /v1/databases/{id}/branches/{branch_id}` and the body `{"protected": true}` (`false` unprotects it). Main can be protected; backup branches can't.

* Delete and reset-to-parent are refused with 409 `branch_protected` while a branch is protected.
* Deleting the database, or the project that holds it, is refused too.
* Restore is allowed. The owner and the roles made in Studio or through the API keep their current passwords; roles made in SQL are as they were at the restore point, passwords included (a role made after it is gone).

### What protecting blocks [#what-protecting-blocks]

A protected branch refuses schema changes from member logins that aren't Admins. A member login is the login a project member gets on a branch (`cb_m_<id>`). Postgres refuses the statement with SQLSTATE `42501` and the hint `chelabase:branch_protected`. Temporary objects, `REFRESH MATERIALIZED VIEW` and `REINDEX` stay allowed for everyone.

Each branch also has a **guard login roles** switch (`guard_logins`). When the switch is on and the branch is protected, every login role on the branch is refused schema changes in the same way, from its first statement, including roles created later from SQL. Superusers, the owner, the member logins of Admins and the roles exempted on that branch are not refused. The member logins of non-Admins are refused whatever the switch says.

The guard is in every database of the branch, and in `template1`. On a protected branch a non-superuser's `CREATE DATABASE` or `ALTER DATABASE` that sets `ALLOW_CONNECTIONS` or `IS_TEMPLATE` is refused with `42501` and the hint `chelabase:branch_protected`. Every other database a non-superuser creates there is born closed, whichever database the session is in, and opens once Chelabase has installed the guard there, usually within seconds. A database closed before the branch was protected stays closed while the branch is protected; unprotect the branch to reopen it. Renaming the branch's main database, or renaming another database to its name, is refused for non-superusers.

### Exemptions [#exemptions]

Exemptions are per branch and are listed by role name. `GET /v1/databases/{id}/branches/{branch_id}/guard-exemptions` lists them. `PUT .../guard-exemptions/{role}` adds a name and `DELETE .../guard-exemptions/{role}` removes it; both are idempotent. In Studio, the branch's roles list has a "Login guard" column with an "Exempt `<name>` from the login guard" switch on each login role. A name may be listed before the role exists, and it is 1 to 63 bytes with no NUL. Because exemptions match by name, a role that is dropped and re-created keeps its exemption, and a renamed role loses it.

An exemption is honored only for a role that exists and that no guarded role can administer: if a guarded role holds ADMIN on it, directly or through a chain of role memberships, the exemption is not in effect (the platform logs a warning). Otherwise a guarded role could take the exempt role over, or create a role under an exempt name and log in as it. A role a guarded role creates is administered by its creator, so its exemption isn't honored.

The switch and the exemptions can be set on any branch. They take effect only while the branch is protected.

### Where the settings come from [#where-the-settings-come-from]

A new branch copies its parent's switch and exemptions when it is created. The copy is dormant until the branch is protected. A branch with no parent (main) starts with the switch on and no exemptions.

### The review [#the-review]

Protecting also requires a review. The review lists code owned by an untrusted role that can run in an Admin's session, because that code can change the schema when an Admin runs something. Only superusers and the owner role itself are trusted; any other role is untrusted, including members of the owner (the guard treats them the same way). The review is a catalog scan of every database on the branch. It lists:

* triggers and rules whose function or table is owned by an untrusted role;
* SECURITY DEFINER functions owned by an untrusted role;
* functions owned by an untrusted role that are reached from policies, column and domain defaults, generated columns, CHECK constraints, index expressions and predicates, views, operators, range types, triggers, rules, or other listed functions, including the functions a SECURITY DEFINER function calls.

Objects from extensions, and objects in `pg_catalog`, `information_schema` and the platform's reserved schemas, are skipped. A PL/pgSQL or dynamic body can't be followed past its text, so such a function is listed by its reach and marked as opaque. Each item shows its database, kind, schema, name, identity, owner, the reasons it is listed, and a stable key.

The review has a hash, computed over the current list. To protect a branch whose list isn't empty, send the hash as `review_hash`. A missing or stale hash is refused with 409 `review_changed`, and the response's `error.review` holds the current review. Protecting stores the hash as acknowledged. An item that wasn't in the list last acknowledged is marked `new`. `POST .../review/acknowledge` with `{"hash": "..."}` stores the current list as acknowledged without changing anything. Studio's branch list shows a "Review changed" badge on a protected branch whose current hash differs from the acknowledged one, and "Review unavailable" when the review can't be loaded (for example, the branch's compute isn't running). The hash covers each item's definition too, so replacing a function's body in place changes it.

### Fixing an item [#fixing-an-item]

Each fix works on one item and carries the item's key and the review hash. A stale hash is refused with 409 `review_changed`. A fix runs with the platform's rights, not the owner's, so that it can reach an object the owner isn't a member of. It can only touch items that the scan found.

* **Reassign** gives the code to the branch owner. For a trigger, the function is reassigned; for a rule, its table is reassigned. The owner then holds that code, and a SECURITY DEFINER function runs as the owner from then on. The Admin who reassigns answers for what the code does. Studio warns about this before the fix runs.
* **Drop** removes a trigger, rule or function with `RESTRICT`. If other objects depend on it, the fix is refused with 409 `has_dependents`, and the message names them. Nothing is removed with them.

Each fix runs in its own transaction with a 5-second lock timeout (409 `lock_timeout`; retry it). A Postgres refusal is returned as 400 `schema_change_failed`, with the SQLSTATE and message. A successful fix returns the SQL it ran and the new review.

The review and its fixes need the branch's compute to be running. When it isn't, the scan answers 503 `unavailable`.

### Routes [#routes]

All routes are under `/v1/databases/{id}/branches/{branch_id}`. Reads need the Developer role on the project; writes need Admin.

| Method and path                   | Access    | What it does                                                                                                                                                                           |
| --------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PATCH`                           | Admin     | Sets `protected`, `guard_logins` or `review_hash`. At least one of `protected` and `guard_logins` is needed, and `review_hash` is accepted only with `protected: true`; otherwise 422. |
| `GET /review`                     | Developer | Returns the review, its hash, the acknowledged hash and each item's `new` flag.                                                                                                        |
| `POST /review/acknowledge`        | Admin     | Body `{"hash"}`. Stores the current review as acknowledged.                                                                                                                            |
| `POST /review/fix`                | Admin     | Body `{"key", "action", "hash"}`, where `action` is `reassign` or `drop`.                                                                                                              |
| `GET /guard-exemptions`           | Developer | Lists the exempt role names.                                                                                                                                                           |
| `PUT /guard-exemptions/{role}`    | Admin     | Exempts a role name on this branch.                                                                                                                                                    |
| `DELETE /guard-exemptions/{role}` | Admin     | Removes an exemption.                                                                                                                                                                  |

Each write that changes the switch or the exemptions updates the branch's member roles. The errors that matter here are `review_changed` (409, with `error.review`), `has_dependents` (409, on a drop), `lock_timeout` (409, retry) and `schema_change_failed` (400, with `sqlstate`). See the [error reference](/docs/reference/errors) for the full list.
