Security
Concrete measures — not marketing claims — protecting every account and every pilot deployment. Audited during our launch readiness review on 2026-04-17.
Encryption
- In transit: TLS 1.3 on every public endpoint. HTTP Strict Transport Security (HSTS) enforced with a 2-year max-age and
includeSubDomainspreload. - At rest: AES-256 on the application database (Supabase-managed Postgres 17). File uploads encrypted at rest in Supabase Storage.
- Secrets: API keys and webhook secrets are never logged. Application secrets live in Vercel environment variables, segregated per environment.
Access control & data isolation
- Row Level Security (RLS) is enabled on every table holding user data — profiles, conversations, messages, API keys, violations, moderation logs, safety events, billing transactions, user memories. Each policy scopes reads to
auth.uid() = user_id. - API keys are stored as SHA-256 hashes only; raw keys are shown once at creation. Lookups happen against the hash.
- Customer isolation: Cross-tenant reads are technically impossible through the public API. Admin client usage is limited to billing / security / webhook paths and always filters by
user_idexplicitly — defense in depth on top of RLS. - Max 3 active API keys per user, enforced at the database level via a trigger, not an application-level check.
Authentication
- Supabase Auth with password hashing via bcrypt.
- Google OAuth supported for customers who prefer SSO.
- Session cookies are HttpOnly, Secure, and SameSite=Lax.
- Optional two-factor authentication on the user profile.
- Post-auth redirects are validated as same-site paths — no open-redirect exposure.
Moderation & abuse prevention
- Every chat message passes through a layered pipeline: rate-limit check, input sanitization, OpenAI Moderation API, local jailbreak regex, progressive violation penalties.
- Default-deny posture: when a security dependency (Redis, Supabase) is unavailable, requests return
503 Service Unavailablerather than bypassing checks. - Progressive violation penalties: 10 min → 1 hour → 24 hour → 7 day → permanent. Zero tolerance for CSAM, terrorism, credible threats against persons, or content sexualizing minors.
- Rate limits enforced per user, per endpoint, and per client IP through Upstash Redis, with a bounded in-memory fallback if Redis is unavailable.
Crisis-safety webhooks
- Per-key pattern detection for suicide ideation, self-harm, first-person abuse disclosure, child-at-risk signals, and explicit violent threats.
- Events delivered to your HTTPS endpoint with an
X-JCIL-Signature: sha256=…header; verify with HMAC-SHA256 over the raw body. - Webhook URLs must use
https://; the application rejects non-TLS targets. - Webhook dispatch happens after the response finishes streaming so it never delays the visitor's answer.
Billing & payment security
- All payments flow through Stripe. JCIL.AI never receives card numbers; we're out of PCI-DSS scope.
- Stripe webhooks are verified with Stripe's signed payload and de-duplicated by
session.idso retries cannot double-credit. - Atomic charge + monthly-cap checks live in a single Postgres function to prevent race conditions under concurrent chat requests.
Vulnerability disclosure
If you believe you've found a security issue in JCIL.AI, please report it through the compliance contact form with topic: security. We respond within 48 hours, acknowledge good-faith reporters, and do not pursue legal action against researchers following coordinated disclosure.
Machine-readable disclosure info lives at /.well-known/security.txt per RFC 9116.
Change log
- 2026-04-17 — Launch readiness audit closed these gaps: row level security added to
api_keysandmoderation_logs; explicit policies added toviolations; Stripe webhook deduplication viastripe_session_idunique index; atomic spending-cap enforcement; security pipeline failing closed on infra outage; DOMPurify sanitization of Excel/Word preview HTML.
Last updated: April 17, 2026.