Skip to content

Security

Your company's data stays your company's.

Every control below is named honestly: in place, available, or planned. We are building toward SOC 2 readiness and do not claim a certification we do not hold.

  • Isolation enforced in the database
  • Encrypted secrets
  • Audit everything
  • MFA: planned

Per-company isolation

In place

Two independent layers: the application refuses to run a query without a company in scope, and the database refuses to store a row without an owner.

  • A database extension merges the company id into every query on every business model and stamps it on every write. No company in scope: the query fails closed. A different company named in the query: a 403.
  • CHECK constraints on every business table make a row without a company id impossible even if the application layer were bypassed.
  • Business identifiers (customer, invoice, estimate numbers, SKUs, slugs, staff emails) are unique per company, never globally, so two companies never collide.
  • Realtime events carry the company id and are dropped for anyone else before any other check. Phone webhooks resolve the company from the called number first.
  • Row Level Security policies for every company-scoped table exist and can be enabled on a dedicated database role; they are a defence-in-depth option, not a dependency.

Host and session binding

In place

A session presented on another company's website yields no user; a cookie can never move data across companies.

  • The hostname is validated by middleware and passed to server code in a header; custom domains must be verified rows. Server code never trusts an arbitrary Host header.
  • A signed-in user's company wins. If the hostname belongs to a different company than the session, the session is ignored for that request.
  • Company users cannot reach platform-staff pages; platform staff cannot act inside a company without an impersonation session (see below).

Passwords and sessions

In place

argon2id hashing, lockouts, database-backed sessions that can be revoked instantly.

  • Passwords are hashed with argon2id (memory 19 MiB, 2 passes). Minimum 12 characters with mixed case and a digit. Never logged.
  • Login lockout for 15 minutes after repeated failures, plus a database-backed rate limit of 8 attempts per 15 minutes per identity.
  • Sessions are database rows bound to an httpOnly, secure, SameSite=Lax cookie with a 14-day lifetime; revoking a row ends the session immediately. Expired sessions are pruned automatically.
  • Every page checks permissions server-side; the edge middleware only redirects and is not an authorisation boundary.
  • Password-reset tokens are stored hashed with an expiry.

Multi-factor authentication

Planned

Not implemented today. MFA for owner, admin and platform roles is on the roadmap; until then use long unique passwords and revoke sessions from Settings → Users if a device is lost.

Encrypted secrets

In place

Provider credentials a company stores (Twilio tokens, SMTP passwords, Discord webhook URLs) are encrypted at rest with AES-256-GCM under a server-held key.

  • Platform-level secrets are marked as secret and encrypted the same way.
  • Health checks report only whether a secret is present, never its value. Audit rows are passed through a sanitiser that redacts password, token, secret and key fields before they are written.
  • Application logs drop stack traces in production and API errors return generic messages for unexpected failures.

Audit logs

In place

Every privileged action is written with actor, action, entity, before/after (secrets redacted), IP and user agent.

  • Companies see their own audit log in the office app (Audit log); platform actions (suspend, cancel, schedule deletion, entitlement override, impersonation start and end) are audited with a reason.
  • Audit rows are append-only and have no automatic deletion.

Support access to your account

In place

Platform staff can only act inside a company through an impersonation session that requires a written reason, lasts 30 minutes, shows a banner in your account and is audited on both ends.

  • The platform console never shows a company's customer data; it shows plan, status, usage and health.

Signed webhooks

In place

Inbound Twilio and Stripe requests are signature-verified before anything else happens.

  • Twilio: the signed URL is rebuilt from the platform's own configured address (never from request headers), the company is resolved from the called number, and the signature is validated with that company's token. Failures return 403 and are recorded.
  • Stripe: signatures are checked against the raw body; every event id is stored once so a retried event is never applied twice.
  • Media-stream sockets for the AI receptionist require a short-lived HMAC token.

Cloudflare in front

In place

TLS termination, CDN and WAF at the edge; Turnstile bot protection on public forms when enabled.

  • Private paths (office, technician and customer portals, platform console, API, guest document links) are sent with no-store cache headers for both browsers and the CDN, and are marked noindex.
  • Uploads are sniffed by magic number (JPEG, PNG, GIF, WebP, HEIC/HEIF, PDF only), capped at 12 MB, stored under random names and served with nosniff.

Backups

In place

A compressed database dump runs nightly on the server, with a documented restore procedure.

  • 14 daily and 8 weekly copies are kept on the server; off-server copies and scheduled restore drills are on the roadmap (see the security programme below).

Least privilege

In place

Role-based permissions per company (owner, admin, dispatcher, office staff, accounting, technician, customer) and a separate platform role set; technicians and customers see only their own jobs and documents.

  • Guest links for estimates and receipts are hashed tokens with expiry and revocation; no account is needed and no other data is reachable from them.
  • The database role used by the application should have no superuser or CREATEDB rights; a dedicated RLS role and a secrets manager are next on the list.

Toward SOC 2 readiness

Security programme

Where each area stands today and what comes next. This is a roadmap, not an attestation.

AreaNowNext
Access controlRBAC by permission, platform/company splitMFA for owner, admin and platform roles
Change managementVersion history, forward-only migrations, documented rollbackProtected branch, review before deploy
LoggingStructured app logs, audit log, provider and billing event logsCentral log shipping and alerting
BackupsDaily database backup, restore procedureOff-server copies, scheduled restore drills
Incident responseWritten outline: detection, containment, evidence, notificationOn-call rota, customer notification templates
VendorsSubprocessor list published on Data handlingData processing agreements with each subprocessor

What we store, where it lives, who can see it and how long we keep it is on the Data handling page. To report a vulnerability, use the support address in the footer or contact us.

Audit log: every privileged action with who did it, when, the entity and a summary.

Questions about a specific control?

Ask before you sign up — we would rather answer than over-promise.