Section 01

Multi-tenant isolation

In plain English: every brokerage on HatchOS is a separate tenant, and the system is engineered so that no query, no API endpoint, and no business process can ever return data from one tenant into another's session — even if a UI bug exposes a foreign record ID. The audit closed 9 isolation items in Jun–Jul 2026.

🔒

153 tenant-scoped tables

Every entity (shipments, carriers, invoices, commissions, emails, accounts, dispatch bids) carries tenant_id and every query filters on it. The middleware-injected scope is enforced server-side — not derived from the session alone.

🛡

withTenantClient — RLS-ready

The withTenantClient() helper in middleware/tenant-context.js sets app.current_tenant_id so Phase-1 Postgres row-level security policies fire even on raw pool calls. Every protected route passes through it.

📊

Tenant-scoped stats & KPIs

CRM dashboards, dispatch KPIs, commission totals, carrier scorecards — every aggregate query threads the request tenant_id through SQL parameters. No global counter leak. Audit item #14 verified zero cross-tenant joins during closeout.

🏷

Dispatch bid tenant verification

The dispatch bid path (server.js) re-verifies the bid's tenant_id against the request session before accept or counter — even if the bid record was created under a different tenant context. Audit item #22.

🔒
Nothing leaks by accident. Cross-tenant data bleed can come from forgotten SQL clauses, missing middleware, or shared cache key prefixes. We audit for all three — including a regex sweep of every WHERE tenant_id = $ clause in the codebase.
Section 02

Authentication & access control

Login is hardened at every layer. Weak defaults are absent — not just discouraged. Audit items #1–#11 covered role enforcement, 2FA, session revocation, and cookie security.

admin
Full tenant read/write · manage users, roles, offices, brands · configure integrations · approve commissions and settlements
Cannot impersonate other tenants (God Mode only).
broker_agent
Full dispatch + carrier + invoice surfaces · commission view · ship & quote within scope
Cannot access other agents' accounts, billing, or admin settings unless explicitly granted.
carrier
Carrier portal · assigned loads · document upload · settlement status
Cannot view other carriers' loads, broker financials, or end-customer data.
customer
Shipment tracking · invoice PDFs · document center for own shipments
Cannot view other customers' data, broker financials, or carrier operations.
  • 2FA — email OTP, TOTP, recovery codes. Three methods backed by routes/two-factor-auth.js. Enforced for God Mode platform access. Recovery codes are bcrypt-hashed at rest. Audit items #4–#5.
  • Session-version revocation. Each users.session_version INT (added in migration 1788300000) is checked on every authenticated request — bump it and every active session for that user dies immediately. Used after forced logout, password reset, and account suspension. Audit item #3.
  • Secure cookies + 7-day session TTL. Express-session configured with maxAge: 7 * 24 * 60 * 60 * 1000. Cookies are HttpOnly, Secure, SameSite=Lax in production. Audit items #7–#8.
  • Fail-closed auth middleware. On database errors during session lookup, auth-context.js returns 401 rather than degrading to anon access. We do not serve protected routes when the auth backend is unreachable. Audit item #11.
  • Rate-limited authentication endpoints. Login, signup, OTP verify, and forgot-password each have a sliding-window rate limiter (middleware/rate-limit.js). Lockouts write to security_events. New-IP logins flag auth_anomaly_events. Audit items #9–#10.
🧭
Role enforcement is server-side, not UI-gated. Every protected endpoint validates the caller's role from the session, never from client-supplied headers. Hiding a nav item and blocking the API route are two separate, independently enforced controls.
Section 03

Payments & financial security

