A plain-English summary of how Stat Notes protects your broadcast data, designed to answer the questions a corporate security team will actually ask. For the full security questionnaire response or a signed NDA + deeper technical detail, email [email protected].
Last updated: April 2026 · Owner: Colin DeFord, Founder · [email protected]
AES-256-GCM, keyed by a separate Key Encryption Key (KEK) held out-of-band
SSO
OIDC and SAML 2.0, per tenant, self-serve. Full protocol-level controls (PKCE, state, nonce, XML signature verification, replay prevention)
Passwords
bcrypt cost 12 — or no password at all (magic-link only); your choice
Tenant isolation
Every DB row tagged with tenant_id. Cross-tenant queries are impossible by design.
Audit log
Append-only. Every login, note send, config change, and role grant is recorded with actor, IP, and user agent.
DDoS & WAF
Cloudflare at the edge on every tenant subdomain
Data residency
United States. Single-region; no cross-border replication.
Sub-processors
Cloudflare (CDN/DNS), Stripe (billing), Hostinger (email/VPS hosting). No analytics, no trackers, no data brokers.
SOC 2 / ISO 27001
Not currently certified. See §12 — we are happy to complete a security questionnaire and sign a DPA.
MFA
Delegated to your Identity Provider via SSO (recommended). Magic-link auth has no password to phish.
2. Multi-tenant isolation
Every customer gets an isolated workspace on their own subdomain: customer.statnotes.app. Tenant isolation is enforced at three layers:
Row-level — every queryable table has a tenant_id foreign key. All queries are scoped to the tenant resolved from the subdomain at the start of the request. Cross-tenant SELECTs are impossible because the query builder never sees a tenant other than the current one.
Session-level — session cookies are host-scoped to the tenant subdomain. A session cookie for acme.statnotes.app cannot be replayed on beta.statnotes.app. On every authenticated request, the server re-verifies that the session's tenant ID matches the resolved subdomain; mismatches are rejected.
Route-level — admin endpoints, user endpoints, and public display endpoints are separated by middleware. Admin endpoints require an admin session; display endpoints accept a signed, per-tenant display token (see §9).
Threat modeled
We explicitly design against "a logged-in Tenant A user tries to read or write Tenant B data." The design answer is: the query layer cannot name Tenant B because the tenant ID is pinned at the middleware boundary, not supplied by the client.
3. Authentication & SSO
Three login paths, admin-configurable
Magic-link — default for new tenants. No passwords stored. A one-time token is emailed, valid for 10 minutes, consumed in a two-step scanner-safe flow that defeats corporate URL pre-fetchers. A 6-digit fallback code is available if the link is mangled in transit.
OIDC SSO — per-tenant self-serve configuration supporting Okta, Microsoft Entra ID, Google Workspace, Auth0, Ping, and any OIDC-compliant IdP.
SAML 2.0 SSO — per-tenant self-serve configuration for IdPs like ADFS, PingFederate, and Shibboleth.
SSO protocol-level controls
Every SSO implementation must pass the following controls. We enforce them in code and test each one with dedicated negative tests.
OIDC (openid-client, OIDC-certified):
PKCE (S256) on every authorize request
Random, single-use, 10-minute state parameter validated on callback
Random nonce echoed in the ID token and validated
Issuer (iss) validation against the configured issuer URL
Audience (aud) validation
ID token signature verification against the IdP JWKS endpoint; JWKS cached with rotation support
exp, iat, nbf enforcement with a 60-second maximum clock skew
Nonce replay cache rejects reused nonces for 10 minutes
HTTPS-only redirect URIs enforced at config save time
SAML 2.0 (@node-saml/node-saml in strict mode):
Signed Response AND signed Assertion both required (wantAuthnResponseSigned and wantAssertionsSigned)
XML Signature Wrapping (XSW) defense — strict assertion-ID binding
SubjectConfirmation validation: Recipient matches our ACS URL, NotOnOrAfter respected, InResponseTo matches the original AuthnRequest ID
AudienceRestriction validated against our SP Entity ID
Assertion-ID replay cache rejects reused IDs for the duration of NotOnOrAfter
Algorithm allowlist: SHA-256 or stronger; SHA-1 and MD5 are rejected
Canonical XML handled by the library
HTTPS-only ACS URL enforced
Defense-in-depth controls beyond the protocol
Email-domain allowlist — even after the IdP successfully authenticates a user, the server independently checks the user's email domain against a tenant-configured allowlist. Prevents "a contractor with a personal Gmail happens to be in the shared Okta" accidents.
No trust in IdP-supplied role claims — JIT-provisioned users are always created at the stat_user role. Privilege escalation via claim manipulation is impossible by design. Admins promote users manually.
Test-before-enable — an admin cannot flip SSO live until they have completed a successful SSO round-trip against their own IdP using their own account. Misconfigurations never ship to end users.
Admin lockout recovery — magic-link authentication is always available to admin users regardless of SSO configuration. An expired IdP certificate or rotated secret can never lock a customer out of their own workspace.
4. Encryption in transit and at rest
In transit
TLS 1.3 only on every edge and origin. TLS 1.0/1.1 are disabled at the CDN and not accepted by the origin.
HSTS with includeSubDomains; no mixed content permitted.
All internal service-to-service hops (CDN → origin, origin → database if remote) are encrypted.
At rest
Secrets — OIDC client secrets, SAML IdP signing certificates, SMTP credentials, Stripe API keys, and session encryption keys are stored AES-256-GCM encrypted using a Key Encryption Key (KEK) held out-of-band from the database.
Passwords (when used) are bcrypt-hashed at cost 12.
Database — SQLite with WAL mode, on an encrypted volume on the host. Nightly snapshots are encrypted before being written to off-host backup storage.
Secrets are held in memory only for the duration of a single request; they are never logged, never echoed to audit entries, and displayed as masked placeholders in the admin UI.
5. Session handling
Cookie flags: HttpOnly, Secure, SameSite=Lax, host-scoped to the tenant subdomain
Session ID is regenerated on every successful login — magic-link, SSO, and bootstrap flows all call req.session.regenerate() before setting the user. Defends against session fixation.
Idle timeout and absolute timeout both enforced by the session middleware
CSRF tokens bound to the session on every state-changing request. Token fetch happens over an authenticated same-origin endpoint; tokens are single-use where practical.
Trust-proxy is locked to loopback and Cloudflare IP ranges. An attacker cannot spoof X-Forwarded-For to bypass IP checks.
6. Network & infrastructure
CDN / WAF / DDoS: Cloudflare in front of every tenant subdomain, free for every customer. Layer-3, -4, and -7 protection included.
Origin: isolated VPS with a locked-down SSH configuration (key-based auth, non-default port, root login disabled in production), reached only via Cloudflare.
Reverse proxy: nginx terminates TLS and proxies to the Node application over loopback.
Content-Security-Policy headers on every response.
No public database: the SQLite file is not network-exposed. The origin is the only service that can read it.
7. Audit logging
Every security-relevant action is recorded to an append-only audit log with the following fields: tenant_id, actor_type (user / admin / super-admin / system), actor_id, event, metadata, ip, user_agent, and timestamp.
Recorded events (non-exhaustive)
Every login attempt, successful or failed, including the reason for failure
Every magic-link issued, every SSO round-trip started, every SSO round-trip completed or failed
Every note sent to a talent monitor, with the note contents and the target display token
Every admin configuration change (SSO, billing, users, roles, display tokens)
Every role grant or revocation
Every billing event and Stripe webhook
Every SSO email-domain rejection
Audit log entries are never modified or deleted in normal operation. They are visible to tenant admins in the admin page for their own tenant only. Exports can be provided to customers on request.
8. Data handling, retention, and deletion
Data residency: all production data is stored in the United States, single region. No cross-border replication.
PII collected: user email, first name, last name (optional), tenant slug, audit log entries (including IP and user agent). Nothing else. No marketing profiles. No device fingerprints.
No analytics, no trackers on any authenticated page. The marketing site is analytics-free as well.
Retention: tenant data is retained for the life of the subscription plus 30 days after cancellation. Audit logs are retained for the full subscription lifetime.
Deletion on request: a tenant admin can request full deletion of their workspace data, processed within 7 business days. Database backups containing the deleted data are rotated out within 30 days.
Data portability: audit logs, notes history, and user lists can be exported as JSON or CSV on request.
No data sales, no third-party sharing beyond the sub-processors listed in §1.
9. Display token security (talent monitors)
Talent monitors are unauthenticated by design — the hardware is often a browser on a set TV with no keyboard, and operators cannot log in mid-broadcast. We secure them with signed display tokens instead:
Signed tokens — each display token is cryptographically signed by the tenant's admin; only the origin can verify it.
Per-token CIDR allowlist — a display token can be locked to a specific IP or CIDR range. Even if the token URL leaks, a request from outside the allowlist is rejected.
Revocable — a compromised or lost token is revoked from the admin page immediately. Revocation is effective within seconds.
Scoped — display tokens can only read the current note feed. They cannot send notes, cannot read history beyond the current note, and cannot reach any admin function.
No cookies — display endpoints are stateless and cookie-free, so a shared/rebooted monitor cannot accidentally carry a previous operator's session.
10. Code quality & vulnerability management
Test-driven development: all security-critical code (auth, SSO, billing, tenant resolution) is written test-first with dedicated negative tests for each threat model case. The current suite is >100 automated tests run on every change.
HTML sanitization: every note passes through a server-side sanitizer before reaching a talent monitor. Scripts, event handlers, and javascript: URLs are stripped. Talent monitors cannot execute arbitrary JavaScript even if an operator pastes it.
Rate limits on login, signup, magic-link, and SSO endpoints; abusive IPs are throttled.
Dependency monitoring: production dependencies are reviewed and updated on a regular cadence. Transitive vulnerabilities flagged by npm audit are triaged; high-severity issues are patched before they reach production.
Canonical libraries: we use the industry-standard implementations for every protocol (openid-client, @node-saml/node-saml, bcrypt, stripe, better-sqlite3) rather than rolling our own.
Secret management: no secrets are checked into source control. Production secrets live in environment variables on the host, encrypted where supported. Rotation is documented and practiced.
Breach notification: in the event of a confirmed breach involving customer data, we will notify affected tenant admins within 72 hours with: what data was affected, the scope, what we've done in response, and what the customer should do. This commitment is reflected in our standard DPA.
Responsible disclosure: we welcome vulnerability reports from security researchers. Please email [email protected] with details; we commit to acknowledging within 48 hours and triaging within 7 business days. No bug bounty at this time.
Status communication: incident status is communicated directly to affected customers via email, not through a public status page (we're too small for one to be honest information).
12. Compliance posture & known gaps
Stat Notes is a deliberately small, focused vendor. We are honest about what we have and what we don't — because we would rather lose a deal than bluff a security questionnaire.
What we have
The full set of technical controls described in this document
Willingness to complete any reasonable security questionnaire (SIG Lite, SIG Core, custom Excel)
A Data Processing Agreement (DPA) ready to sign, aligned with GDPR/UK-GDPR requirements
Full transparency into sub-processors and data flows
What we don't have yet
Known gaps — ask us directly
We are upfront about these because they will come up in procurement. We're happy to discuss mitigations or timelines for any customer who needs them.
SOC 2 Type I / II — not currently certified. On the roadmap once we have committed enterprise customers who require it.
ISO 27001 — not currently certified.
Formal penetration test report — not currently available. We plan to commission one once a first enterprise customer commits.
Bug bounty program — not yet.
24/7 SOC — we are founder-operated. Support and incident response are best-effort during business hours, with escalation paths for active broadcasts.
Multi-region / active-active DR — single region, single origin. Nightly encrypted off-site backups. Recovery Time Objective (RTO) is measured in hours, not minutes.
If any of these are blockers for your procurement team, please reach out — most can be addressed contractually or via compensating controls, and we would rather have the conversation than lose the deal silently.
13. Contact
Security questions, vulnerability reports, DPAs, security questionnaires, or a longer technical conversation:
Email: [email protected] Owner: Colin DeFord, Founder Response target: within 48 hours during business days, faster for active incidents