scribasedocs

CLI Reference

scribase import

Two shapes share one verb:

  • scribase import <url> — reads an existing Postgres database and produces a Scribase schema file.
  • scribase import supabase ... — migrates a live Supabase project into a Scribase environment via the management API.

Import a schema from a URL

Connects to an existing Postgres database, introspects its columns, constraints, and RLS policies, and emits a .scribase schema file.

Terminal
scribase import <connection-url> [--out FILE] [--partial]

Options

Flag Description
--out <file> Write output to file instead of stdout
--partial Allow incomplete import (some columns may not be expressible)

Example

Terminal
scribase import "postgresql://user:pass@host/db" --out schema.scribase

Notes

  • Any column type, constraint, or default that cannot be expressed in the Scribase schema language is printed as a fidelity note. Without --partial, the command fails if there are any such notes.
  • Tables without row-level security are printed as security notes — they are imported but the output warns you.
  • Use --partial when working with complex legacy schemas and iterating toward full coverage.

Migrate from Supabase

Migrates a live Supabase project into a Scribase environment. The source project's schema and data are read and applied to the destination environment.

Terminal
scribase import supabase \
  --project-ref <supabase-project-ref> \
  --org <scribase-org-id> \
  --project <scribase-project-id> \
  --env <scribase-env-id> \
  [--connection-string <url>] \
  [--service-key <key>] \
  [--dry-run]

Options

Flag Env override Description
--project-ref — Supabase project reference ID
--org — Destination Scribase organization ID
--project — Destination Scribase project ID
--env — Destination Scribase environment ID
--connection-string SCRIBASE_SOURCE_CONNECTION_STRING Supabase database URL
--service-key SCRIBASE_SOURCE_SERVICE_KEY Supabase service role key
--schemas a,b — Copy only these schemas (default: every application schema)
--no-storage — Skip buckets and objects
--dry-run — Plan what would move without touching anything
--no-wait — Start the import and print its import_id without following progress
--functions-dir DIR — After the data import succeeds, deploy every edge function in this local supabase/functions directory (ignored with --dry-run)
--functions-only — Skip the data import and only deploy the functions (--functions-dir defaults to supabase/functions)

The connection string and service key are secrets. Pass them via environment variables rather than on the command line in production contexts.

Example

Terminal
export SCRIBASE_SOURCE_CONNECTION_STRING="postgresql://..."
export SCRIBASE_SOURCE_SERVICE_KEY="eyJhbGci..."

scribase import supabase \
  --project-ref abcdefghijkl \
  --org acme \
  --project store \
  --env production \
  --dry-run

The server returns a report describing what would be imported. Remove --dry-run to execute: the server starts the import in the background, and the command follows GET /v1/imports/{import_id}, printing each phase as it starts, then prints the verified report. Pressing Ctrl-C stops watching, not the import.

Edge functions

Terminal
# Data first, then every function in the local checkout:
scribase import supabase --project-ref <ref> --org acme --project store --env production \
  --functions-dir supabase/functions

# Or only the functions, for example after a data import that already finished:
scribase import supabase --functions-only --org acme --project store --env production \
  --functions-dir supabase/functions

Function secrets are never read from the source. Re-enter them with scribase secrets set.

What is migrated

