Skip to main content

Trust & security

How Security Flare handles your data

Written for a prospect's security or risk officer. Every claim links to the underlying code or architecture decision so a technical reviewer can verify. The source of truth lives at docs/security-whitepaper.md in the public repository.

Security Flare — security whitepaper

Audience: a prospect's security or risk officer evaluating whether to trust Security Flare with their ISO/IEC 27001 evidence. This document is written for non-engineers. Every claim links to the underlying code or architecture decision so a technical reviewer can verify.

TL;DR:

  • Customer data lives in Sydney (ap-southeast-2) under Cloudflare Data Localisation Suite, never leaves the region.
  • Every record is row-level-security isolated per organisation in Postgres. The exceptions are catalogued, audited, and bounded.
  • Microsoft authentication; Security Flare never stores customer passwords.
  • Evidence files land in Cloudflare R2 with Object Lock in compliance mode and 7-year retention. Nobody — including Security Flare staff — can delete them during the lock period.
  • Forensic Worker logs ship to R2 with the same retention. Customer audits get the full access trail on demand.
  • The full architecture is public in the ADRs at docs/adr/.

1. What does Security Flare do, and what data does it touch?

Security Flare is a multi-tenant SaaS that helps Australian Microsoft-centric MSPs and their customers reach and maintain ISO/IEC 27001:2022 certification. The first module is an ISMS readiness tracker.

To do that, Security Flare reads (never writes) from your Microsoft 365 tenant via Microsoft Graph: Conditional Access policies, Intune device inventory, audit logs, security events, sensitivity labels. Those map to ISO 27001 Annex A controls.

You can also upload evidence files (screenshots, PDFs, exports) manually. Files go to Cloudflare R2 under evidence/{org_id}/{control_id}/{evidence_id}/{sha256}.{ext}. The SHA-256 is recorded so any post-upload tampering is detectable.

We don't read your email, files, OneDrive, SharePoint, or Teams messages. The Graph permission scopes we request are listed in §3 below.


2. Tenant isolation — how is my data kept separate from another

customer's?

Two layers, defence in depth.

2.1 Postgres Row Level Security

Every tenant-scoped table has Postgres row-level security enabled with the policy:

USING (organisation_id = current_setting('app.org_id', true)::uuid)

The application sets SET LOCAL app.org_id = $1 at the start of every authenticated transaction. Postgres physically refuses to return rows from a different tenant — the database is the boundary, not the application code. Even a bug in the application can't cross tenants because RLS is the floor.

CI fails if a new table lands without RLS — the static check is at packages/db/tests/rls-coverage.test.ts.

2.2 BYPASSRLS sites are audited

Four classes of operation legitimately need to step outside RLS:

Class Example Trust root
First-sign-in bootstrap Find-or-create an org on a brand-new Entra tenant Microsoft-signed OIDC token (verified)
Operator console List all customer orgs from /admin ADMIN_TOKEN bearer, constant-time compare
MSP cross-tenant rollup Aggregate readiness across an MSP's child orgs Session + an RLS-pre-filtered whitelist
Cron / system Daily evidence collection per customer Cloudflare scheduled handler (no HTTP path in)

Each individual site is documented in ADR-0013 — 21 call sites audited as of end of May 2026. The standing rule: any new bypass site must be added to the audit + bounded by a specific id, specific token, or RLS-pre-filtered whitelist. The reviewer's question is always "show me where this bypass is bounded" — that boundary is made explicit in code and doc.

2.3 Multi-tenant model: MSP + customer orgs

An MSP organisation can parent customer organisations. The organisations RLS policy allows the MSP to see their own row plus any child orgs (parent_msp_id = app.org_id) — Postgres enforces it, not application code. Customer orgs see only themselves.


3. Authentication — how do users sign in?

Microsoft Entra ID OIDC. Security Flare is a multi-tenant Entra app. Customer users sign in with their Microsoft work account; the id_token issuer, audience, nonce, and signature are validated against the Microsoft JWKS cached in Cloudflare KV.

Security Flare never sees, stores, or proxies customer passwords.

