Skip to content

Branching Security

This guide covers security considerations and best practices for database branching.

When a user creates a branch, they automatically receive admin access. This cannot be revoked without deleting the branch.

LevelPermissions
readQuery branch data
writeRead + insert/update/delete data
adminWrite + reset/delete branch

Service keys have full access to all branches, including:

  • Creating and deleting any branch
  • Resetting any branch
  • Accessing any branch’s data

Recommendation: Don’t expose service keys to untrusted applications.

Users with dashboard_admin or admin role have full access to all branches.

Each branch is a completely separate PostgreSQL database:

PostgreSQL Server
├── fluxbase (main)
├── branch_feature_login
├── branch_pr_42
└── branch_staging

Benefits:

  • No accidental cross-branch data access
  • Independent connection pools
  • Complete schema isolation

Branches are accessed via:

  1. HTTP Header: X-Fluxbase-Branch: branch-slug
  2. Query Parameter: ?branch=branch-slug

The router validates the branch exists and the user has access before routing.

Always configure a webhook secret:

Terminal window
# Generate a secure secret
openssl rand -hex 32
# Configure in Fluxbase
curl -X POST http://localhost:8080/api/v1/admin/branches/github/configs \
-H "Authorization: Bearer $SERVICE_KEY" \
-H "Content-Type: application/json" \
-d '{
"repository": "owner/repo",
"webhook_secret": "your-secret-here"
}'

Webhooks from unconfigured repositories are rejected by default. This prevents:

  • Unauthorized branch creation
  • Resource exhaustion attacks
  • Abuse of the webhook endpoint

For additional security, configure your firewall to only accept webhooks from GitHub’s IP ranges:

  • 140.82.112.0/20
  • 143.55.64.0/20
  • 192.30.252.0/22

Check GitHub’s documentation for current ranges.

ModeData CopiedUse Case
schema_onlySchema onlyDevelopment, testing
full_cloneAll dataStaging, data analysis

Recommendation: Use schema_only for PR previews to avoid copying sensitive production data.

Data in branches:

  • Is isolated from other branches
  • Is deleted when the branch is deleted
  • Is not backed up separately (branches use PostgreSQL templates)

Avoid copying production data to preview branches:

branching:
default_data_clone_mode: schema_only # Don't copy production data

If you need test data, use seed scripts instead of full_clone.

Configure limits to prevent resource exhaustion:

branching:
max_branches_per_user: 5 # Per user limit
max_total_branches: 50 # System-wide limit
auto_delete_after: 24h # Auto-cleanup for preview branches

Each branch has its own connection pool:

  • Max 10 connections per branch
  • 30-minute connection lifetime
  • Pools are created on-demand

Branch databases consume:

  • Disk space (schema + data if full_clone)
  • Connection slots
  • PostgreSQL resources

Monitor usage and clean up unused branches.

Grant only necessary access levels:

-- Read-only access for testers
INSERT INTO branching.branch_access (branch_id, user_id, access_level)
VALUES ('...', 'tester-uuid', 'read');

Implement regular cleanup of expired branches:

Terminal window
# Manual cleanup
curl -X DELETE http://localhost:8080/api/v1/admin/branches/cleanup \
-H "Authorization: Bearer $SERVICE_KEY"

Monitor branch operations in logs:

Terminal window
# View recent branch activity
fluxbase branch activity feature-login

Use separate service keys for:

  • GitHub webhook integration
  • CI/CD pipelines
  • Administrative operations

The admin_database_url requires CREATE DATABASE privileges:

branching:
admin_database_url: "postgresql://branching_admin:password@localhost:5432/postgres"

Security recommendations:

  • Use a dedicated PostgreSQL role with minimal privileges
  • Store credentials in environment variables or secrets manager
  • Don’t use superuser credentials
-- Create dedicated role for branching
CREATE ROLE branching_admin WITH LOGIN PASSWORD 'secure-password';
GRANT CREATE ON DATABASE postgres TO branching_admin;

Branch connection URLs are derived from the main database URL. They inherit:

  • Authentication credentials
  • SSL settings
  • Connection parameters

All branch operations are logged to the branching.activity_log table:

ActionDescription
createdBranch was created
deletedBranch was deleted
resetBranch was reset to parent state
clonedData was cloned from parent
migratedMigrations were applied
access_grantedUser was granted access
access_revokedUser’s access was revoked

Each activity includes:

  • executed_by - User ID who performed the action
  • status - started, success, or failed
  • details - JSON with additional context
  • duration_ms - Time taken (for long operations)
SELECT * FROM branching.activity_log
WHERE branch_id = '...'
ORDER BY executed_at DESC;
-- Active branches by type
SELECT type, status, COUNT(*)
FROM branching.branches
WHERE status != 'deleted'
GROUP BY type, status;
-- Branches per user
SELECT created_by, COUNT(*)
FROM branching.branches
WHERE status != 'deleted'
GROUP BY created_by;
-- Expired but not deleted
SELECT * FROM branching.branches
WHERE expires_at < NOW()
AND status != 'deleted';
  1. Migrations - Branches don’t support running migrations via the API independently. Workaround: connect directly to the branch database using psql or database tools.
  2. RLS in Branches - RLS policies are copied but may need adjustment
  3. Extensions - PostgreSQL extensions must be installed server-wide
  4. Sequences - Sequence values are reset in schema_only mode
  • REST API on Branches - Use X-Fluxbase-Branch header or ?branch= parameter to query data on non-main branches
  • Branch Access Control - Grant read/write/admin access to specific users
  • Audit Logging - All branch operations are logged for security monitoring
  • Branch owners have full control over their branches
  • Service keys can access all branches
  • Deleted branch data is not recoverable
  • Child branches become orphaned if parent is deleted