Security overview

Last updated: 2026-04-28.

Safe to Publish handles draft marketing material that may quote clients, employees, or third parties by name. This page describes the technical and organizational controls we apply to keep that data confidential, available, and intact. It is the operative security overview referenced in §4 of our Data Processing Addendum.

Safe to Publish is operated by Zackary Trenholme as an Ontario sole proprietorship; we do not yet hold a SOC 2 Type II report. The controls below are nonetheless implemented in production. We will update this page as our control posture changes.

1. Encryption

  • In transit. All HTTPS endpoints are served over TLS 1.3 with HSTS preload. The application itself does not accept plaintext HTTP.
  • At rest. The Postgres database is encrypted at rest using AES-256 by the managed-database provider (Supabase). Backups inherit the same encryption.
  • Reviewer-call transit. Calls to the Anthropic reviewer model are TLS 1.3 to the Anthropic API endpoint. Anthropic Zero Data Retention is enabled — drafts are not retained by the model provider.

2. Authentication and session security

  • Authentication is delegated to Clerk. Passwords are stored only by Clerk (never by Safe to Publish), salted and hashed per Clerk's implementation.
  • Sessions are short-lived JWTs issued by Clerk and verified on every request via the Clerk middleware.
  • Multi-factor authentication, passkeys, and SSO (where the customer has it) are available and configured in your Clerk-backed account settings.

3. Tenant isolation

  • Every firm-scoped table (firms, users, content_items, reviews, flags, audit_events, disclosure_packs, subscriptions, invitations) has Postgres Row-Level Security enabled with a fail-closed policy: a query that does not set app.firm_id to the row's firm sees zero rows and cannot insert, update, or delete.
  • The application connects to Postgres as clearmark_app — a non-superuser role created with NOSUPERUSER NOBYPASSRLS — so the policies actually enforce. The owner role used for migrations is never used at runtime.
  • Server actions and route handlers wrap every firm-scoped query in withFirmContext(firmId, fn), which sets the GUC inside a transaction and clears it on exit.
  • A second, deliberately limited connection (stp_service, also non-superuser but with BYPASSRLS) is used only by the few flows that must operate across firms by design — the Stripe webhook's customer-to-firm lookup, invitation token resolution, and the existing-user-by-identity lookup that runs before the firm context is known. Every call site is grep-auditable as dbService.

4. Audit log

  • Every mutation to a review (create, flag dispositioned, plan changed, subscription event, etc.) appends a row to audit_events.
  • Each row includes a SHA-256 hash chained to the previous row in the firm's audit log. Tampering with any historical row breaks the chain and is detected on export.
  • Customers can export the entire audit log + all reviewed content and flag dispositions as PDF + CSV from the Audit page in the application.

5. PII auto-redaction

Before any draft is sent to the reviewer model, the application scans for and substitutes detected Social Security Numbers, email addresses, and US phone numbers with same-length placeholders. The substitutions are reversed when the result is rendered back to you, so flag offsets remain correct. Detected counts and types are recorded on each review row for transparency.

6. Backups and recovery

  • The database is backed up daily by Supabase with point-in-time recovery available within the retention window of our managed-DB plan.
  • Application code is versioned in a Git repository; deploys are reproducible from any commit.
  • Configuration secrets are stored only in Vercel's encrypted environment-variable store and are never written to logs or version control.

7. Logging and monitoring

  • Request and error logs are captured by Vercel and retained for 90 days.
  • Application errors are forwarded to Sentry. PII is filtered out of error payloads before transmission. Stack traces include source-mapped file references but never request bodies.
  • Product-usage events (page-views, feature-usage) are sent to PostHog. Draft text and review results are never sent to PostHog.

8. Vulnerability management

  • Dependencies are tracked by GitHub's native dependency graph; security advisories trigger an alert and a same-week patch.
  • The application stack (Next.js, Drizzle, Postgres) is updated to the current minor release at least quarterly, and to a current major release at least annually.
  • Penetration tests are not yet scheduled; we will publish results when we engage one.

9. Incident response

Confirmed personal-data breaches are notified to affected customers without undue delay and in any event within seventy-two (72) hours of becoming aware, per §6 of the DPA. Notifications include the information required by Article 33(3) GDPR to the extent then known.

10. Reporting a vulnerability

If you believe you have found a security vulnerability in Safe to Publish, please email support@safetopublish.com with a description and steps to reproduce. We will acknowledge within 72 hours. We do not currently offer a paid bug bounty.

Questions or a customer security questionnaire? support@safetopublish.com.