Security
pac.dog's posture is minimal attack surface: the corpus is read-only public data mirrored from primary U.S. government sources, the only persistent user data we hold is an email address (for sign-in), and we sit on audited subprocessor platforms for compute, storage, payments, and email.
Last reviewed: 2026-05-17
Subprocessors
Every system that handles user data is run by a subprocessor with a current independent audit. We do not operate our own hosting, database, payments, or email infrastructure.
| Subprocessor | Function | Independent attestations |
|---|---|---|
| Vercel | App, API, cron, edge cache | SOC 2 Type II, ISO 27001, PCI DSS Level 1 |
| Neon | Postgres database (encrypted at rest) | SOC 2 Type II, HIPAA-aware |
| Stripe | Payments (Checkout, Webhooks) | SOC 1/2 Type II, PCI DSS Level 1 |
| SendGrid (Twilio) | Magic-link + transactional email | SOC 2 Type II, ISO 27001 |
| Replicate | Inference for /ask | SOC 2 Type II |
The data we send each subprocessor is the minimum required to serve the user's request. Stripe never sees pac.dog's database; Replicate sees only the question + the retrieved context block, not user identity or wallet state.
Authentication & session
- No passwords. Sign-in is via single-use magic-link emailed by SendGrid. Magic-link tokens are stored server-side, expire after one use, and have a 15-minute TTL.
- Session cookie (
pacdog_session) is HttpOnly, Secure, SameSite=Lax, 30-day TTL. The cookie value is a 32-byte random token; the database stores only the token, no derived material. - Personal Access Tokens for the REST API are format
pacd_<22-char-base32>(128 bits of entropy). Only the SHA-256 hash and an 8-char prefix are persisted; the plaintext token is shown to the user exactly once at creation time. - Webhook authenticity. Stripe webhooks are verified against
STRIPE_WEBHOOK_SECRETon every request before any side effect runs.
Encryption
- In transit — TLS 1.3 everywhere, terminated at the Vercel edge. HSTS enforced (max-age 63072000). No plain-HTTP listener.
- At rest — Postgres pages are encrypted by Neon using AES-256 on AWS KMS-managed keys. Backups inherit the same envelope. Secrets (API keys, webhook signing keys) are stored as Vercel environment variables and injected into function execution only; they never reach the database or client.
Data we handle — and don't
| Kind | Handled? | Detail |
|---|---|---|
| Email address | Yes | Sign-in identifier. Stored in users.email. |
| Watchlist + reaction + thread content | Yes | User-created records, owned by the user, deletable via the API. |
| Wallet ledger | Yes | Token balance + append-only charge log for /ask. No card data. |
| Card / bank data | No | Stripe Checkout is hosted; card details never reach pac.dog servers. |
| SSN, DOB, government ID, biometrics | No | Not collected. Not stored. |
| Geolocation, device fingerprint | No | No third-party analytics. No tracking pixels. No ad SDKs. |
| Public-record corpus | Yes | FEC, congress.gov, govinfo.gov, IRS Form 990, House Clerk / Senate LIS roll-call XML, Senate LDA, DOJ FARA, Federal Register, Regulations.gov, U.S. Census ACS, each state's SoS / LegInfo. Source URLs preserved on every row. |
Audit trail
- Ingest ledger (
ingest_runs) — every cron + CLI ingest writes a start/end row with status, row counts, watermark, and truncated error text. Append-only. - Wallet ledger (
token_charges) — every balance-changing event lands as one signed row (purchase / use / free_refresh / refund / adjustment). The wallet balance is denormalized; the ledger is the source of truth. Stripe charge IDs are the idempotency key. - Filing-form drift (
filing_form_specs+filing_form_spec_revisions) — every form spec we generate is hashed nightly and compared to the prior hash. Drift surfaces on /status. - Entity-resolution review queue (
person_link_reviews,organization_link_reviews) — heuristic-flagged candidate-to-canonical-person matches that need human review before the alias is promoted. Available to admins at/review/links.
SOC 2 control mapping
pac.dog does not yet hold an independent SOC 2 Type II report. We can speak to the Trust Service Criteria as observed posture today:
| Criterion | Observed posture |
|---|---|
| Security (CC) | TLS 1.3 everywhere, HSTS, magic-link auth, session cookies HttpOnly/Secure/SameSite, PAT entropy ≥ 128 bits, webhook signature verification, Neon-managed at-rest encryption. Subprocessors are SOC 2 Type II. |
| Availability | Vercel edge cache fronts every read endpoint with s-maxage; Neon serves Postgres with multi-AZ replication. Live status: /status. |
| Processing Integrity | Mechanical extractors only — no LLM in any ingest path. Idempotent upserts (ON CONFLICT DO NOTHINGor single-statement CTEs). Source URL preserved on every row. Drift detection on every filing-form spec we generate (/api/v1/filing-forms). |
| Confidentiality | Minimal PII (email only). Cards handled by Stripe Checkout off-platform. API keys + webhook secrets are environment variables, not database rows. Personal Access Tokens are hashed at rest. |
| Privacy | No third-party analytics, no advertising SDKs, no tracking pixels. One first-party session cookie. Users can delete their account + every owned record from the account page. See /privacy. |
ISO 27001 Annex A mapping
| Annex A control | Observed posture |
|---|---|
| A.5 Information security policies | This page + /privacy are the published policies. |
| A.8 Asset management | Single repository, single Postgres database, enumerated subprocessors above. |
| A.9 Access control | Magic-link sign-in; PAT scoping; admin surfaces gated on users.is_admin. |
| A.10 Cryptography | TLS 1.3 in transit; AES-256 at rest (Neon-managed); SHA-256 PAT hashes. |
| A.12 Operations security | Append-only ingest + wallet ledgers; per-source cron with lock; nightly drift checks. |
| A.13 Communications security | HTTPS-only, HSTS, no plain-HTTP listener; Stripe webhook signature verification. |
| A.14 System acquisition, development & maintenance | Single source tree; every change is a versioned commit; database migrations are forward-only files in drizzle/. |
| A.15 Supplier relationships | Subprocessor list above; each is independently audited. |
| A.16 Incident management | Security disclosures to security@pac.dog. Acknowledgment within one business day. |
| A.18 Compliance | Federal data is public-domain (no licensing constraints). User data handled per /privacy. |
What we don't claim
- pac.dog does not hold its own SOC 2 Type II report or ISO 27001 certification.
- We have not yet commissioned an independent penetration test.
- We do not run a public bug bounty program.
- We do not operate a 24/7 staffed security operations center.
Each of these is roadmap. Until they land, the controls above are what we can attest to as the operator. Subprocessor attestations are linked above and verifiable directly with each vendor.
Reporting a security issue
Email security@pac.dog. Include a clear reproduction. We acknowledge within one business day and aim to remediate critical issues within 7 days. Coordinated disclosure preferred; we will not pursue legal action against good-faith research that respects user data.