In order, each step replay-safe (--resume <import_id> continues from the last committed step):

  1. Extensions the app schemas use (best effort; any the target cannot install is listed as an action item).
  2. Schema of every application schema (public plus any schema that Supabase or an extension does not own): tables, types, functions and RPCs, views, indexes, ENABLE ROW LEVEL SECURITY. Foreign keys and triggers are held back until after the data, as pg_restore does.
  3. Grants: the exact privileges anon, authenticated and service_role hold on every schema, table, sequence and function (revokes included, so an RPC hidden from anon stays hidden).
  4. Auth: auth.users with ids and bcrypt encrypted_password hashes byte for byte, plus instance_id, aud and role (the lookup columns GoTrue uses on sign-in); auth.identities (provider, provider_id, identity_data), so Apple and Google signInWithIdToken users resolve to the same user id; auth.mfa_factors (TOTP secrets verbatim).
  5. Data, page by page, keyset-paged and idempotent.
  6. Post-data: foreign keys, table triggers, sequence positions (setval), and triggers on auth.users / storage.objects that call app functions (for example on_auth_user_created).
  7. RLS policies on the app schemas and on storage.objects / storage.buckets, with their auth.uid() / auth.jwt() expressions unchanged.
  8. Storage: buckets, then each object's bytes through the destination environment's storage API (x-upsert, so a resume overwrites), then the object's original owner_id, so owner-based storage policies keep matching.
  9. Verification: per-table row counts and checksums, policy parity, auth user and identity counts, object counts, and an optional login proof.

What is not migrated

  • Sessions and refresh tokens. Scribase signs with its own JWT secret, so every existing session ends. Users sign in once more after the switch, with the same password or the same Apple/Google account, and keep their user id and data.
  • API keys and the JWT secret. They are never copied. The app gets the environment's own keys (see below).
  • Edge function source from the hosted project. The Supabase API cannot reliably hand back function source, so functions move from your local checkout instead: pass --functions-dir supabase/functions (or run --functions-only later) and the CLI deploys each function, honouring _shared, deno.json / import maps and verify_jwt in config.toml. The console import wizard does not move functions yet.
  • Vault secrets and function env vars (listed by name; re-enter with scribase secrets set), custom auth hooks, SMTP, and auth provider settings (set them on the environment's Auth page), realtime publications, pg_cron jobs, and column-level grants.
  • Keyless tables (no single-column primary key) are protected against duplicates on a resume, not on a second fresh run. Import into an empty environment.

Switch an Expo app to Scribase

The end-to-end procedure for a mobile app (Expo + supabase-js, email/password plus native Apple/Google sign-in):

  1. Create the environment (console, or scribase env create ...). Open its Storage page once so the storage service creates its schema.

  2. Dry run, then import:

    Terminal
    export SCRIBASE_SOURCE_CONNECTION_STRING="postgresql://postgres:...@db.<ref>.supabase.co:5432/postgres"
    export SCRIBASE_SOURCE_SERVICE_KEY="<source service_role key>"
    scribase import supabase --project-ref <ref> --org <org> --project <app> --env production --dry-run
    scribase import supabase --project-ref <ref> --org <org> --project <app> --env production

    Proceed only when the verdict is VERIFIED.

  3. Configure auth on the environment (Auth page), mirroring the Supabase project's Auth settings: site_url, the redirect allow list (myapp://**, plus exp://** for Expo Go), and for native sign-in the Apple provider (client id = the iOS bundle id, plus the Services ID if you also use the web flow) and the Google provider (client ids = the web, iOS and Android OAuth client ids, comma-separated). The console also asks for each provider's client secret.

  4. Print the app config:

    Terminal
    scribase apps switch <org> <app> --env production            # .env lines for Expo
    scribase apps switch <org> <app> --format json               # for scripts
    scribase apps switch <org> <app> --key publishable           # sb_publishable_ key instead of the anon JWT

    It prints exactly EXPO_PUBLIC_SUPABASE_URL=https://<label>.<domain> and EXPO_PUBLIC_SUPABASE_ANON_KEY=<key>, plus the auth setting names above. It never prints the service_role / sb_secret_ key. That key must never ship in an app, because it bypasses row-level security.

  5. Update the app: put the two values in .env (or the EAS environment), rebuild, and ship. createClient(url, key) needs no code change.

  6. Cut over: rehearse steps 2 to 5 against a staging environment first. At cutover, stop writes on the Supabase project (or force-update the old app version), import into the still-empty production environment, and release the build that points at it. The URL and key printed in step 4 are fixed once the environment exists, so the build can be prepared before the final import. Existing users see the sign-in screen once.