Security

Last updated July 23, 2026

This document is in force for our closed beta and is pending review by counsel. Questions? erikbigelow@gmail.com

How to report a vulnerability, and how we protect the data in your account.

Reporting a vulnerability

Email erikbigelow@gmail.com with a description of the issue, the steps to reproduce it, and the URL or feature where you found it. A proof of concept, if you have one, is the fastest way for us to confirm and fix it. We read this inbox directly and will acknowledge your report and keep you updated as we investigate. We do not currently run a paid bug-bounty program — we can't offer a reward, but we take every report seriously.

Give us a reasonable window to investigate and fix an issue before disclosing it publicly. If you report in good faith and avoid privacy violations, data destruction, and disruption to other people’s use of the service, we will not pursue legal action or refer you to law enforcement over your research. Test only against your own account and data — do not access, modify, or exfiltrate another operative’s data to prove a point.

How we protect data

Contact email addresses and phone numbers are encrypted at rest, with a one-way hash stored alongside so the product can find a contact without decrypting the stored value. That protection is not complete: contacts imported through CSV also get their email written into a separate, unencrypted profile field, so the same contact’s email can exist in both an encrypted and a plaintext form depending on how they entered the system. Our Privacy Policy’s "How we protect it" section has the full explanation — we are not going to give it inconsistently between two documents.

Your own account email, name, and codename are stored in plain text today — the database has encrypted-column scaffolding reserved for all three, but no code path writes to it yet. Your password is different: it is hashed by our authentication library before storage, and this application never sees or keeps it in plain text. Payment card numbers never reach our servers either — Stripe tokenizes them in your browser, and we only ever hold the resulting customer ID.

In production every response is served over HTTPS with HTTP Strict Transport Security enabled, plus a content security policy and standard hardening headers (X-Content-Type-Options, X-Frame-Options, a restrictive Referrer-Policy). Inbound webhooks are signature-verified before we act on them — Resend’s payloads are checked with a signed-webhook library, Stripe’s against a webhook secret — and anything that fails verification is rejected outright.

Every request that touches business data is scoped server-side to the businesses the signed-in user actually has access to — the same code path that resolves which business you are viewing also decides what you are allowed to see, not just what the interface renders. We are in closed beta and every account is reviewed by hand before activation; that status is checked against the live database on every request, so a revoked account loses access on its next request rather than at its next login. Sign-in and other sensitive endpoints are rate-limited.

Text submitted to the product’s AI chat and generation endpoints is scanned for personal data — email addresses, phone numbers, and similar identifiers typed into a prompt — before it reaches any AI provider. A request that trips that check is blocked, not forwarded.

Infrastructure

We run on Vercel for hosting and serverless functions, Neon for our PostgreSQL database, and Amazon S3 for file storage. Upstash provides our rate limiting, caching, and background job queue. Sentry receives error reports when something breaks in production.

That is the infrastructure this product runs on, not the full list of services your data can reach — AI providers, sending providers, and the accounts you connect are covered in the Service providers section of our Privacy Policy.

Questions about this document

Email erikbigelow@gmail.com.