1. The three pillars
The product's trust posture rests on three things:
- Encryption at rest. Source credentials live in Supabase Vault (pgsodium · AES-256-GCM). Never in plaintext, anywhere.
- Tenant-scoped access. Every table is gated by Row-Level Security keyed on tenant membership. The web app holds the anon publishable key and cannot decrypt credentials.
- Audit trail. Every credential write, read, and revoke is logged with actor + timestamp. You can see the last six entries in each source's drawer.
2. Encryption
In transit
TLS 1.2+ everywhere. Worker → upstream API calls use the upstream's own TLS.
At rest — Postgres & Storage
Supabase encrypts Postgres data and Storage objects at rest by default (AES-256, managed keys).
At rest — credentials
Source tokens are stored via supabase_vault (pgsodium / libsodium) with AES-256-GCM and per-record nonces. The vault decrypt operation is privileged — it's callable only by the worker holding the service-role JWT, never from the browser.
After you save a credential, the UI immediately shows ••••a3b9 and a VAULT pill and discards the cleartext from React state. To replace a value you click Replace; unchanged fields are preserved server-side via a vault-merge so you never have to re-enter the rest.
3. Tenant isolation
- Every multi-tenant table (
tenants,memberships,sources,contact_events,kb_documents,insights,credential_audit_log) is gated by RLS keyed on the calling user's tenant membership. - Tenant creation goes through a
SECURITY DEFINERRPC (create_tenant_and_membership) rather than direct INSERTs — the naïve INSERT-with-check pattern would let a curious user join any tenant whose UUID they could guess. - Writes that affect Vault or the audit log run through SECURITY DEFINER RPCs as well:
save_source_credentials,disconnect_source,log_credential_access. - Storage uses the
kbbucket with RLS policies keyed on a path-encoded tenant prefix — a user cannot download or list another tenant's KB files.
4. Audit trail
The credential_audit_log table records:
- Action — write, read, revoke.
- Source — kind + source ID.
- Actor — the user UUID for web actions; the ingestor name for worker reads.
- Detail — a short note (e.g. which fields were replaced).
- Timestamp — UTC.
You can see the last six entries on any source drawer's Recent access panel. A full log export is available on request to hello@trythroughline.ai.
5. Operations
- Where data lives: Supabase project, ap-southeast-1 (Singapore).
- Hosting: Vercel (web app, stateless). The worker is deployed on Cloud Run or Railway (stateless, region-pinned).
- Backups: Supabase's managed daily backups (point-in-time recovery available).
- Sub-processors: See Privacy §6.
- Breach notification: If we discover a confirmed breach affecting your data, we will notify you within 72 hours of confirmation with what happened, what data was affected, and what we're doing about it.
6. Connection guide — seven habits
If you're wiring up sources for your team, treat these as defaults:
- Generate a dedicated token named “Throughline”. Don't reuse your personal admin login. A dedicated PAT / system user / service account can be revoked without breaking anyone.
- Use the exact scopes the drawer lists — no more. If we ever ask for more later, the change will be flagged before the next pull runs.
- Use a shared / business account for Meta, not personal. A personal Facebook token dies when the person leaves; a Business-Manager system-user token survives team changes.
- Rotate every ~90 days. Set a quarterly reminder. Open the drawer, click Replace, paste the new value, Save. Unchanged fields are preserved via vault merge.
- Revoke from both sides when someone leaves. Disconnect inside Throughline (clears the Vault entry + the
sourcesrow + writes a revoke audit entry), then revoke upstream via the Manage upstream ↗ link in the drawer. Throughline-side disconnect kills our access; upstream revoke kills the token globally. - Watch the audit log for unexpected reads. Worker reads happen on schedule (hourly / daily). A burst at an odd time is anomalous — open the source's Recent access panel. Web-side writes (you replacing a key) show as
user:entries. - Prefer OAuth over PAT/token where it exists. For Google sources (GSC, YouTube, GA4), OAuth means Throughline never sees your password and Google can revoke our access from their side any time. PAT/token-only tools (Calendly, Mailchimp, Amplitude) don't give us that option — pick OAuth where the choice exists.
7. Reporting an issue
Found a vulnerability? Email hello@trythroughline.ai. We commit to:
- Acknowledge receipt within 24 hours.
- Provide an initial assessment within 72 hours.
- Keep you posted while we work on a fix and credit you in our advisory if you want recognition.
- Not pursue legal action against good-faith research that respects user data and gives us a reasonable window to remediate.
Please don't run automated scans against the production environment without prior coordination — they page our oncall. For research purposes we can spin up a sandbox tenant; email first.