How the embed keeps your members' data safe
Written for the IT and procurement teams who review us. Everything below describes the system as it runs today — including, plainly, what we don't have yet.
Architecture at a glance
The visitor's browser
All mortgage math runs client-side. Loan amounts, incomes, and payments are transmitted only when a visitor chooses to send their scenario to your team.
The embed edge
Each widget load is served with a per-tenant Content-Security-Policy that restricts framing to your approved domains.
Our database
Tenant configuration and opt-in leads live in PostgreSQL behind Row-Level Security; privileged writes go through narrow, server-defined functions.
Payments
Billing is redirect-based Stripe Checkout. Card numbers never transit or rest on our infrastructure.
Domain locking
- Every keyed embed request is validated server-side: the response carries a Content-Security-Policy frame-ancestors directive built from your tenant's approved domains, so browsers refuse to render your widget anywhere else.
- Fail-safe defaults: a tenant with no approved domains configured is refused outright rather than served openly.
- Embed tokens are high-entropy and rotatable from your portal at any time. Rotation immediately invalidates the old token (existing embeds need their snippet updated with the new one).
Tenant isolation
- PostgreSQL Row-Level Security restricts every tenant-scoped table to its owner — cross-tenant reads and writes are denied at the database layer, independent of application code.
- Privileged fields (plan, active status, billing identifiers, ownership) cannot be written directly by users. All legitimate writes flow through SECURITY DEFINER database functions with explicit column allowlists and entitlement checks.
- An adversarial authorization test suite exercises these boundaries — attempts to escalate a plan, flip another tenant's status, or self-promote to admin are tested to fail.
Data minimalism
- Visitor financial inputs are computed in the browser — no passive server-side copy of calculator inputs exists. The only scenario data we store is what a visitor explicitly submits as a lead.
- Widget analytics record only: an anonymous session identifier, the event type, and which calculator. No personal identifiers, no financial inputs, and no IP addresses retained in analytics or associated with calculator activity — this is a documented contract in the ingest endpoint itself. (Our hosting provider keeps standard, short-lived request logs at the infrastructure layer, as disclosed in our privacy policy.)
- Lead capture is strictly opt-in: contact details and a scenario snapshot are stored only when a visitor submits the save form, and the consent statement they saw is recorded with the lead. Lead submissions are validated, rate-limited per tenant, and entitlement-checked server-side.
Payments & webhooks
- Checkout is redirect-based Stripe Checkout — PCI SAQ-A posture; card data is handled entirely by Stripe.
- Stripe webhooks are signature-verified against the raw request body before any processing, and logged for idempotency and failure triage.
- Prices are resolved server-side from the chosen plan and interval. The client never supplies a price or price ID.
Development practices
- Single source-controlled repository; changes land via pull request.
- CI on every push and PR: reproducible install from a committed lockfile, unit tests, a production build, and a dependency audit.
- Server-side input validation throughout — the client is never trusted for entitlements, prices, or tenant identity.
What we don't have yet
Procurement reviews go faster when the gaps are stated up front. Here are ours, with the compensating controls we rely on in the meantime.
No SOC 2 or ISO 27001 certification yet.
Our infrastructure providers (Supabase, Vercel, Stripe) each hold their own independent attestations, and the product collects minimal regulated data by design.
No independent penetration test yet.
Database-enforced isolation, an adversarial authorization test suite in CI, and signature-verified webhooks are the compensating controls. An independent review is on the roadmap, prioritized for customers whose vendor-management requirements mandate one — ask about timing during your review, and we'll share results when available.
Lead submissions are rate-limited and honeypot-protected, but not CAPTCHA-gated.
By design — we don't use visitor IPs for abuse decisions or retain them in analytics. A motivated abuser could submit junk leads up to the hourly cap; per-tenant limits are tunable and leads can be marked as spam.
Reviewing us for your institution?
We'll share the full security & architecture document, walk your IT team through the system, and answer your questionnaire directly.