3.1 Out-of-band verification for new tenants

The very first user from a new Entra tenant lands in org.status = 'pending' with a one-shot verification code. A Security Flare operator phone-verifies the customer against their company's publicly-listed ABN/ASIC contact details before flipping the org to active. Until then the customer can browse the catalogue but can't write — see ADR-0002.

This prevents domain-spoofing — an attacker who registers an Entra tenant on a look-alike domain can't auto-admit themselves to an existing org.

3.2 Sessions

Sessions live in Cloudflare Durable Objects, not JWTs. Specifically:

  • One Durable Object per organisation. Holds the live session set.
  • The browser cookie carries only the session id (256-bit random).
  • HMAC-signed cookie. Cookie name is __Host-sf-session, prefixed for the strictest host-binding. Cookie flags: Secure, HttpOnly, SameSite=Lax, Path=/. No Domain attribute (which would weaken the host binding).
  • Session TTL: 8 hours.
  • Instant revocation: delete the session in the DO; the next request 401s. We don't have to wait for a JWT to expire.

3.3 Microsoft Graph application tokens

The cron evidence pipeline uses Microsoft Graph application permissions (not delegated). Customers grant admin consent via /integrations ([ADR-0009 + #79]); tokens cache in Cloudflare KV encrypted with an AES-GCM key stored in Cloudflare Secrets Store. Tokens are keyed by Entra tenant id — a token for tenant A can't be used against tenant B because the cache lookup is itself tenant-scoped.

The Graph scopes Security Flare requests (read-only):

  • Policy.Read.All — Conditional Access + authentication policies
  • Reports.Read.All — sign-in + audit + service reports
  • Directory.Read.All — users, groups, app registrations
  • SecurityEvents.Read.All — Microsoft 365 Defender alerts
  • IdentityRiskEvent.Read.All — Identity Protection risk events
  • AuditLog.Read.All — Entra audit + directory activity
  • DeviceManagementConfiguration.Read.All — Intune config
  • DeviceManagementManagedDevices.Read.All — Intune device inventory
  • RoleManagement.Read.Directory — privileged role assignments
  • InformationProtectionPolicy.Read — sensitivity labels

No write scopes. No Mail, Calendar, OneDrive, SharePoint, or Teams content scopes.


4. Evidence integrity

Evidence files land in Cloudflare R2 with Object Lock in compliance mode and 7-year retention on the evidence/, policies/, and exports/ prefixes per CLAUDE.md §3.4 and ADR-0003.

What this means in practice:

  • Once a file lands, it cannot be deleted or overwritten before the retention period expires — not by an attacker, not by Security Flare staff, not by a compromised root account.
  • Each file's SHA-256 fingerprint is recorded in the evidence table at upload time. Post-upload tampering is detectable on the next read.
  • Every evidence-collection run from the Graph pipeline writes a row to evidence_runs recording: tenant, control, Graph endpoint, response status, output hash, R2 key, timestamp. The run record itself is an audit artefact.

5. Audit trail

Three layers, each with a different durability profile (see ADR-0014):

Layer 1: forensic Worker logs

Every authenticated request emits a structured JSON line capturing {org_id, user_id, user_role, method, path}. Cloudflare Logpush ships these to R2 with Object Lock + 7-year retention — same durability as evidence files.

When you ask for an access report for a customer audit, this is what gets surfaced. The data is structured so a 5-minute rclone + jq drill answers "what did Alex at Acme do at 14:32 on 2026-05-30?"

Layer 2: operational metrics

Cloudflare Workers Analytics Engine — request count, p95 latency, error rate per route. Standard SaaS observability, 30-day retention. We use this for on-call paging, not customer audits.

Layer 3: tenant audit log

The audit_log Postgres table records every tenant mutation in plain English:

  • Control status changes: control.status_changed, control.bulk_status_changed
  • Risk register: risk.created, risk.updated, risk.closed
  • Policies: policy.cloned, policy.updated, policy.approved
  • Evidence: evidence.uploaded, evidence.downloaded, evidence.deleted
  • Org lifecycle: org.activated, org.suspended, org.unsuspended
  • Integrations: graph.admin_consent_granted
  • Billing: billing.customer_linked, billing.subscription_created, etc.

Customers see their own org's rows at /audit-log. Security Flare operators see across all orgs (BYPASSRLS-bounded) at /admin/audit-log. Both views are filterable + cursor-paginated.


6. Australian data residency

  • Database: Neon Postgres, region pinned to ap-southeast-2 (Sydney). All queries and writes happen within Australia.
  • Object storage: Cloudflare R2 with jurisdictional restriction set to AU. Buckets refuse cross-region replication that would leave Australia.
  • Worker compute: Cloudflare's Australian edge locations. The Worker code itself runs on Cloudflare's global edge for low latency, but the regulated data never leaves the Sydney data plane.
  • Cloudflare Data Localisation Suite: enabled. Cache keys, logs, rate-limiter state — all bound to the AU region.
  • Privacy Act 1988 + OAIC Notifiable Data Breaches scheme: 30-day breach notification window built into the operational procedures (see docs/operations/RUNBOOK.md §8).

7. Operational hygiene

7.1 Secrets

Every credential lives in Cloudflare Secrets Store, never in environment variables, never in KV, never in the database. This includes:

  • Postgres connection string
  • Cookie HMAC signing key
  • Microsoft Entra app client secret
  • Microsoft Graph token encryption key
  • Stripe webhook signing secret
  • Operator-only admin token

Secrets rotation is a wrangler secret put command — no redeploy required; the Worker reads the new value at the next request.

7.2 Disaster recovery

Documented in docs/operations/RUNBOOK.md §10:

  • Neon point-in-time restore: 7-day window on the free tier, 30 days on paid. Procedure: spin up a branch from the target timestamp, validate, swap the DATABASE_URL secret.
  • Worker rollback: wrangler rollback <version-id> is instant and DB-state-agnostic.
  • Migration rollback: forward-only — write an undo migration. Catastrophic cases fall back to point-in-time restore.
  • Quarterly DR drill: recommended cadence to validate the above.

7.3 Code review + CI

Every code change passes through:

  • TypeScript strict mode + lint (Biome)
  • Unit tests (507 as of end of May 2026)
  • RLS coverage check — CI fails if a new tenant table lands without RLS
  • OpenAI code review on PR

The repository is small, contained, and reviewable: one Worker, one Postgres database, one R2 namespace.


8. Pen testing / customer security review

We support customer-driven pen tests against a staging environment. Contact hello@securityflare.com.au to scope and schedule.

For document review:

  • Architecture decisions — read docs/adr/ in order. ADRs 001 through 015 cover the full design space.
  • Operations — read docs/operations/RUNBOOK.md for deploy, observability, recovery procedures.
  • BYPASSRLS auditADR-0013 walks every place we step outside row-level security and pins the isolation invariant.
  • Repository hard rulesCLAUDE.md §3 lists the non-negotiable invariants every change in the codebase must satisfy.

9. Compliance certifications

Security Flare is currently pre-certification — we are dogfooding our own product to walk the ISO 27001:2022 path. Target: certification within 12 months of GA. Until then we publish:

  • The full architecture (ADRs).
  • The full runbook (operations docs).
  • Customer access logs on request.
  • This whitepaper, updated as the system evolves.

The data-handling guarantees in this document are enforced by code and infrastructure, not by certificate. Verify them by reading the linked ADRs.


Appendix A — ADR index

ADR Topic
0001 TanStack Start as the SSR layer
0002 Out-of-band verification for new tenants
0003 R2 Object Lock + 7-year retention on evidence
0004 SPA mode deferral
0005 Neon WebSocket vs Hyperdrive
0006 TanStack Start foundation
0008 Prerender /trust /privacy /terms /status
0009 Cron + Queues + Workflows orchestration
0010 Per-user invitations security model
0011 Register architecture pattern
0012 Queues disabled until Workers Paid
0013 withServiceRole BYPASSRLS audit
0014 Observability strategy
0015 Stripe billing MVP