Skip to content
Memtru
Documentation menu
Guides

Vercel + Supabase private beta

The owner selected this topology on 2026-10-22. The Next.js app runs on Vercel; Supabase provides PostgreSQL. Memtru retains its existing password/session and API-token authentication. Supabase Auth and browser database access are not used.

Current state (2026-10-22)

The hosted beta is live at https://memtru.com (MEMTRU_PUBLIC_URL). The domain is registered at Cloudflare and keeps Cloudflare DNS: the apex A records and the www CNAME point to Vercel with proxying off, and www redirects to the apex. https://memtru.vercel.app still serves the app, but the CLI and browser sessions should use the canonical domain (same-origin checks compare against it).

  • Supabase project zyveakirlpxoftlajmko (organization "DevAtzma's Org", region ap-south-1, PostgreSQL 17). Migration 0001_memtru.sql is applied; the memtru_app runtime role has LOGIN with a generated password that exists only in Vercel's DATABASE_URL.
  • Vercel project memtru (team memtru), root directory apps/web, Node 24.x, Production variables: DATABASE_URL (transaction pooler, port 6543), DATABASE_SSL_CA, MEMTRU_SIGNUP=open (public registration since 2026-10-22, rate-limited per client address), MEMTRU_PUBLIC_URL. vercel.json no longer pins MEMTRU_SIGNUP; without the variable, production stays closed. MIGRATION_DATABASE_URL is also present in Vercel at the owner's request; the application does not read it. The administrator password it contained was rotated afterwards, so it no longer authenticates.
  • Remote TLS verification uses the Supabase Root 2021 CA (DATABASE_SSL_CA). Without it Node rejects the pooler certificate (SELF_SIGNED_CERT_IN_CHAIN).
  • The operator account was provisioned with admin:provision; the owner created the first organization through onboarding.
  • Git-triggered deployments require the commit author to map to a GitHub account on the Vercel team; commits authored by an unknown identity are blocked.
  • Verified on 2026-10-22 against https://memtru.com: 36 hosted checks (database role privileges, row-level security, migration checksum, session cookie flags, cross-organization and cross-origin denial, real CLI uploads, idempotency, token revocation, membership removal) passed with synthetic accounts that were deleted afterwards. A read-only, application-schema backup was encrypted locally and restored into an in-memory PostgreSQL; all 13 tables matched. This is not a substitute for a provider-level restore into a separate project.

Database setup

  1. Inspect the project's schemas, roles, exposed API schemas and backup settings. Preserve existing objects. If memtru or memtru_meta already exists, stop and reconcile its history before applying this application's migrations.
  2. Obtain an operator direct or session pooler connection from Supabase's Connect dialog. Set MIGRATION_DATABASE_URL only in a protected operator environment. Do not store it in the repository or in Vercel runtime variables.
  3. From apps/web, run npm run db:migrate:postgres. Versioned SQL lives in db/postgres. Migrations use one transaction, an advisory lock and a checksum ledger. They never run from an HTTP request or during next build.
  4. The migration creates a private memtru schema and a memtru_app role with NOLOGIN. Through a protected database administration session, enable LOGIN with a newly generated password for this role. Store that password directly in the deployment's encrypted secret configuration, never in a chat or SQL file. Do not reuse the project's administrator password.
  5. Runtime DATABASE_URL uses the transaction pooler, custom username memtru_app.<project-ref>, the actual pooler host from Connect and port
    1. Percent-encode credentials when constructing URLs. Do not guess the region or pooler hostname from the project reference.

TLS certificate validation is mandatory for remote connections. Supply Supabase's root CA (downloadable from the project's database settings) as the server-only PEM DATABASE_SSL_CA; do not disable verification. Named prepared statements and session-persistent tenant context are not used, so transaction pooling is safe.

The runtime role has DML privileges only in the application schema. RLS is enabled on every application table; its policy permits the trusted server role. Tenant authorization remains enforced by Memtru's services, not by per-user RLS claims. Untrusted roles have no schema access. Keep memtru and memtru_meta out of the Supabase Data API's exposed schemas, and never grant the browser memtru_app.

Optional local-data cutover

Do not assume that the local SQLite database should be uploaded. Inspect data ownership first. A new beta can start empty and use admin:provision for the verified operator account.

When preserving the local application's data is required:

  1. Stop local writes and create an integrity-checked snapshot using db:backup.
  2. Set SQLITE_IMPORT_PATH to that snapshot and keep the operator migration URL.
  3. Run npm run db:import:sqlite to rehearse. This executes the actual inserts, foreign keys and count checks, then rolls everything back.
  4. Inspect the count-only result. Run npm run db:import:sqlite -- --apply for the actual cutover. It refuses to merge into any populated application table. The source is opened read-only. Account/password hashes, organization roles, token hashes/revocations and report contents are preserved. Existing browser sessions are invalidated so local sessions cannot become public sessions.
  5. Verify sign-in, report contents, token revocation and cross-organization denial before allowing the cohort to use the new site. Retain the source snapshot.

Vercel project

  • Import the existing private GitHub repository. Root directory: apps/web.
  • Keep Include source files outside of the Root Directory in the Build Step enabled: the published Markdown lives in the repository's docs/ directory. Next.js tracing explicitly includes those files in the documentation functions.
  • Node.js: 24.x. Framework: Next.js. Install: npm ci. Build: npm run build. The repository includes apps/web/vercel.json; use default Next.js output.
  • Set encrypted, server-only DATABASE_URL to the runtime role's pooled URL.
  • Set MEMTRU_SIGNUP=closed and MEMTRU_PUBLIC_URL to the approved HTTPS origin.
  • Never set MEMTRU_INSECURE_COOKIES on Vercel.
  • Keep Preview and Production variables separate. Previews must use an isolated database, never a production connection copied into all environments.
  • Initially deploy the reviewed branch to Preview with platform access protection. Promote the verified revision only after the hosted checks below pass. Keep registration closed for the private beta.

Without DATABASE_URL, Vercel requests fail instead of creating an ephemeral SQLite file. Build-time rendering does not migrate or seed a database. No Supabase administrator key, anonymous key or browser SDK is required for this topology.

Hosted checks and operations

Before inviting users, verify on the actual HTTPS origin: /api/health, secure cookies, same-origin mutations, closed signup, provisioned operator login and onboarding, synthetic CLI upload/read/idempotency, foreign organization denial, revoked token denial, and rate limiting across independent requests.

All service transactions share a PostgreSQL advisory lock during this controlled beta. Database write triggers acquire the same lock, including administrative membership changes. This intentionally preserves the single-writer authorization ordering of the tested SQLite implementation. It is not a high-throughput scaling claim. Query/lock deadlines are set inside transactions. Pools use at most three connections each and close idle clients after ten seconds. Review pooler client capacity and metrics under realistic Vercel concurrency before expanding access.

Rate-limit buckets are atomic PostgreSQL rows with hashed keys, expiration and bounded cleanup. They survive cold starts and are shared across instances. A database failure does not fall back to an uncoordinated local limiter.

Choose backup retention and restore objectives based on the actual Supabase plan; PITR is not assumed. Rehearse restoring a database backup into a separate project. Reconcile revoked credentials and password changes before reopening traffic after restore. SQLite's db:backup command explicitly refuses cloud configurations.

Keep previous compatible application revisions for rollback. Do not roll back to the earlier synchronous SQLite-only build while pointing at PostgreSQL. Do not drop schemas or overwrite existing Supabase data as a rollback technique.

Official references