Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.insforge.dev/llms.txt

Use this file to discover all available pages before exploring further.

Overview

A branch is an isolated, full copy of your project — its own database, auth config, storage buckets, edge functions, email templates, realtime channels, and schedules. You can change anything on the branch without touching the parent. When you’re happy with the result, merge it back. When you’re not, reset it and try again. Branching is available on InsForge OSS 2.1.0+, from the dashboard and from @insforge/cli.
Although this page lives under Database, a branch captures more than just the database. The schema and migrations are usually the reason you reach for a branch, but auth providers, storage buckets, edge functions, email templates, realtime channels, and schedules all branch with it.

What a Branch Captures

A branch is a real child project — its own EC2 instance, its own Postgres, its own state. Writes against the branch are tracked separately from the parent. Branched state:
  • Database schema — DDL, RLS policies, triggers, functions, indexes, sequences, enum types, extensions
  • Auth config and OAuth providers
  • Storage buckets and bucket config
  • Edge function source
  • Email config and templates
  • Realtime config and channels
  • Schedules (cron jobs)
Row-level inserts in your application tables are isolated to the branch like everything else. The branch shares the parent’s JWT_SECRET, so users authenticated against the parent are still authenticated on the branch with the same tokens — but it gets its own API_KEY / ANON_KEY. Website and Vercel deployments, plus Fly.io compute services, are not branched. If you need them for a branch, redeploy against the branch’s API_KEY.

When To Use a Branch

Reach for a branch when the change is risky enough that a failed attempt on production would hurt — and when re-creating the change as one clean migration after the fact is preferable to leaving partial state behind. Good fits:
  • Schema migrations you’re unsure about. Try the migration on a copy of real data, watch what breaks, iterate, then merge the final version.
  • RLS policy rewrites. Validate that policies still permit your golden-path queries before they touch production traffic.
  • Auth config changes. Add a new OAuth provider, change redirect URIs, or rotate email templates with a real test user account, not a guess.
  • Edge function rewrites. Refactor a function and run it end-to-end against branched data.
  • Backend upgrade rehearsals. Verify a migration sequence on production-shaped data before a maintenance window.
  • Per-feature sandboxes. Long-lived feature branches that accumulate schema and config changes alongside an application branch.
Skip a branch for:
  • Data backfills. Branches are for schema and configuration changes. User-data inserts on a branch are not auto-merged — write a one-off migration instead.
  • Trivial changes you’re confident in. A new nullable column with a default doesn’t need a branch.
  • Anything outside the branched scope. Compute services and frontend deployments don’t branch.

Full vs. Schema-Only

When you create a branch you choose how much state comes along:
  • full (entire schema and data) — copies the parent’s complete database (schema + data) at the moment of branch creation. Best when you need realistic data to test against.
  • schema-only — copies the schema and the platform’s mergeable config (auth providers, storage buckets, edge function definitions, …) but leaves user-data tables empty. Faster, lighter, and avoids bringing production rows into a sandbox.
Both modes restore the parent’s state once at create time. After that the branch runs on its own infrastructure with no live coupling — drift between parent and branch is what merge later resolves.

Branch Lifecycle

A branch moves through a small number of states:
StateMeaning
creatingParent is being dumped and restored onto the branch.
readyThe branch is usable — edit anything, run code against it.
mergingA merge is in flight; branch edits are paused.
mergedA merge completed; the branch is read-only but still around.
pausedBranch inherited the parent’s paused state; resume it explicitly when you need it again.
deletedTombstoned, EC2 and S3 cleaned up.
ready is where you do nearly all of your work. merged lets you keep iterating with reset if you want another round after a successful merge. delete is always explicit — a successful merge does not auto-delete the branch.

Managing Branches

You can manage branches from the dashboard or the CLI. The two surfaces do the same thing — pick whichever fits your workflow.

From the Dashboard

Open a project, then use the Branches panel to:
  • Create a new branch from the current project, choosing full or schema-only.
  • Switch into an existing branch — its own dashboard view with all the project’s features.
  • Merge a branch back to the parent. You’ll see the merge diff as a SQL preview before anything is applied; conflicts are surfaced inline.
  • Reset a branch to the snapshot taken when it was created.
  • Delete a branch when you’re done.

From the CLI

# Create a branch from the currently linked project.
npx @insforge/cli branch create feat-billing --mode full

# List branches of the linked parent.
npx @insforge/cli branch list

# Preview the merge as SQL — nothing applied yet.
npx @insforge/cli branch merge feat-billing --dry-run --save-sql ./preview.sql

# Apply the merge.
npx @insforge/cli branch merge feat-billing

# Roll the branch back to the moment it was created.
npx @insforge/cli branch reset feat-billing

# Tear down the branch.
npx @insforge/cli branch delete feat-billing
See the npx @insforge/cli branch --help for the full set of flags.

How Merge Works

branch merge doesn’t just replay your branch’s writes on top of the parent. It computes a three-way diff between:
  1. Parent at T0 — the parent’s state when the branch was created (captured in branch metadata).
  2. Parent now — the parent’s live state.
  3. Branch now — the branch’s live state.
For each object (table, RLS policy, function, mergeable config row), the diff decides:
  • Branch changed, parent unchanged → apply the branch’s version.
  • Parent changed, branch unchanged → no-op; parent’s version stays.
  • Both changed to the same result → no-op.
  • Both changed to different results → conflict; merge is blocked.
When a merge is blocked, you get a structured report of which object diverged and why. Resolve on the branch (or reset and start over), then merge again. The merge SQL is rendered as a single migration-style script with [DDL] / [DATA] / [MIGRATION] sections so you can review it before applying. Platform config tables (auth providers, storage buckets, email templates, edge function definitions, schedules) merge by stable key. User-data rows on the branch are not auto-merged — branches are for schema and configuration, not data backfills.

Quotas and Limits

These limits have not been finalized and may change in the future.
  • Up to 3 parent projects per organization can have at least one active branch.
  • Up to 2 active branches per parent project.
  • Nested branches (a branch of a branch) are not supported.
  • When the parent is paused, its branches pause too. Resuming the parent does not auto-resume branches — resume each one explicitly when you need it.
  • When the parent is deleted, all of its branches are deleted with it.
  • Database Migrations — the forward-only SQL files you’ll typically iterate on inside a branch before merging.
  • Database Architecture — what’s running under each branch’s Postgres + PostgREST stack.