Security at Pace
How we keep your company’s information private, and what we have not done yet. Security reviewers will find the technical detail, with the file behind each claim, further down.
Last reviewed October 4, 2026. Contact security@pacehq.io.
How your data is protected
- Your workspace is yours alone. Every record belongs to one company. Pace checks that on every request, in our API and again inside the database, so one company cannot read another's data.
- Your apps run on their own addresses. Third-party apps such as Files live on a separate domain from your Pace login, so they cannot touch your session.
- Risky actions wait for a person. AI agents queue anything risky for someone on your team to approve first.
- Encrypted in transit, kept in the United States and Finland. Details are under "Where data lives".
Workspace data in the database
- Every record is tagged with its company. Pace's workspace data (accounts, approvals, invoices, agent activity, settings) lives in a Supabase Postgres database, the same design most SaaS products use. Every company-owned row carries a
tenant_id, and the checks below enforce it.supabase/migrations/20260505000000_init_multi_tenant.sql - The API names the company on every request. It opens a database transaction and sets
app.current_tenant_idfor that transaction only, so a pooled connection cannot carry one company's context into another's request.internal/db/tenant.go, WithTenantTx - Row-level security is a second wall behind the API. Policies compare each row's
tenant_idto that setting. Row-level security is enabled and forced (the table owner is not exempt) on the tenant tables, includingtenants,tenant_users,tenant_entitlements,audit_log,invoices,refunds,agent_stepsandtenant_ai_provider_configs.supabase/migrations/20260710000004_force_rls.sql and later migrations - One exception, stated plainly. The API connects as the database's
postgresrole, which has explicit bypass policies on some system tables (tenants, users, audit log writes, entitlements). Webhooks and provisioning have no single company to name. On those tables the API's own checks are the wall.supabase/migrations/20260710000008_postgres_backend_bypass.sql - The browser never writes to the database. Writes go through our API, which checks the caller's role. A browser holds only the public Supabase key, and for it the database is read-only: insert, update and delete are revoked from the
authenticatedrole on the user, membership, onboarding and audit tables, and the anonymous role has no table access.supabase/migrations/20261004000007_supabase_audit_hardening.sql - Audit log deletes are rejected by a database policy.supabase/migrations/20260511000000_audit_log_immutable.sql
These are the migrations in our repository. They describe the intended state of the production database. Ask us if you need a live check.
Apps
Apps are separated in one of three ways. This table is the isolation field of each app in the product's app registry. A Go test fails if it disagrees with the server's own table.
| Separation | Apps |
|---|---|
| Own instance or storage per company | Files, Finance |
| Shared instance, one organization or team per company inside the app | Vault, Messages |
| Shared, no proven boundary. Not offered to customers; shown to Pace operators only, as previews | Sign, Inbox, Wiki, Forms, Schedule, Flow, CRM, Search, HR, Code |
Source: ui/src/lib/apps.ts and internal/entitlements/isolation.go. Apps in the last row stay hidden from customer workspaces until a boundary is built and tested.
- Third-party apps are moving to a separate domain, pacehq-apps.com, so they cannot read the sign-in cookies set for pacehq.io. This move is in progress and is not complete.
- Sign-in is handled by Pace ID, our own sign-in service. Everyone signs in at app.pacehq.io, so session cookies stay on one origin; company-specific hostnames exist only for chat and files, on the separate apps domain.landing/cookies.html, docs/URL_POLICY.md
Cross-company test suite
- What it covers. Integration tests run against a real Postgres and check that a request made as company B cannot read or change company A's rows, including the production table set.tests/isolation/ (cross_tenant_test.go, cross_tenant_data_test.go, production_tables_isolation_test.go)
- How it runs. It is a named job in our CI, "HARD GATE: Cross-Tenant Isolation", and is on the required list of our merge check. We do not claim it passed on every release: it has failed on main before, and our CI is not always fully green..github/workflows/ci.yml, .github/workflows/merge-gate.yml
Where data lives
- Database: Supabase Postgres 17, region us-east-2 (United States).docs/HOSTING_PACEHQ.md, landing/legal/subprocessors.html
- Company app servers: Hetzner. Today's server is in Helsinki, Finland. A United States location (Ashburn, Virginia) is planned. We will change this line when that happens.docs/HOSTING_PACEHQ.md
- Control plane: Railway (United States). Edge: Cloudflare.landing/privacy.html
- In transit: public endpoints use HTTPS. TLS ends at Cloudflare and at our Traefik proxy. Traffic between our own services stays on private Docker networks.docs/ARCHITECTURE.md
- At rest: the database is encrypted at rest by Supabase. We do not add a second layer on top.
- Vendors: the full list is on the subprocessors page.
AI agents and model providers
- Risky actions wait for a person. New workspaces start in a supervised mode where every agent action is queued for a human to approve. More autonomy is earned over time, and write and high-risk actions still queue. Approval decisions are stored under row-level security.docs/AGENTS.md (autonomy tiers), supabase/migrations/20261004000004_agent_approval_decisions.sql
- Agent actions are logged with the action, the tier it ran under, the outcome and the cost.docs/AGENTS.md
- Who sees prompts. Agent requests go from our agent service to OpenRouter, which routes them to a model host. The text sent is whatever the task needs. We prefer United States hosts for one model family. We do not control the retention or training terms of the model hosts, so check OpenRouter's policy and the host's.sidecar/pace_agents/openrouter_models.py
- Bring your own key. A workspace can use its own model key instead of ours. The key is stored encrypted; only a short preview is readable.supabase/migrations/20260626200000_tenant_ai_provider_configs.sql
Cancelling, retention and deletion
- Cancelling is in Settings. Your workspace and data stay available until the end of the paid period. After that, access ends.ui/src/pages/settings/CancelDialog.tsx
- Export and deletion are by request to privacy@pacehq.io. Workspace data is kept until you ask for deletion or the subscription ends, plus any export window in your agreement.landing/privacy.html
- Logs and billing: metrics and logs are kept 30 days. Billing records are kept as tax and accounting law requires.landing/privacy.html
What we do not have
- No SOC 2 report yet. We have not started an audit, so there is no report to share. We also have no ISO 27001 certificate and no independent audit report.
- No published backup or recovery-time commitment. We do not make one on this page.
- No self-serve data export or delete button. Both are handled by email today (see "Cancelling, retention and deletion").
- Some apps are shared with no proven boundary between companies. We do not offer those to customers (see "Apps").
Report a security issue
- Email security@pacehq.io with steps to reproduce and the impact. We aim to acknowledge within a few business days. Also at /.well-known/security.txt.
- Platform status is at pacehq.io/status. Errors go to Sentry; we do not log passwords or card numbers.
Join the waitlist
Leave your work email. No account needed. We will invite you when your workspace is ready.
Free. No card needed.