Skip to content

Security & compliance

This page documents PerSQL’s current security posture: controls in place today, items on the roadmap, and the status of formal certifications. It is updated as new controls ship.

For security questions not answered here, or to report a vulnerability, contact security@persql.com.

  • Console uses Better Auth with GitHub OAuth, Google OAuth, and email/password. Sessions are HTTP-only cookies, scoped to the console hostname.
  • API (/v1/*) uses bearer tokens (Authorization: Bearer psql_live_… for production tokens, psql_test_… for tests, and psql_cli_… for the persql CLI). Tokens are namespace-scoped, can be set read-only, and are revocable from the console.
  • Token validation is KV-cached for 60s to keep D1 reads off the hot path. Revocation propagates within that TTL.

Every database is its own Cloudflare Durable Object with embedded SQLite. There is no shared connection pool, no shared SQLite engine, no shared cache between tenants — when you query database A, none of the bytes in database B are reachable from your code path. Even the idempotency cache is namespaced by token and DB ID.

The control plane (users, namespaces, members, the database registry) lives in a single Cloudflare D1 instance. Row-level access is enforced in apps/api middleware — a request for acme/orders is rejected at auth time if the bearer token’s namespace isn’t acme.

  • In transit: TLS 1.3, terminated at Cloudflare’s edge. No plaintext HTTP — api.persql.com, console.persql.com, *.persql.com, and customer SaaS hostnames all redirect.
  • At rest: Cloudflare-managed encryption for Durable Object storage and D1. We don’t run our own KMS.
  • Token storage: bearer tokens are stored hashed (SHA-256) in D1. We never log raw tokens — the analytics pipeline stores only the token’s ID.
  • Region pinning. When you create a database, you can pin its Durable Object to a Cloudflare location group: wnam, enam, weur, eeur, apac, oc, me, afr. The DO’s home is fixed at init — moving it later is not currently supported.
  • auto (the default) lets Cloudflare place the DO at first request.
  • Control plane (D1) is global; it stores no application data — only metadata (user IDs, namespace slugs, database registry rows, schedule definitions, audit metadata).

If you have a hard residency requirement (e.g. EU-only), pin every database to weur and email us so we can flag the namespace.

  • Query analytics — namespace ID, database ID, token ID, status, rows-read, rows-written, duration. No SQL text and no parameters. Used for billing and the customer “Insights” view.
  • Slow query log (per database) — first 4 KB of SQL text, redacted parameters, duration. Visible only to namespace members.
  • Audit log (per namespace) — DDL changes, schedule runs, membership changes, custom hostname events. Visible only to namespace members.
  • Better Auth events — sign-in, sign-out, OAuth provider name. No password material.
  • Cloudflare access logs — handled by Cloudflare per their DPA.

We do not sell logs, query data, or any other customer data. We do not feed customer query data into third-party model training.

  • Point-in-time recovery (30 days). Every database gets continuous PITR via the underlying Cloudflare SQLite engine’s 30-day rolling bookmark history — every committed write is recoverable. No schedule, no cron, no plan tier. See Backups.
  • Labeled snapshots. Take a named snapshot before a risky operation and restore to that label later. The label is just a pointer into the same 30-day window.
  • Long-term archives. For retention past 30 days, take a manual R2 archive (or call the archive endpoint from CI). Archives stay until you delete them. See Archives.
  • Restore. Owner/admin can restore in place from any bookmark or labeled snapshot, or fork a snapshot into a new database.
  • Forking. Any database can be forked into an independent copy at any time — handy for staging or what-if work. See Forking.

Roles are: owneradminmember. The owner is the namespace creator and is the only role that can transfer ownership or delete the namespace. Roles are checked in middleware on every API call; see Members & roles for what each can do.

Database-level overrides are supported — you can grant a member read-only on production while leaving them admin on staging.

  • SOC 2 Type I — in progress. We’re operating against the SOC 2 controls (audit log, change management, access reviews, vendor review) but have not yet completed a Type I audit. Expect a target date in the second half of 2026.
  • GDPR. We will sign a DPA on request — email security@persql.com with your entity name and we’ll send the current version. PerSQL acts as a processor; the customer is the controller. Subprocessors are listed below.
  • HIPAA. Not supported. Don’t put PHI on PerSQL today.
  • PCI-DSS. Not supported. Don’t put unencrypted cardholder data on PerSQL today; use a tokenization vault.
  • CCPA / CPRA. We follow GDPR-equivalent processes for California residents — same DPA covers both.
SubprocessorPurposeData
Cloudflare (Workers, Durable Objects, D1, R2, KV, Pages, Access, Analytics Engine)Compute, storage, DNS, edgeAll customer data, metadata, logs
ResendTransactional email (alerts, invites)Email addresses, member names
StripeBillingEmail, namespace, payment method (held by Stripe)
Better Auth (self-hosted in apps/api)Auth library — runs in our workerSessions, OAuth tokens
Pulumi (state on R2)Infrastructure-as-codeNo customer data, only infra resource IDs

If you’ve found a vulnerability:

  1. Email security@persql.com with details. PGP key on request.
  2. Don’t post publicly until we’ve had time to fix it.
  3. We’ll acknowledge within 2 business days and aim to fix high-severity issues within 14 days.

We don’t have a bug bounty program yet, but we’ll happily credit researchers in a security advisory and send you swag.

Public status: status.persql.com.

Severe incidents (data exposure, prolonged outage > 1 h) trigger an email to namespace owners within 24 h with a postmortem at the status page within 5 business days. No automated SLA credits today — this is a pre-revenue posture and we’ll formalise it with the SOC 2 work.