Money in motion is the highest-risk surface. HatchOS uses Stripe Connect Standard with broker-controlled sub-accounts — HatchOS itself never holds broker funds, and every inbound Stripe event is signature-verified before it touches a single database row. Audit items #12–#17.

  • Stripe Connect Standard for carrier payouts. Each brokerage onboards its own Stripe Connected Account. HatchOS originates transfers into those accounts; we never pool broker funds, never hold escrow, and never become the merchant of record for carrier settlements. STRIPE_SECRET_KEY is required at boot.
  • Stripe webhook signature validation. The server rejects POST /api/webhooks/stripe without a valid HMAC against STRIPE_WEBHOOK_SECRET. Missing secret fails the process at boot (server.js:127–129) so a misconfigured deploy can't accept forged events. Audit item #12.
  • Event-ID dedup. Every Stripe event is recorded in stripe_webhook_events before downstream side-effects. Re-deliveries are detected by Stripe event ID and short-circuited — a stuck Stripe retry loop cannot double-pay a settlement. Audit item #13.
  • No freight-invoice payment-link exposure. Customer-facing invoice PDFs include a Stripe-hosted payment link, not raw card fields. We never see card numbers. PCI scope is minimized to SAQ-A burden for both HatchOS and the brokerage.
  • Subscription & reconciliation webhooks are separate, both verified. Subscription lifecycle events hit /api/stripe/subscription-webhook with its own STRIPE_SUBSCRIPTION_WEBHOOK_SECRET. A daily reconcile job rolls forward any missed events.
💳
Stripe-hosted, not Stripe-embedded. Card data never enters HatchOS servers. We redirect to Stripe's hosted payment page and accept the redirect back. PCI scope is structurally minimized — auditors confirm, not us.
Section 04

Data protection

Data at rest, data in motion, data being verified. Three different controls — each enforced independently.

  • Postgres over SSL. The connection pool enforces TLS to Neon. Database connections are encrypted in transit — even within Render's private network. Local-development connections localhost skip TLS but production traffic does not. Audit item #18.
  • Encrypted at rest via Neon platform. Postgres data, WAL, backups, and replicas are encrypted at rest by Neon with AES-256. HatchOS does not operate a separate KMS layer — provider-managed encryption is auditable and reflected in Neon's SOC 2 Type II report.
  • Encrypted session store & credentials. Session records live in Postgres with the same SSL transport. OAuth tokens, API credentials, and integration secrets are encrypted with ENCRYPTION_KEY using AES-GCM — process-exits at boot if ENCRYPTION_KEY is missing or set to a known-insecure default. Audit item #19.
  • File uploads → Cloudflare R2. Carrier documents, BOLs, COI packets, customer logos, and generated PDFs flow through services/r2.js. Never committed to source. Endpoints are authenticated; no public listing.
  • USPS address verification over OAuth 2.0. Stop entry on shipments and tender intake verifies addresses via USPS's OAuth-gated address API. We do not keep USPS credentials in source — they live as USPS_CLIENT_ID / USPS_CLIENT_SECRET env vars.
Section 05

Infrastructure hardening

Boot-time guards, no-questions-asked. If a required secret is missing, the process exits before serving the first request.

Control Implementation Source
SESSION_SECRET Boot fails if missing or set to a known-insecure default. Required for cookie signing. Live
ENCRYPTION_KEY Boot fails if missing or set to a known-insecure default. Required for OAuth/credential encryption. Live
STRIPE_WEBHOOK_SECRET Boot fails if missing. Without it, Stripe webhooks would be unverified and forgeable. Live
Cron auth — Bearer tokens only Scheduled jobs (outreach tick, Ava, Quinn, Nia, etc.) read secrets from a Bearer header. No secrets in query parameters. Live
Rate limiting (auth) In-memory sliding-window: 5 login attempts / 15 min, 3 signup attempts / hour, 3 forgot-password / hour, 5 OTP verify attempts / 10 min. Locks logged to security_events. Live
Rate limiting (public) 20 req/min/IP on the public demo surface, 60 req/min/IP on the public demo-click tracker. Live
Upload caps CSV and JSON imports capped at 5 MB via Multer. Only text/csv / application/json MIME types accepted. Carrier & customer import wizards reject everything else. Live
AI employee sandboxing AI employees execute in isolated sandbox providers with allowlist-only environment construction. Production credentials (DATABASE_URL, Render tokens) are not in their env. Live
🧪
Misconfiguration is loud, never silent. The application refuses to start in a known-bad state, and refuses to serve a known-bad request. There is no "best effort" path that accepts unverified data because a secret was forgotten.
Section 06

