# Scribase documentation (full text) > Scribase is an application cloud built for coding agents: Postgres, Auth, Storage, Realtime and Edge Functions per project, managed through a /v1 control API, a CLI, an SDK and an MCP server. An agent goes from an empty folder to a live backend with `npx scribase init --temp --yes --json`, no account and no pasted key. --- Source: https://docs.scribase.com/docs/hosted # Hosted quickstart From "no account" to a live backend with its URL and key in your app, in about a minute. There is nothing to configure: region, database engine and keys are handled for you. ## 1. Sign up Open [console.scribase.com/signup](https://console.scribase.com/signup) and choose **Continue with Google**, **Continue with GitHub**, or an email and a password. ## 2. Add a card Hosted Scribase starts with a one-time **$1 card check**, paid through Creem (our merchant of record) and **credited to your organization's balance**. Your first project also gets a **7-day trial credit**, so it is live right away. After that each project costs $9 a month, charged daily from the balance (see [Pricing and billing](https://docs.scribase.com/docs/pricing.md)). ## 3. Your backend is live When you come back from checkout the console creates, without asking anything: - your **organization**, named from your email (`ada@acme.io` becomes `acme`); - your first **project**, `my-app`; - its **`production`** environment: Postgres, Auth, Storage, Realtime (and Functions where the deployment runs them). The **Your backend is live** screen shows the **Project URL** and the **publishable key** with one-click copy, ready-to-paste setup for supabase-js, Expo / React Native, Next.js and curl, and each service's status as it wakes. You never create keys or secrets yourself. ```ts import { createClient } from '@supabase/supabase-js' export const supabase = createClient('', '') ``` For Expo put them in `.env` as `EXPO_PUBLIC_SUPABASE_URL` and `EXPO_PUBLIC_SUPABASE_ANON_KEY`; for Next.js as `NEXT_PUBLIC_SUPABASE_URL` and `NEXT_PUBLIC_SUPABASE_ANON_KEY`. The data plane is wire-compatible with Supabase, so nothing else in your app changes. ## 4. Moving from Supabase? One field Open **Move an existing Supabase project here** (or [console.scribase.com/move](https://console.scribase.com/move)) and paste the database connection string from Supabase dashboard > **Connect**. The move starts on paste and shows its progress: tables, rows, users (with their password hashes, so they keep signing in) and row-level security policies. To copy storage files too, tick **Also copy storage files** and add the source project's `service_role` key. Your Supabase project is only read. When it finishes, the console shows the two values to switch your app to (`SUPABASE_URL`, `SUPABASE_ANON_KEY`); from a terminal, `scribase apps switch ` prints the same for Expo. ## Pricing and limits There is no project limit. Each project costs **$9 a month**, charged daily ($9 / 30 per project per day) from your organization's prepaid balance, or the **Unlimited** plan covers any number of projects for **$299 a month**. Idle projects scale to zero. The console's **Billing** page shows your balance, "N projects × $9/mo", every daily charge, a **Top up** button and the **auto top-up** setting. Details, including what happens when the balance runs out, are in [Pricing and billing](https://docs.scribase.com/docs/pricing.md) and on [scribase.com/pricing](https://scribase.com/pricing). ## Manage it from code (optional) Everything above is also available over the `/v1` API, the SDK and the CLI. | You need | Value | |---|---| | Control API (`SCRIBASE_API_URL`) | `https://api.scribase.com` | | Credential (`SCRIBASE_ACCESS_TOKEN`) | A personal access token or organization API key, created below | The examples below use organization `acme`, project `store` and environment `production`; use the identifiers the console shows for yours. ### Create an access token In the console, open **Account → Access tokens** and create a token. Copy it when it is shown — the secret is displayed once and stored only as a hash. For CI and services, prefer an **organization API key** (**Organization → API tokens**) scoped to what the job needs: `read`, `deploy`, or `admin`. Both are also available over the API; see [Access tokens & API keys](https://docs.scribase.com/docs/api/tokens.md). ```sh export SCRIBASE_API_URL=https://api.scribase.com export SCRIBASE_ACCESS_TOKEN=scb_pat_... # the token you just copied ``` ### Make your first call With plain HTTP: ```sh curl -H "Authorization: Bearer $SCRIBASE_ACCESS_TOKEN" \ "$SCRIBASE_API_URL/v1/environments?organization_id=acme" ``` With the SDK (`scribase` on npm is not yet published; see [SDK installation](https://docs.scribase.com/docs/sdk.md#installation) for installing it from the repository): ```ts import { ScribaseClient } from 'scribase'; const scribase = new ScribaseClient({ baseUrl: process.env.SCRIBASE_API_URL ?? 'https://api.scribase.com', token: process.env.SCRIBASE_ACCESS_TOKEN ?? '', }); const environments = await scribase.environments.list({ organizationId: 'acme' }); console.log(environments.map((env) => `${env.environment_id}: ${env.phase}`)); ``` With the CLI (see [installation](https://docs.scribase.com/docs/getting-started.md)): ```sh scribase doctor # checks the URL and token scribase env get acme store production ``` ### Create a preview for a pull request ```sh # env create [ttl-hours] scribase env create acme store pr-42 preview sanitized us-east create-pr-42 72 scribase operation get acme scribase branch register acme store pr-42 --base production ``` The preview is swept automatically when its TTL expires. Renew it with `POST .../environments/pr-42/renew` or `scribase.previews.renew(...)`. ### Apply migrations ```sh scribase migrate acme store pr-42 --dir ./migrations # plan and lint only scribase migrate acme store pr-42 --dir ./migrations --apply # run them ``` ## Next steps - [SDK reference](https://docs.scribase.com/docs/sdk.md) — every resource with examples - [Backups & restore](https://docs.scribase.com/docs/api/backups.md) — verified backups and restore drills - [Exports](https://docs.scribase.com/docs/api/exports.md) — take the whole stack with you - [Import from Supabase](https://docs.scribase.com/docs/cli/import.md) — move an existing project in --- Source: https://docs.scribase.com/docs/frameworks # Frameworks `npx scribase init` detects four project types from `package.json` and config files, in this order: | Detected as | When | Env file | Variables | Client file | |---|---|---|---|---| | `next` | `next` dependency or `next.config.*` | `.env.local` | `NEXT_PUBLIC_SUPABASE_URL`, `NEXT_PUBLIC_SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY` | `lib/supabase.ts` (`src/lib/` with a `src/` folder) | | `expo` | `expo` dependency, or `app.json` plus `react-native` | `.env` | `EXPO_PUBLIC_SUPABASE_URL`, `EXPO_PUBLIC_SUPABASE_ANON_KEY` | `lib/supabase.ts` (`src/lib/` with a `src/` folder) | | `vite` | `vite` dependency or `vite.config.*` | `.env.local` | `VITE_SUPABASE_URL`, `VITE_SUPABASE_ANON_KEY` | `src/lib/supabase.ts` | | `node` | anything else | `.env` | `SUPABASE_URL`, `SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY` | `supabase.ts`, or `supabase.mjs` without `tsconfig.json` (under `src/` when it exists) | Every run also writes `SCRIBASE_API_URL`, `SCRIBASE_ORG`, `SCRIBASE_PROJECT` and `SCRIBASE_ENV`, adds the env file and `.scribase/` to `.gitignore`, and installs `@supabase/supabase-js` with your package manager. Expo and Vite projects never get the service role key, because their env values can end up in the client bundle. The client file uses `.js` instead of `.ts` without a `tsconfig.json`. An existing client file is kept. ## Per framework | Framework | Detected as | Works as written? | The one edit | |---|---|---|---| | [Next.js](https://scribase.com/frameworks/nextjs) | `next` | Yes | None | | [Expo](https://scribase.com/frameworks/expo) | `expo` | Yes | Optional: pass `AsyncStorage` as `auth.storage` to keep sessions | | [React Native (bare CLI)](https://scribase.com/frameworks/react-native) | `expo` | Needs one edit | Metro without Expo does not inline `EXPO_PUBLIC_` values: put the URL and publishable key in `lib/supabase.ts` as constants | | [Vite + React](https://scribase.com/frameworks/vite-react) | `vite` | Yes | None | | [SvelteKit](https://scribase.com/frameworks/sveltekit) | `vite` | Yes | None; import as `$lib/supabase` | | [Nuxt](https://scribase.com/frameworks/nuxt) | `node` | Needs one edit | Expose the pair through `runtimeConfig.public` and create the client in a composable | | [Astro](https://scribase.com/frameworks/astro) | `node` | Needs one edit | Read `import.meta.env.SUPABASE_URL` instead of `process.env` | | [Remix / React Router](https://scribase.com/frameworks/remix) | `vite` | Yes | Optional: move the client under `app/lib/` | | [Node.js / Express](https://scribase.com/frameworks/express) | `node` | Yes | Start with `node --env-file=.env` | | [Flutter](https://scribase.com/frameworks/flutter) | not supported | Manual | Use `supabase_flutter` with the URL and publishable key from the console, passed with `--dart-define` | Each linked guide shows the real terminal output, the env file, a first query and a prompt to paste into your coding agent. ## For coding agents Run `npx scribase init --yes --json` in the project root. On success it prints one JSON object with `framework`, `env_file`, `env_vars`, `client_file` and `api_url`. If nobody is signed in it returns `{"ok":false,"error":{"code":"authorization_pending",...}}` with a `verification_uri_complete` and `user_code`: show those to the person, wait for approval, and run the same command again. Never print the env file. After init, create tables with the MCP server or migrations (see [Connect your agent](https://docs.scribase.com/docs/agents/setup.md)), keep row-level security on for every table the publishable key can reach, and use the service role key only in server code. --- Source: https://docs.scribase.com/docs/pricing # Pricing and billing Scribase has **no project limit**. You pay for the projects that exist: | Option | Price | What it covers | |---|---|---| | **Per project** (default) | **$9 per project per month** | Postgres, Auth, Storage, Realtime, Functions and previews for each project | | **Unlimited** | **$299 per month** | Any number of projects, under pooled fair-use quotas | Idle projects scale to zero and wake on the next request; they are not paused while your balance covers them. Self-hosting Scribase is free (Apache-2.0). ## How the balance works 1. **Sign up and a $1 card check.** A one-time $1 payment through Creem (our merchant of record) verifies your card and is credited 1:1 to your organization's balance. 2. **Trial credit.** After the card check your first project gets a **7-day trial credit**, so it is live immediately. 3. **Daily charges.** Once a day, every project that exists (running or scaled to zero) is charged **price / 30** from the balance: $0.30 a day at $9/month. Each project is charged at most once per day, so a retry never double-charges. On Unlimited the organization is charged $299 / 30 per day instead, whatever the project count. Delete a project and its charges stop from the next day. 4. **Top-ups.** **Top up** in Billing opens a one-click checkout for the amount you choose; the amount is credited as soon as the payment completes. 5. **Auto top-up.** Turn it on in Billing with a threshold and an amount. When the balance drops below the threshold, the console shows a one-click checkout for that amount. Creem cannot charge a saved card off-session for one-time payments, so **auto top-up is a prompted one-click checkout, not an automatic charge**: nothing is charged until you click it. 6. **Grace period, then pause.** If the balance reaches zero, projects keep running for **72 hours**. After that they **pause**: compute scales to zero and all data is kept. New projects cannot be created while paused. 7. **Resume.** A top-up that brings the balance above zero resumes paused projects right away. The console **Billing** page shows the balance, "N projects × $9/mo = $X", a per-project ledger of daily charges, the **Top up** button and the **auto top-up** toggle. When the per-project total would be more than $299, it suggests switching to Unlimited and shows how much you would save. ## Temporary agent projects An agent can create a temporary project with `POST /v1/temporary-projects` (see [Temporary databases](https://docs.scribase.com/docs/agents/temporary-databases.md)). Temporary projects are **free for 72 hours**. Claim one to keep it; billing starts from the claim. An unclaimed temporary project is removed when it expires. ## Unlimited fair use Unlimited quotas are pooled across the organization, per calendar month: | Resource | Fair use | |---|---| | Database | 500 GB | | File storage | 2 TB | | Egress | 5 TB | | Compute hours | 50,000 | Each environment still has its own hard caps (from `config/plans.json`), and capacity packs add room to one environment. ## API All routes are under `/v1/organizations/{org}/` and use the same bearer as the rest of the API. | Route | Who | What | |---|---|---| | `GET billing/balance` | Any member | Balance, price, billable projects, monthly estimate, status (active, grace, paused), auto top-up settings, pending top-up prompt, Unlimited savings | | `GET billing/ledger` | Any member | Credits and daily charges, per project | | `POST billing/top-up` | Owner, admin | `{"amount_cents":2000,"success_url":"https://..."}` returns a `checkout_url` | | `POST billing/top-up-confirm` | Any member | `{"query":"..."}` from the signed success redirect; idempotent | | `PUT billing/settings` | Owner, admin | Auto top-up (`auto_top_up_enabled`, `auto_top_up_threshold_cents`, `auto_top_up_amount_cents`) and `billing_model` (`per_project` or `unlimited`) | ## Operator configuration Hosted billing is configured with environment variables on the control API. | Variable | Default | Meaning | |---|---|---| | `SCRIBASE_PRICE_PER_PROJECT_CENTS` | `900` | Monthly price of one project; charged daily at price / 30 | | `SCRIBASE_UNLIMITED_PLAN_CENTS` | `29900` | Monthly price of the Unlimited plan; charged daily at price / 30 | | `SCRIBASE_TRIAL_DAYS` | `7` | Days of trial credit for the first project after the card check | | `SCRIBASE_BILLING_GRACE_HOURS` | `72` | Hours projects keep running after the balance reaches zero | | `SCRIBASE_TOP_UP_MIN_CENTS` | `500` | Smallest top-up | | `SCRIBASE_TOP_UP_MAX_CENTS` | `100000` | Largest top-up | | `CREEM_PRODUCT_ID_TOP_UP` | the card-check product | A $1 one-time Creem product; a top-up buys it in units. Falls back to `CREEM_PRODUCT_ID_CARD_CHECK` | | `SCRIBASE_PROJECT_BILLING` | on when `CREEM_API_KEY` is set | `1`/`0` forces per-project billing on or off | | `SCRIBASE_BILLING_SWEEP_SECONDS` | `300` | How often the daily-charge, pause and resume sweep runs | The card check itself keeps its existing variables (`CREEM_API_KEY`, `CREEM_PRODUCT_ID_CARD_CHECK`, `SCRIBASE_CARD_CHECK_CENTS`, `SCRIBASE_REQUIRE_CARD`), and top-up payments arrive through the same signed webhook (`CREEM_WEBHOOK_SECRET`). There is no project cap to configure; a self-hosted deployment without billing never charges or pauses anything. --- Source: https://docs.scribase.com/docs/getting-started # Getting Started with Scribase Scribase is an open application cloud built on PostgreSQL. It provides a durable control plane, branching data environments, and a full set of application-layer services — auth, storage, realtime subscriptions, and edge functions — all deployable as a single stack that you own. ## What Scribase gives you | Surface | Description | |---|---| | Control-plane API | Durable `/v1` REST API for organizations, projects, environments, and operations | | Local dev | `scribase dev` — Postgres + all services, no Docker, no configuration | | Branching | Schema-level CoW branches for each pull request or preview environment | | Migrations | Lint-first migration runner with up/down tracking | | Auth | JWT-based authentication with OAuth providers via GoTrue | | Storage | S3-compatible object storage with bucket policies | | Realtime | WebSocket subscriptions over Postgres replication | | Functions | V8-based edge function runtime | | Aegis | Compile-time RLS policy verifier | | SDK | Typed TypeScript client — `scribase` on npm | There are two ways in: - **Hosted Scribase** — sign up, add a card ($1, credited back), and your backend is live with its URL and key in about a minute. Nothing to install. Follow the [hosted quickstart](https://docs.scribase.com/docs/hosted.md). - **Local development** — run the whole stack on your machine with the `scribase` CLI, below. ## Quick start (local development) ### 1. Install the CLI and its prerequisites The CLI is a single Rust binary named `scribase`. Build it from a checkout of the Scribase source with Rust 1.85 or newer: ```sh cargo install --locked --path crates/scribase-cli scribase version ``` The source repository is private while Scribase is in early access; email [hello@scribase.com](mailto:hello@scribase.com) for access. If you only need the hosted platform, skip the CLI and use the [SDK](https://docs.scribase.com/docs/sdk.md) instead. Local databases use your installed PostgreSQL 17 server binaries (`initdb` and `pg_ctl` must be on `PATH` — for example `brew install postgresql@17` on macOS or the `postgresql-17` package on Debian/Ubuntu). The optional application services are the upstream open-source binaries — `gotrue` (auth), `storage-api` (storage), `realtime`, and `edge-runtime` (functions) — found next to the `scribase` binary or on `PATH`. A missing service is skipped with a message; the database always comes up. ```sh # Check your API configuration once you have a token (optional for local work) scribase doctor ``` ### 2. Start the local stack From your project directory, start the full local stack — Postgres, auth, storage, realtime, and functions — with one command. No Docker and no configuration file are required for the database; service binaries are resolved from the same directory as the `scribase` binary or from `PATH`. ```sh scribase dev ``` Scribase boots Postgres locally, runs bootstrap migrations, then brings up the application services. The process blocks and prints connection information: ``` preflight: service binaries auth found /usr/local/bin/gotrue storage found /usr/local/bin/storage-api realtime found /usr/local/bin/realtime functions found /usr/local/bin/edge-runtime database ready at .scribase ``` To start only specific services: ```sh scribase dev --services auth,storage ``` ### 3. Apply a schema ```sh scribase dev up --schema schema.scribase ``` The schema file uses the Scribase schema language — a typed model definition that compiles to Postgres DDL with row-level security policies baked in. See [Aegis Policy Compiler](https://docs.scribase.com/docs/aegis.md) for the policy syntax. ### 4. Connect `scribase dev url` prints the connection string for the primary database: ```sh psql "$(scribase dev url)" ``` ### 5. Branch for a feature ```sh scribase branch new my-feature # returns the branch connection string ``` Scribase uses copy-on-write snapshots to create branches in under a second. Each branch is an independent Postgres cluster under `.scribase/`. ## Next steps - [Hosted quickstart](https://docs.scribase.com/docs/hosted.md) — sign up, create a token, make your first API call - [Self-hosting on a server](https://docs.scribase.com/docs/self-hosting.md) — Docker Compose or Helm - [CLI reference](https://docs.scribase.com/docs/cli.md) — all commands and flags - [Control API](https://docs.scribase.com/docs/api.md) — the `/v1` REST interface - [SDK](https://docs.scribase.com/docs/sdk.md) — the `scribase` TypeScript client - [Concepts](https://docs.scribase.com/docs/concepts/branching.md) — branching, auth, storage, realtime ## Configuration The local stack resolves configuration from environment variables. | Variable | Purpose | |---|---| | `SCRIBASE_HOME` | Override the `.scribase` workspace directory | | `SCRIBASE_API_URL` | Control-plane API the CLI talks to — `https://api.scribase.com` for hosted Scribase | | `SCRIBASE_ACCESS_TOKEN` | Personal access token or organization API key ([how to create one](https://docs.scribase.com/docs/api/tokens.md)) | | `SCRIBASE_CURL_BINARY` | Override the `curl` binary used by the CLI | --- Source: https://docs.scribase.com/docs/self-hosting # Self-hosting Scribase Scribase ships two first-class deploy targets under `deploy/`: | Target | Path | Use it for | |---|---|---| | Docker Compose | `deploy/compose/` | Single-box deployments — one host, all services | | Helm chart | `deploy/helm/scribase/` | Kubernetes — control plane and optional shared data plane | Hosting many small projects (one per app) on one VPS? Use the [single-box layout](https://docs.scribase.com/docs/single-box.md) instead: scale-to-zero project containers, shared storage, realtime and functions, and built-in admin sign-in. The service stack is the same in both: **postgres · control-api · gateway (Envoy) · auth · rest · storage · realtime · functions · worker**. Background jobs (backups, restores, verifications, exports, scheduled backups, retention, export expiry and usage metering) run inside `control-api` as its data-job workers, so there is no separate scheduler service. --- ## What you must provide Everything else has a working default. These four are yours to supply: 1. **A PostgreSQL 17 database.** Compose runs one for you (`supabase/postgres`); a production install points at your own managed database. The schema migrator needs a role with `BYPASSRLS`; the API and operator use least-privilege login roles. 2. **A public domain** — where clients reach the gateway, e.g. `scribase.example.com`. 3. **A TLS certificate** for that domain. The gateway speaks plain HTTP internally and trusts a TLS terminator in front of it. 4. **Operator sign-in** for the management API: either the control plane's own password login (`SCRIBASE_AUTH_MODE=builtin`, no identity provider needed; see [Built-in admin login](https://docs.scribase.com/docs/admin-login.md)), or an OIDC provider with token introspection (`SCRIBASE_AUTH_MODE=oidc` + `SCRIBASE_OIDC_*`). If you provision managed per-tenant databases via Neon, also a **Neon API key** (`SCRIBASE_NEON_API_KEY`). Secrets are never baked into images or committed. Generate them, then pass them via `.env` (Compose) or a Kubernetes Secret / external secret store (Helm). --- ## Single-box deploy (Docker Compose) Files: `deploy/compose/docker-compose.yml`, `.env.example`, `envoy/envoy.yaml`, `initdb/`. ### 1. Configure ```sh cd scribase/deploy/compose cp .env.example .env # Edit .env: set POSTGRES_PASSWORD, JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY, # SECRET_KEY_BASE, and either SCRIBASE_AUTH_MODE=builtin or the # SCRIBASE_OIDC_* values. See the file comments. ``` Required secrets to generate: ```sh # JWT secret (at least 32 random bytes, base64-encoded) openssl rand -base64 32 # Anon key and service-role key are JWTs signed with JWT_SECRET. # Generate them with the scribase CLI or the Scribase Console. ``` ### 2. Validate the config (no daemon required) ```sh docker compose --env-file .env -f docker-compose.yml config ``` ### 3. Boot ```sh docker compose --env-file .env -f docker-compose.yml up -d --build ``` Boot order is enforced by health and completion gates: 1. `db` becomes healthy 2. `migrate` (one-shot) applies `postgres/migrations/*.sql` and exits `0` 3. `control-api`, `auth`, `rest`, `storage`, `realtime`, `functions` start 4. `gateway` (Envoy) starts once all data-plane services are up ### 4. Verify ```sh # Public API through the gateway curl http://localhost:8000/health/ready # Management API directly curl http://localhost:8081/health/ready ``` ### 5. Register your first organization ```sh export SCRIBASE_API_URL=http://localhost:8081 # builtin mode: the first run creates the administrator and signs in; # later runs use `scribase login --email you@example.com`. scribase admin setup --email you@example.com --setup-token scb_setup_... # oidc mode instead: export SCRIBASE_ACCESS_TOKEN= scribase doctor scribase org put myorg "My Organization" scribase project put myorg myproject aws-us-east-1 scribase env create myorg myproject production production snapshot aws-us-east-1 init-001 scribase operation get myorg ``` --- ## Kubernetes deploy (Helm) The Helm chart at `deploy/helm/scribase/` manages the control-plane components and its PostgreSQL dependency. ### Prerequisites - Helm 3.x - A Kubernetes cluster (1.28+) - A PostgreSQL 17 database accessible from the cluster - A TLS-terminating Ingress controller ### 1. Add values ```sh cp deploy/helm/scribase/values.yaml myvalues.yaml # Edit myvalues.yaml ``` Key values to set: ```yaml controlApi: database: url: "postgresql://scribase_api:PASSWORD@postgres:5432/scribase?sslmode=require" oidc: introspectionEndpoint: "https://idp.example.com/oauth/introspect" issuer: "https://idp.example.com" audience: "scribase-api" clientId: "scribase-control" clientSecret: "SECRET" gateway: domain: "scribase.example.com" secrets: jwtSecret: "GENERATED_JWT_SECRET" anonKey: "GENERATED_ANON_JWT" serviceRoleKey: "GENERATED_SERVICE_ROLE_JWT" ``` ### 2. Install ```sh helm install scribase deploy/helm/scribase/ -f myvalues.yaml -n scribase --create-namespace ``` ### 3. Upgrade ```sh helm upgrade scribase deploy/helm/scribase/ -f myvalues.yaml -n scribase ``` ### 4. Scale ```sh kubectl scale deployment scribase-control-api --replicas=3 -n scribase ``` --- ## Environment variables reference ### Control API | Variable | Required | Description | |---|---|---| | `SCRIBASE_API_DATABASE_URL` | Yes | TLS URL for the `scribase_api` role | | `SCRIBASE_AUTH_MODE` | No | `oidc` (default here) or `builtin` | | `SCRIBASE_ADMIN_EMAIL`, `SCRIBASE_ADMIN_PASSWORD_HASH` | No | builtin only: pre-seed the first administrator | | `SCRIBASE_OIDC_INTROSPECTION_ENDPOINT` | oidc only | RFC 7662 token introspection HTTPS endpoint | | `SCRIBASE_OIDC_ISSUER` | oidc only | Exact trusted `iss` claim | | `SCRIBASE_OIDC_AUDIENCE` | oidc only | Required `aud` claim | | `SCRIBASE_OIDC_CLIENT_ID` | oidc only | Introspection client ID | | `SCRIBASE_OIDC_CLIENT_SECRET` | oidc only | Introspection client secret | | `SCRIBASE_HTTP_BIND` | No | Default `127.0.0.1:8080` | | `SCRIBASE_TRUST_PROXY_TLS` | No | Set to `1` to allow non-loopback bind | | `SCRIBASE_MAX_ENVIRONMENTS` | No | Default `10` per new organization | | `SCRIBASE_DATABASE_MAX_CONNECTIONS` | No | Default `10` | ### Organization single sign-on (optional) Needed only if organizations will connect their own identity provider (see [Single sign-on](https://docs.scribase.com/docs/concepts/sso.md)). Apply control-plane migration `0018` first. | Variable | Description | |---|---| | `SCRIBASE_PUBLIC_API_URL` | Public `https://` origin of the control API. Used for the OIDC redirect URI and the SAML entity ID and ACS URL. SSO is unavailable until it is set. | | `SCRIBASE_CONSOLE_URL` | Console origin the browser returns to after the IdP. Default `https://console.scribase.com`. | | `SCRIBASE_KMS_MASTER_KEY` | 64 hex characters. Seals OIDC client secrets at rest (shared with environment secrets). | | `SCRIBASE_ORG_SETTINGS_DATABASE_URL` | Store for SSO settings, sign-in state and sessions; falls back to `SCRIBASE_CONTROL_PLANE_ADMIN_DATABASE_URL`. | | `SCRIBASE_SSO_SESSION_HOURS` | Console session length after SSO, 1 to 168. Default `12`. | | `SCRIBASE_DOH_URL` | DNS-over-HTTPS JSON endpoint for domain verification. Default Cloudflare's public resolver. | ### Neon integration (optional) | Variable | Description | |---|---| | `SCRIBASE_NEON_API_KEY` | Neon API key for managed Neon environments | | `SCRIBASE_NEON_PROJECT_REGION` | Default Neon region for new environments | --- ## Database roles Scribase uses three database roles: | Role | Privileges | Used by | |---|---|---| | `scribase_migrator` | `BYPASSRLS`, DDL | Schema migration runner | | `scribase_api` | Least-privilege DML | control-api process | | `scribase_operator` | Operator plans | Operator (`worker`) | The `bootstrap.sql` script creates all three. The control-api process is never given the migrator or operator credential. --- ## Upgrading 1. Pull the new images or build from source. 2. Run the migration runner: `docker compose run --rm migrate`. 3. Restart services: `docker compose up -d`. The migration runner is idempotent and checksum-guarded — it will not re-apply already-applied migrations. --- Source: https://docs.scribase.com/docs/single-box # Single-box deploy (many projects) This layout hosts **30 to 50 small projects (for example one per mobile app or game) on one VPS** in a 6 to 8 GB RAM budget. It is the layout Scribase itself is operated on. For a plain one-tenant stack, see [Self-hosting](https://docs.scribase.com/docs/self-hosting.md). The full operator reference, with memory budgets and every overlay, is `DEPLOY.md` in the repository. | File | Role | |---|---| | `deploy/compose/docker-compose.yml` | Base stack: control-plane Postgres, migrator, control API | | `deploy/compose/docker-compose.single-box.yml` | Overlay: operator, activator, project router, restore-verify scratch database, memory caps | | `deploy/compose/docker-compose.shared-services.yml` | One shared storage, realtime and edge-functions runtime for every project | | `deploy/compose/docker-compose.postgres-cow.yml` | Default engine: one small Postgres 17 per environment on this box, branches as copy-on-write clones | | `deploy/compose/docker-compose.neon-local.yml` | Optional: self-hosted Neon on the same box (instead of `postgres-cow`) | | `deploy/compose/single-box.env.example` | Env template (variable names only, no values) | ## How it works - **Every environment gets its own Postgres 17 on the box** (`SCRIBASE_DATA_ENGINE=postgres-cow`). A branch (preview, staging, a sandbox per agent session) is a copy-on-write clone of its parent's data directory: it takes milliseconds and only costs the blocks that later change. An idle environment's database is stopped after `SCRIBASE_POSTGRES_COW_SUSPEND_SECONDS` (default 900, like the API container) and started again by the next connection. The bundled `db` holds only the control plane. No cloud account is needed. - **Managed Neon is optional.** Set `SCRIBASE_NEON_API_KEY` and new projects can use `neon-cloud` (one Neon project per Scribase project, 0.25 CU, suspended after 5 idle minutes, point-in-time restore). `scribase project move --engine postgres-cow|neon-cloud|neon-local` moves a project between engines and keeps its URL and keys. - **One container per environment** runs GoTrue (`/auth/v1`) and PostgREST (`/rest/v1`). It **scales to zero**: the activator starts it on the first request and stops it after `SCRIBASE_PROJECT_IDLE_SECONDS` (default 900) without traffic. An idle project uses disk, not memory. - **Storage, realtime and edge functions are shared**, multi-tenant services. Each function call runs in its own isolated Deno worker that only sees its environment's variables and secrets. - **Every environment is served at `https://.`** through the project router. - **Operator sign-in is built in** (`SCRIBASE_AUTH_MODE=builtin`). No identity provider is needed. See [Built-in admin login](https://docs.scribase.com/docs/admin-login.md). ## 1. Build the images ```sh sudo git clone /srv/src/scribase cd /srv/src/scribase/deploy/compose # Control-plane image (control API, operator, activator, CLI). docker compose -f docker-compose.yml build migrate # Per-environment runtime image (GoTrue + PostgREST). docker compose -f docker-compose.yml -f docker-compose.single-box.yml \ --profile build build project-runtime-image ``` ## 2. Configure ```sh sudo install -m 640 -o root -g deploy single-box.env.example /etc/products/scribase.env sudoedit /etc/products/scribase.env ``` Fill every blank in the template: - Control-plane passwords and keys (`openssl rand -hex 32` for each). - `SCRIBASE_ORGANIZATION_ID`, the id of your first organization. - `SCRIBASE_POSTGRES_COW_HOST_DIR` (default `/srv/scribase/pg-cow`): a directory on a filesystem that can clone files (XFS, btrfs or ZFS), owned by uid 10001, mode 0700. On an ext4 root, create an XFS image file and loop-mount it there (see [Copy-on-write storage](#copy-on-write-storage)). - Optional: `SCRIBASE_NEON_API_KEY`, a Neon API key, to offer `neon-cloud` too. - The `SHARED_*` keys for the shared storage and realtime services, and SMTP. - `SCRIBASE_FUNCTIONS_ADMIN_KEY`, its own random value. The functions runtime refuses an admin key that equals a key functions can read. - `SCRIBASE_RESTORE_VERIFY_DB_PASSWORD`, the password of the bundled `restore-verify-db` scratch server that Backup Verify restores into. Leave `SCRIBASE_RESTORE_VERIFY_DATABASE_URL` blank to use that server. Without a verification database, Backup Verify answers `503 verification_not_configured`. - Leave `SCRIBASE_ADMIN_EMAIL` and `SCRIBASE_ADMIN_PASSWORD_HASH` blank to get a one-time setup token in the log, or pre-seed the administrator (see [Built-in admin login](https://docs.scribase.com/docs/admin-login.md)). Keep every published port on `127.0.0.1`. Docker bypasses host firewalls such as UFW. ## 3. DNS and TLS - `api.` and `console.`: ordinary records pointing at the box. - `*.`: one wildcard record to the box. Every environment is served at `https://.`. - A wildcard certificate. HTTP-01 cannot issue one, so use DNS-01 or, behind Cloudflare, a Cloudflare Origin CA certificate for `*., ` with SSL mode "Full (strict)". - Your TLS terminator forwards `api.` to `127.0.0.1:8081` and `*.` to `127.0.0.1:8010` (the project router). ## 4. Boot ```sh cd /srv/src/scribase/deploy/compose dc="docker compose -p scribase --env-file /etc/products/scribase.env \ -f docker-compose.yml -f docker-compose.single-box.yml -f docker-compose.shared-services.yml \ -f docker-compose.postgres-cow.yml" $dc config -q # validate first $dc up -d ``` ## 5. Create the administrator and the organization Skip `admin setup` if you pre-seeded `SCRIBASE_ADMIN_*`, and sign in with `scribase login --email ...` instead. ```sh $dc logs control-api | grep -A1 'setup token' # scb_setup_... $dc exec -e SCRIBASE_API_URL=http://127.0.0.1:8080 control-api \ scribase admin setup --email you@yourdomain --setup-token scb_setup_... $dc exec -e SCRIBASE_API_URL=http://127.0.0.1:8080 control-api \ scribase org put "Your Org" ``` You can also open the console: on a control plane with no administrator, its sign-in page asks for the setup token instead of a password. ## 6. First project Create a project and a `production` environment in the console, or with the CLI (see [Organizations, projects and environments](https://docs.scribase.com/docs/cli/resources.md)): ```sh scribase project put my-app aws-eu-central-1 scribase env create my-app production production snapshot aws-eu-central-1 first-env-1 ``` The operator creates the environment's database, bootstraps the Supabase roles, publishes the runtime and creates the project container. Then check it with the host label and `anon` key the console shows on the environment's API keys page: ```sh host=https://