Encryption
- ✓TLS 1.3 minimum on all connections
HSTS preload-eligible header configured:
max-age=31536000; includeSubDomains; preload - ✓AES-256 at rest
Database encryption managed by Supabase, our SOC 2 Type 2-certified Postgres provider.
- ✓HttpOnly + Secure cookies
All authentication and session tokens use both flags — JavaScript can't read them, and they're never transmitted over plain HTTP.
Access Control
- ✓Row-Level Security on every database table
RLS policies enabled on all six tables: votes, verified contractors, vote quotes, audit log, embed pings, and email opt-ins.
- ✓Anonymous reads hit materialized views only
Public API endpoints serve aggregate scores from materialized views — never raw vote rows. Individual voter records are not readable from the client.
- ✓Service role keys live server-side only
Privileged Supabase keys are stored as Netlify environment variables, accessible only to server-side functions. They never appear in client-rendered HTML, JavaScript, or shipped bundles.
- ✓JWT sessions: HS256, fixed 6-month expiry, revocable
Verified contractor sessions use HS256-signed JWTs in HttpOnly + Secure cookies, with a fixed 6-month expiration. Sessions can be revoked on demand from /data-rights/.
Anti-Abuse
- ✓Cloudflare Turnstile on every state-changing endpoint
Vote submission, batch vote, license verification start, vote deletion, and data export — all require a valid Turnstile token before processing. No CAPTCHA frustration; invisible challenge for normal users.
- ✓IP-based rate limiting
10 votes per hour, 5 verifications per day, 3 data-rights actions per day — all keyed on hashed IP. Excessive activity returns HTTP 429.
- ✓Cookie-based duplicate-vote prevention
Each browser receives a random voter token (no PII). The database enforces a unique constraint on (voter_token, product, category) — re-voting upserts the existing record instead of creating duplicates.
- ✓SHA-256 HMAC IP hashing
Both full-IP and subnet-level hashes are stored for forensic and rate-limiting purposes. Plaintext IPs are never written to the database. Hashing is one-way.
- ✓Quote moderation queue
Verified contractor quotes are auto-screened for profanity and defamation patterns before reaching public display. Flagged submissions route to a manual review queue; clean submissions auto-approve.
What We Never Collect
- ✗Driver's licenses, passports, or any government-issued photo IDs
- ✗Social Security Numbers
- ✗Payment information or credit card data
We don't take payments from contractors. There's no payment surface to attack.
- ✗Customer lists, business financial data, or CRM records
- ✗Plaintext IPs or user-agents
Everything used for abuse detection is one-way hashed before storage.
- ✗Credit history or anything requiring a credit pull
Compliance & Standards
We align with the controls below. We do not currently hold an independent SOC 2 attestation as an entity — we inherit Supabase's audited controls at the database layer and apply equivalent application-layer controls openly documented on this page.
- SOC 2 Type 2 — inherited via Supabase, our managed Postgres provider. Their audit covers access management, change management, encryption, monitoring, and incident response at the database tier.
- GDPR Article 32 — appropriate technical and organizational measures, including encryption at rest and in transit, pseudonymization (IP hashing), and the ability to restore access after incidents.
- CCPA & CPRA reasonable security practices — California Civil Code 1798.81.5. Data minimization is the primary control: we don't collect what we don't need.
- OWASP Top 10 mitigations — input validation, parameterized queries (via Supabase client), CSRF protection, secure session management, and XSS protection through framework defaults and CSP-aligned headers.
Breach Response
If we confirm a data breach, our response runs in this order:
Containment, scope assessment, evidence preservation.
Per GDPR Article 33 timing — direct email to anyone whose data was affected, with specifics on what was exposed and recommended next steps.
Filed per applicable state law (50 states have varying notification requirements; we follow the strictest applicable threshold for affected users).
A clearly labeled incident notice with date, scope, affected user counts, root cause, and remediation steps taken.
After remediation, a public post-mortem describes what happened, what we changed to prevent recurrence, and any policy or control updates rolled out as a result.
Why We Publish This
Public security documentation is the industry standard — Wirecutter, Stripe, GitHub, Cloudflare, and every major editorial site publish similar pages. The reasoning:
- Researchers need a known reporting path. Without a published policy and an RFC 9116
security.txt, vulnerability researchers don't know how to reach us — and they may go public before we can fix anything. - Trust is verifiable, not assumed. Readers, partners, and AI search platforms vetting our editorial credibility expect to see specifics, not a vague "we take security seriously" sentence.
- Nothing on this page is exploitable intel. TLS versions are visible in HTTP response headers regardless. Hashing algorithms can't be reversed. Rate-limit thresholds are discovered through normal probing. Architectural patterns (RLS, JWT, Turnstile) are design choices, not attack surface.
What we deliberately do not publish: software versions, active vulnerabilities under remediation, internal architecture diagrams, specific firewall or WAF rules, or names of individual security personnel. The signal-to-noise of public security pages favors defenders.
Your Rights
Self-service at /data-rights/: export everything we have about you, delete a single vote, or fully erase your account. All deletion requests confirm via email before running. Full privacy details in our privacy policy; full terms in our terms of service.