HFI GRC Work OS — Security Self-Review

We ran the "is your AI-built app ready for a security review?" checklist on our own app · Live: app.harvestandfort.com · In production · July 2026

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.

in place implemented & verified in code partial exists, not yet complete gap not built yet

01Map your system

You can't secure what you can't see.

Checklist itemStatusWhere it stands
One-page description of what the app does and who uses itin placeFour areas — Learn, Career, Apply (learner side), Deliver (advisor side). See PLATFORM-TLDR.md and the area maps.
List every place data is storedin 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 outin placeBrowser (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 accessin placeAnthropic 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 serverin placeBrowser: 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.

02Data & privacy

Know exactly what you touch.

Checklist itemStatusWhere it stands
Inventory the data collected, categorizedin placePII: 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 needin placeMinimal 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 itpartialAll 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 requestpartialTenant-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/countryin placeNo. 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.

03Access & authentication

Decide who can get in — and to what.

Checklist itemStatusWhere it stands
Require strong auth (passwordless or MFA) for all usersin placeSupabase 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 datain placeRLS 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 accountsin placeJWT-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 credentialsin placeNo 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 whenin placeConsolidated, 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.

04Secrets & configuration

Close the leaks that sink most AI-built apps.

Checklist itemStatusWhere it stands
No API keys, tokens, or passwords in client code or the repoin placeScan 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 managerin place31 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 tablesin placeNo public storage buckets exist. All tenant tables are RLS-protected (§3).
Turn off verbose error messages in productionpartialEdge 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.

05Third-party & model risk

You inherit your vendors' risk.

SubprocessorUsed forMode
Anthropic (Claude)AI drafting (Sonnet 4.5), lab feedback + recording enrichment (Haiku 4.5)Always-on*
SupabasePostgres, Auth, edge functions, VaultAlways-on
VercelWeb hosting / CDN / security headersAlways-on
StripePayments (cards never touch our DB)Always-on
ResendTransactional emailAlways-on
Bunny StreamRecording video hostingAlways-on
Box / Microsoft 365Evidence file metadata only — never contentOptional connector
HubSpotOne-way push of computed compliance valuesOptional connector
ZoomMeetings, recordings, transcripts, attendanceOptional connector
SerpAPIJob-listing ingestion for the opportunities feedScheduled

*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 itemStatusWhere it stands
List every external service and AI model your app callsin placeTable above; 10 subprocessors, all server-side.
Check each vendor's data-use and retention termsin placeAnthropic: 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 modelsin placeConfirmed for Anthropic via API terms (no training; ZDR available for client-confidential flows).
Have a fallback if a vendor goes down or changes termspartialAI features fail soft (optional, graceful fallback). No documented per-vendor fallback runbook yet.
Add basic input and output guardrails on AI featuresin placeFirst-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.

06Evidence & documentation

What turns "trust me" into a passed questionnaire.

Checklist itemStatusWhere it stands
Keep a current architecture and data-flow diagramin placeRAMPQUEST-TECH-BRIEF.html §2 (system) and §6 (library reuse).
Write a short security overview you can hand to a buyerin placeThis document, plus docs/security-posture.md and RAMPQUEST-PROCUREMENT-ANSWERS.md.
Document your incident-response steps: who, what, howpartialA v1 runbook is adopted (below): owner Aysha Davis, standard notification timeline. Not yet exercised in a tabletop.
Track your subprocessors and where data is hostedin place§5 above; hosting is US East (Ohio).
Keep a changelog of security-relevant changesgapNo dedicated security changelog. (Architectural decisions are logged in DECISIONS.md, but not security events specifically.)

07Governance & oversight

Keep it true after launch day.

Checklist itemStatusWhere it stands
Assign an owner for security and compliance decisionsin placeOwner: Aysha Davis (Harvest & Fort Industries) — accountable for security and compliance decisions.
Map your obligations (SOC 2, HIPAA, GDPR, state laws) to realitypartialSOC 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 cadencegapNo set cadence yet. Recommend quarterly access + vendor review (fits the platform's own quarterly PMO rhythm).
Have a plan for AI model updates and driftpartialModel 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 wrongpartialCovered by the v1 IR runbook below; mature it with a tabletop exercise.

The honest gaps (nothing hidden)

Most items are in place. The genuine gaps, all documentation/governance rather than broken controls, are: (1) a formal incident-response plan, (2) a 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.

What to fix next (prioritized)

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.

Incident-response runbook — v1, adopted July 2026

Adopted v1 — lightweight but real. Mature it with a tabletop exercise and per-vendor detail.

Aysha Davis · app.harvestandfort.com · Working self-assessment, not a third-party attestation · July 2026