Security overview
Last reviewed 2026-04-29
This page describes the security controls actually in place today. Items we have committed to but not yet implemented are listed separately at the bottom under “Commitments and roadmap” — we don’t mix the two.
Hosting
- Hosted on Google Cloud Platform (Firebase) and Vercel — both SOC 2 Type II attested. Google additionally holds ISO 27001/17/18/701, PCI DSS Level 1, HIPAA, and FedRAMP High. We rely on these inherited controls for at-rest encryption (AES-256 by the platform) and physical security.
- Primary region: United States.
Network and headers
- TLS 1.2+ enforced end-to-end (Vercel platform).
- HSTS with 2-year max-age and preload (
next.config.mjs). - Strict Content Security Policy with per-request nonces; X-Frame-Options DENY; X-Content-Type-Options nosniff (
middleware.js,next.config.mjs). - Server-side outbound HTTP wrapped with an SSRF guard that blocks private CIDRs, link-local, and cloud metadata IPs (
lib/net/safeFetch.js; CI grep-gate viascripts/audit-unsafe-fetch.js).
Identity and access
- Passwordless magic-link signin and OAuth (Google, LinkedIn, Facebook).
- Multi-factor authentication available; mandatory for administrators.
- Role-based access control with distinct admin/user roles.
- CSRF protection on auth endpoints (NextAuth).
Data store
- Firestore Security Rules with user-scoped reads and writes; documents not explicitly allowed are denied. Rule changes go through PR review.
- Backups: Firestore Point-in-Time Recovery is enabled by the platform; we depend on Google Cloud’s default backup posture for the project.
Audit log
- Authentication and session-management endpoints (sign-in, MFA enroll/verify/disable, account export, AI consent record) write to an append-only
auditLogcollection vialib/audit/log.js. - Coverage is not universal across the API surface; expanding coverage is a roadmap item below.
Application security
- Output sanitization for rendered user-provided HTML (
lib/security/sanitizeRichText.js,utils/sanitizeHtml.js). - Rate limiting on selected high-risk endpoints (signup, writer/generate, writer/revise, AI feedback) via
utils/rateLimiter.js. Coverage is not universal across the API surface. - Schema validation (Zod) is in place on a small set of newer endpoints; legacy endpoints validate ad-hoc. We do not currently claim universal schema validation.
- Field-level AES-256-GCM encryption (
lib/crypto/fieldEncrypt.js) for high-sensitivity fields: MFA TOTP secrets (lib/mfa/mfaStore.js) and LinkedIn OAuth session cookies (utils/server-utils/linkedinCookieCrypto.js). Other Restricted-class fields are not yet field-encrypted; broadening coverage is on the roadmap.
Dependency and code security (CI)
- Dependabot for dependency updates (
.github/dependabot.yml). - Semgrep SAST and CodeQL run on every PR (
.github/workflows/security.yml). - SBOM generated per release.
- Custom audit scripts:
scripts/audit-unsafe-fetch.js,scripts/verify-ai-zdr.js,scripts/audit-dpa-register.js,scripts/audit-compliance-claims.js.
Error monitoring
- Sentry for application errors and performance.
- Note: our current Sentry configuration sends default PII (
sendDefaultPii: true) to capture useful debugging context. Tightening this to scrub PII at ingest is on the roadmap below.
AI safety
- Per-request flags minimize provider storage where supported (
store: falseon OpenAI,redact=trueon Deepgram). We have not signed Zero Data Retention amendments; standard provider retention applies (for OpenAI, up to 30 days for abuse monitoring). - We rely on each AI provider’s standard API terms for the no-training default.
- OpenAI Moderation runs on AI outputs (blocks
hate,violence,self-harm,sexual/minors). - Prompt-injection / jailbreak detection in
lib/ai/safetyGuard.js. - AI calls routed through
lib/ai/callWithAudit.jslog only metadata (model, endpoint, token counts, timing) — never prompt or completion content. Coverage is partial: only some AI call sites currently use the wrapper. - See AI disclosure for the full picture, including which features may auto-trigger AI calls in the background.
Compliance posture
- HIPAA: N/A — PHI is prohibited by our Terms.
- PCI DSS: SAQ-A scope; Stripe handles all card capture.
- GDPR / UK GDPR: 2021 EU SCCs and the UK IDTA where required; relies on sub-processor agreements (see Sub-processors).
- CCPA / CPRA: service-provider posture.
Commitments and roadmap
The following are documented as program commitments. Where evidence of execution does not yet exist in the repository, we describe them as such rather than asserting them as ongoing practice.
- Quarterly access reviews — documented in
docs/compliance/internal-audit.md; first review pending. - Annual full + semi-annual partial restore drills — documented in
docs/compliance/BCP.md; first drill pending. - Annual AI bias evaluation across demographic name variations — methodology in
docs/compliance/ai-bias-evaluation.md; first evaluation pending. - 72-hour regulator notification SLA for qualifying breaches — documented in
docs/compliance/breach-notification.md. - Universal audit-log coverage on mutating API routes — currently partial (~8/367 routes).
- Universal schema validation (Zod) on API routes — currently partial (~4/367 routes).
- Tighten Sentry to scrub PII at ingest (
sendDefaultPii: false+ custombeforeSendstripping). - Broader field-level encryption coverage (currently used for MFA secrets and LinkedIn OAuth cookies; extend to other Restricted-class fields per
docs/compliance/data-classification.md). - Wrap remaining direct OpenAI call sites in
lib/ai/callWithAudit.jsfor universal AI audit-log coverage.