This is the seven-area builder's-security checklist, answered honestly for this platform, backed by the actual code and configuration. It is a working self-assessment, not a third-party attestation — the same posture the product itself takes. Each item is marked for where it stands, with a pointer to the evidence.
You can't secure what you can't see.
| Checklist item | Status | Where it stands |
|---|---|---|
| One-page description of what the app does and who uses it | in place | Four areas — Learn, Career, Apply (learner side), Deliver (advisor side). See PLATFORM-TLDR.md and the area maps. |
| List every place data is stored | in place | ~33 tenant-scoped eb_* Postgres tables (all under RLS), plus auth.users (email), profiles (name), payments (Stripe IDs), onboarding responses. No file storage — evidence files stay in the client's own Box/SharePoint (we index metadata only). Browser localStorage holds only UI prefs and debounced drafts — no PII, no secrets. |
| Diagram how data flows in, through, and out | in place | Browser (Vite SPA) → Supabase (Postgres + RLS, Auth, ~39 edge functions) → external APIs, server-side only. Rendered diagram in RAMPQUEST-TECH-BRIEF.html §2. |
| Identify which parts are AI/LLM-powered and what they can access | in place | Anthropic Claude only: Sonnet 4.5 (drafting, portfolio Q&A, persona commands) and Haiku 4.5 (lab feedback, recording enrichment). Two-lane boundary: the learner Tutor sees the public control library only; the Deliver copilot sees the active client only. Neither browses the web. Full map in §5 and docs/ai-guardrails.md. |
| Note what runs in the browser vs. on the server | in place | Browser: RLS-scoped reads with a publishable anon key, never any AI or external-API call, never a service key. Server (Supabase edge functions, Deno): every external call (Anthropic, Stripe, Zoom, Box, HubSpot, Resend) and every service-role write. |
Know exactly what you touch.
| Checklist item | Status | Where it stands |
|---|---|---|
| Inventory the data collected, categorized | in place | PII: email (auth.users), display name (profiles). Payment: Stripe customer/subscription IDs only — no card data touches our DB. Operational: onboarding prefs; client compliance/evidence data in eb_* (tenant-owned). No health/PHI, no phone. |
| Confirm you only collect what you actually need | in place | Minimal PII (email + name); no card data, no health data, no phone. Payment handling delegated to Stripe. |
| Document where data lives and how long you keep it | partial | All data in Supabase Postgres, US East (AWS us-east-2, Ohio). Retention: operational data lives for the account's life; the audit log has no formal retention/TTL yet (a 90+ day SLA is the open item — see §6/§7). |
| Make sure you can delete a user's data on request | partial | Tenant-level: built — one-click JSON export, and a two-step suspend → terminate that cascade-deletes every child row and writes a durable proof-of-deletion record (eb_terminate_tenant). User-level self-deletion: gap — an individual account/profile delete isn't wired yet. |
| Check whether any data leaves your region/country | in place | No. Single US region (aws-1-us-east-2). Optional connectors (Box, HubSpot, Zoom, M365) only activate when an admin connects them with the tenant's own credentials. |
Decide who can get in — and to what.
| Checklist item | Status | Where it stands |
|---|---|---|
| Require strong auth (passwordless or MFA) for all users | in place | Supabase Auth (email/password, magic link, Google OAuth) with MFA / AAL2 step-up and an opt-in per-tenant "MFA required" gate. |
| Enforce row-level security so users only see their own data | in place | RLS on every eb_* table (39 tables, 162 policies), keyed by eb_my_tenants(). The last legacy per-client tables were migrated onto the tenant model in July 2026 — no table falls back to broader access. (Scripted cross-tenant isolation test is written but not yet run — see §7.) |
| Use least-privilege roles for admins and service accounts | in place | JWT-gated edge functions; platform-superadmin vs. tenant-admin vs. advisor split; optional per-member client scoping limits each consultant to assigned clients. |
| Remove default, test, and demo credentials | in place | No seeded credentials in the repo (verified by scan). The demo tenant "GrantFlow AI" is fictional sample content, not a login. |
| Log who accessed what, and when | in place | Consolidated, tenant-scoped eb_audit_log: DB triggers record who-changed-what across the security-relevant tables, plus invite/export/termination events. Admin-readable; never crosses the tenant boundary. |
Close the leaks that sink most AI-built apps.
| Checklist item | Status | Where it stands |
|---|---|---|
| No API keys, tokens, or passwords in client code or the repo | in place | Scan found no hardcoded secrets in src/ or the edge functions. Everything reads from Deno.env.get (server) or import.meta.env.VITE_* (client). The two client-exposed keys — the Supabase anon key and the Stripe publishable key — are safe by design (RLS-protected / publishable). |
| Store secrets in environment variables or a secrets manager | in place | 31 env vars for the external services; OAuth tokens for connectors held in Supabase Vault, not in application tables. .env is gitignored (only .env.example is tracked). |
| Lock down public storage buckets and database tables | in place | No public storage buckets exist. All tenant tables are RLS-protected (§3). |
| Turn off verbose error messages in production | partial | Edge functions return the raw error string in JSON (not stack traces, but it can leak internal detail). Recommend logging full errors server-side and returning a generic message. Related: the Content-Security-Policy is still in Report-Only mode (monitoring, not enforcing) — flip to enforced in production. |
You inherit your vendors' risk.
| Subprocessor | Used for | Mode |
|---|---|---|
| Anthropic (Claude) | AI drafting (Sonnet 4.5), lab feedback + recording enrichment (Haiku 4.5) | Always-on* |
| Supabase | Postgres, Auth, edge functions, Vault | Always-on |
| Vercel | Web hosting / CDN / security headers | Always-on |
| Stripe | Payments (cards never touch our DB) | Always-on |
| Resend | Transactional email | Always-on |
| Bunny Stream | Recording video hosting | Always-on |
| Box / Microsoft 365 | Evidence file metadata only — never content | Optional connector |
| HubSpot | One-way push of computed compliance values | Optional connector |
| Zoom | Meetings, recordings, transcripts, attendance | Optional connector |
| SerpAPI | Job-listing ingestion for the opportunities feed | Scheduled |
*AI degrades gracefully if no key is configured. Optional connectors activate only when an admin connects them with the tenant's own credentials; tokens live in Vault; syncs are admin-gated.
| Checklist item | Status | Where it stands |
|---|---|---|
| List every external service and AI model your app calls | in place | Table above; 10 subprocessors, all server-side. |
| Check each vendor's data-use and retention terms | in place | Anthropic: inputs/outputs not used to train, zero-data-retention available. Connectors are metadata-only / one-way. Documented in the tech brief and procurement answers. |
| Confirm customer data isn't used to train third-party models | in place | Confirmed for Anthropic via API terms (no training; ZDR available for client-confidential flows). |
| Have a fallback if a vendor goes down or changes terms | partial | AI features fail soft (optional, graceful fallback). No documented per-vendor fallback runbook yet. |
| Add basic input and output guardrails on AI features | in place | First-party grounding (cite only real control IDs), deterministic scoring core (AI drafts prose only), human-in-the-loop on every output, no web/tool access. Full detail in docs/ai-guardrails.md. |
What turns "trust me" into a passed questionnaire.
| Checklist item | Status | Where it stands |
|---|---|---|
| Keep a current architecture and data-flow diagram | in place | RAMPQUEST-TECH-BRIEF.html §2 (system) and §6 (library reuse). |
| Write a short security overview you can hand to a buyer | in place | This document, plus docs/security-posture.md and RAMPQUEST-PROCUREMENT-ANSWERS.md. |
| Document your incident-response steps: who, what, how | partial | A v1 runbook is adopted (below): owner Aysha Davis, standard notification timeline. Not yet exercised in a tabletop. |
| Track your subprocessors and where data is hosted | in place | §5 above; hosting is US East (Ohio). |
| Keep a changelog of security-relevant changes | gap | No dedicated security changelog. (Architectural decisions are logged in DECISIONS.md, but not security events specifically.) |
Keep it true after launch day.
| Checklist item | Status | Where it stands |
|---|---|---|
| Assign an owner for security and compliance decisions | in place | Owner: Aysha Davis (Harvest & Fort Industries) — accountable for security and compliance decisions. |
| Map your obligations (SOC 2, HIPAA, GDPR, state laws) to reality | partial | SOC 2: not held; infra (Supabase, Vercel, Stripe) is SOC 2 — our own is roadmap. HIPAA: N/A (no PHI). GDPR: US-hosted, minimal PII; a subprocessor list + DPA is the partial piece. State laws: unmapped. Full mapping is a gap. |
| Review access and vendors on a set cadence | gap | No set cadence yet. Recommend quarterly access + vendor review (fits the platform's own quarterly PMO rhythm). |
| Have a plan for AI model updates and drift | partial | Model IDs are pinned (Sonnet 4.5 / Haiku 4.5) via env, so nothing changes silently. No formal re-eval/drift-check plan when models are upgraded. |
| Decide what you'll do when something goes wrong | partial | Covered by the v1 IR runbook below; mature it with a tabletop exercise. |
SECURITY.md / responsible-disclosure channel, (3) an audit-log retention SLA (the log exists; a retention window doesn't), (4) a formal SSP + POA&M (security-posture.md is the starting point), (5) a scheduled vulnerability-scan / pen-test cadence, (6) a data-classification doc, (7) user-level self-deletion, (8) a security changelog. (A named security owner — previously open — is now assigned: Aysha Davis.) Two validation items also remain: run the cross-tenant isolation test, and sanitize production error messages.
Immediate (days): run the checked-in cross-tenant isolation test against a disposable DB; sanitize edge-function error messages; add a SECURITY.md with a disclosure contact.
Short (weeks): mature the incident-response runbook (below) with a tabletop; set an audit-log retention SLA and enforce it; write a one-page data-classification doc; set a quarterly access/vendor review.
Medium (months): wire user-level self-deletion; formalize the SSP + POA&M off security-posture.md; establish a vuln-scan / pen-test cadence; flip CSP from Report-Only to enforced.
Adopted v1 — lightweight but real. Mature it with a tabletop exercise and per-vendor detail.
eb_audit_log, Vercel logs, and vendor status pages. Reviewed on a weekly cadence, plus any real-time alert.eb_suspend_tenant to freeze the affected tenant; rotate any implicated secret (env / Vault) immediately.Aysha Davis · app.harvestandfort.com · Working self-assessment, not a third-party attestation · July 2026