Audit & observability

If you can't see who did what, you can't answer a security questionnaire. HatchOS writes an immutable audit trail for every privileged action and exposes a health probe you can put in your monitoring stack.

📋

Audit log on every dispatch mutation

Create, status-change, accept, post-Settlement — every dispatch write passes through middleware/audit-logger.js and writes audit_log with user_id, role, action, target, JSONB details, client IP. The rows are append-only.

📡

Health check with DB probe

/health runs SELECT 1 against Postgres. Returns 503 with diagnostic JSON if the connection fails. Suitable for an external uptime monitor — returns within the Render pinger's timeout.

🪪

Startup diagnostics endpoint

Boot-time checks are logged to lib/startup-log with category tags (env.check, db.acquire, env.warn). Every guard failure leaves a structured trace audit-able post-incident.

🛂

Impersonation audit, separated from login events

God Mode login to a tenant posts both a platform_impersonation_log session row (start/end) and a per-request impersonation_audit row (per API hit). The two are immutable and never overlap with regular audit_log.

  • Webhook event lifecycle stored. Every Stripe webhook event is recorded by event ID before downstream effects fire — so an audit can reconstruct exactly what Stripe said, when.
  • Email health event sink (6 pipelines). Postmark open/click/bounce events for transactional, marketing, dunning, activation, nurture, and cold outreach flow through one email_health_events table — easy to correlate a complaint back to the originating send.
Section 07

AI safety

HatchOS ships seven autonomous AI employees (Hatch, Dash, Mark, Lena, Shield, Rex, Knox). They make decisions — but never without guardrails, and never without a human override path. Audit items #26–#32 in Jun–Jul 2026.

💰

Quinn — margin-floor guardrails

Quinn auto-quotes against a tenant-configurable margin floor. Quotes below the floor never auto-send — they escalate to ai_employee_escalations for human review. Audit item #27. Per-tenant daily cap on Quinn auto-sends prevents a runaway batch.

📤

Ava — human-in-the-loop on AP payouts

Ava groups approved bills into a payment run, but only posts after the tenant-approved threshold. Bills above the threshold require explicit user approval. The run is idempotent — re-entrant crons cannot double-post. Audit item #29.

🧠

Tenant-scoped model prompts

Every AI employee prompt includes the active tenant_id in context. Cross-tenant information is structurally absent from the prompt. Tenant isolation is enforced at the LLM-call level, not just at the data layer. Audit item #28.

🔎

Shield — Magica MCP COI extraction

Shield extracts carrier COI fields via Magica vision. Extraction is a suggestion; the ai_employee_escalations row is opened for human review before any dispatch_carriers row is mutated. Audit item #30.

🛡
Escalation queue is the override. Every AI employee can raise an ai_employee_escalation row. Resolved rows are visible to operators; unresolved ones surface in the God Mode dashboard. Killing an autonomous run is a single PATCH.
🔬
Why capital matters to AI safety: we log every AI employee run with token usage + cost (ai_employee_runs.cost_usd, migration 1791800000). Cost anomalies are visible in the analytics dashboard — not just in a vendor invoice.
Section 08

Download & report a vulnerability

Forward this to your IT department. Or, if you found something we missed, tell us.

Download security summary

One-page PDF — the same audit closeout summary above, distilled for a procurement security review. Suitable for IT, compliance, and finance reviewers. No login required.

Download PDF →

Last updated July 2026 · regenerated from the codebase

Report a vulnerability

Found something? Email security@hatchos.com. We acknowledge within 2 business days and do not pursue legal action against good-faith researchers.

Scope: HatchOS production tenants + public APIs, render URLs, and downloadable artifacts.

Out of scope: phishing, social engineering of staff, volumetric denial-of-service, vulnerabilities in third-party platforms we integrate with (report those to the upstream vendor).