Skip to main content

What's shipped

Changelog

Every code change to Security Flare lands here. We publish the source of truth from the repository's CHANGELOG.md— no editorial filter, no marketing rewrites. Each entry links to the underlying ADR or pull request so a technical reviewer can verify what changed.

Changelog

All notable changes to Security Flare are recorded here. Format roughly follows Keep a Changelog; the project is pre-1.0 so version numbers track the slice + PR sequence rather than semver.

Unreleased — audit-grade policy templates + brand-aware tables

The 14 master policy templates were thin summaries; an ISO 27001 auditor needs controlled documents. Rebuilt all 14 to a documented authoring standard.

  • Policy authoring standard (docs/policy-authoring-standard.md): every policy now has a document-control header (POL-XX-NNN, version, owner, approver, dates), a fixed section skeleton (Purpose → Scope → Definitions → specific policy statements → Roles → Compliance → Exceptions → References → Version history), and specific testable requirements. Templates grew from ~250 to ~900–1,500 words; the Risk Management Policy now carries the full Clause 6.1.2/6.1.3 methodology — defined likelihood/impact/evaluation scales, risk appetite, treatment→SoA, and residual-risk approval. Still paraphrases (never quotes) ISO, keeps every scoping conditional + merge field.
  • Brand-aware tables: the branded .docx renderer now themes Markdown tables (risk scales, document control, roles, version history) from the customer's brand.primaryColour — a light brand-hued header band with deep brand-hued header text and a hairline border (brandTableTheme()), derived so any palette stays coherent and readable. Plain exports get neutral tables.
  • Template tests strengthened to enforce the standard (document control, required sections, depth floor, unique ids, risk methodology).

Unreleased — production hardening: evidence automation + Object Lock + headers

  • Microsoft Graph evidence auto-pull enabled — Workers Paid confirmed active; provisioned the security-flare-evidence queue + -evidence-dlq dead-letter queue, and enabled the daily [triggers] cron (14:00 UTC) + [[queues.*]] producer/consumer in wrangler.toml. The scheduled() handler now fans one job per (org, control) onto the queue each day; the queue() consumer runs the evidence-pull dispatcher with retries → DLQ.
  • R2 Object Lock live — the three -prod buckets (evidence/policies/exports) now carry a 7-year bucket lock rule, and the Worker binds them (was -dev, no lock). Evidence/policy/export objects are now WORM for 7 years.

Unreleased — production hardening: security headers

Global security response headers on every Worker egress — the app previously sent none.

  • withSecurityHeaders (apps/web/src/lib/security-headers.ts) applied at the single Worker fetch egress in server.ts, so the API, auth, evidence, and SSR document surfaces are all covered: HSTS (1yr), X-Content-Type-Options: nosniff, X-Frame-Options: DENY + CSP frame-ancestors 'none', Referrer-Policy, Permissions-Policy, and a CSP scoped to non-breaking directives (base-uri/object-src/upgrade-insecure-requests) that leaves the SSR/Vite inline script+style model intact. HSTS preload is a deliberate separate opt-in (the apex carries live M365 mail).
  • ADR-0003 corrected: the R2 Object Lock ritual was written against a non-existent API (no --object-lock-* flags on wrangler r2 bucket create; no objectLockMode on the Workers R2Bucket.put() binding). Replaced with the real mechanism — a bucket-level wrangler r2 bucket lock add retention rule that locks every written object automatically, no app-code change.

Unreleased — compliance heat map (PRs #154–#157)

A dashboard heat map showing how close each area is to full control implementation, so you can see at a glance where you're furthest from compliant.

  • By capability (#154): the 93 controls grouped by ISO 27002 operational capability, each tile scored implemented / applicable (N/A excluded; in-progress surfaced), red→amber→green, worst areas first. compliance-heatmap.ts pure engine; GET /api/dashboard/heatmap.
  • Clickable tiles (#155): each tile deep-links to /controls?capability=<area> via a new comma-boundary capability filter on the catalogue.
  • Annex A theme axis (#156): a compact 4-tile top row (Organizational / People / Physical / Technological), each linking to the theme-filtered controls.
  • Trend over time (#157): a daily compliance_snapshots (migration 0025, captured on dashboard view — no cron needed) + an overall-score sparkline with the delta since the first snapshot.

Unreleased — branded, tailored policy generator (PRs #144–#150)

Shipping to production 2026-07-04. An MSP configures its branding once and Security Flare generates the whole ISO 27001 policy set as branded, tailored, auditor-ready .docx documents.

Four phases, each independently shippable (design spec):

  • Phase 1 — branding foundation (#145–#147): brand_profiles table (migration 0022) with MSP-default + per-customer-override resolution; a branded .docx renderer modelled on the reference proposal (dark hero cover with logo + cyan eyebrow + title, PREPARED-FOR block, running header/footer, brand theme, Calibri); merge-field engine ({{org.name}}, {{policy.owner}}, …); /api/brand-profile CRUD + logo upload to R2; a /branding settings page with live cover preview. A policy's Export .docx is now branded + auto-filled.
  • Phase 2 — scoping (#148): policy_scoping_answers (migration 0023) + an 8-question questionnaire and a fail-closed {{#if flag}} … {{/if}} conditional engine, so generated policies only include the clauses that fit the business.
  • Phase 3 — AI tailoring (#149): a "Tailor with AI" action drafts a Claude-tailored policy (prose paraphrased, never ISO verbatim; Australian spelling). The draft always lands needs-review — approval is cleared and a human re-approves; every run writes a policy_ai_generations provenance row (model, prompt version, inputs hash, tokens, actor). Gated on ANTHROPIC_API_KEY — absent, the feature simply hides.
  • Phase 4 — bulk pack (#150): one Generate policy pack (.zip) button on /policies produces the whole set — every one of the 14 templates rendered as a branded, scoped, merged .docx (the org's edited clone wins where it exists), plus a manifest.json, in a single download. The "produce all policies for a business in one click" payoff.
  • Phase 2b — template enrichment (#151): the 14 seed policy templates now carry real {{#if flag}} scoping clauses (M365, remote work, BYOD, cloud, sensitive data, physical office, outsourced IT, software development), so the questionnaire visibly tailors the stock wording. Conditionals resolve at clone time (the editor stays clean) and at pack/export time. CD now runs an idempotent seed-policies step so the reference templates always match the repo; a schema test guards balanced markers + known flag keys.

Unreleased — detail-route fix (PR #138)

Deployed to production 2026-07-03, same day it was found — by an in-browser walkthrough of the live deploy.

Fixed

PR Fix Why it matters
#138 /controls/$code, /policies/$id, /risks/$id were unreachable — the detail files used TanStack Router's flat-file nesting convention under list routes that render no <Outlet/>, so every detail URL silently re-rendered its parent list. Renamed to the trailing-underscore un-nesting convention (controls_.$code.tsx etc.); public paths unchanged Clicking any catalogue row, policy card, or risk row navigated but visibly did nothing — the single most load-bearing click on each register

Unreleased — public surfaces + §4-6 audit foundation (PRs #87–#133)

Deployed to production 2026-07-03 via the new CD pipeline — migrations 0019/0020/0021 applied, Worker live, post-deploy smoke tests green across all 13 public surfaces + 5 export endpoints.

After the operational push, the §4-6 audit-foundation gap closed in one turn: three new clauses-aware registers shipped end-to-end (schema → API → UI → dashboard tile → audit_log trail → Cmd+K discovery → MSP rollup → operator triage signal → XLSX export → ADR → runbook), alongside the public marketing/legal surface getting promoted from "coming soon" to real content across all four /trust /privacy /terms /status pages, plus discovery surfaces (security.txt, robots.txt, sitemap.xml, openapi.json), plus per-page Open Graph + Twitter meta overrides so social-shared links unfurl with the right content, plus a single-click operator audit-handover .zip bundle that wires every register XLSX + a manifest into one download — backed by pure helpers + boundary-pinning tests so the operator zip can't drift from the customer's UI. Thirty-nine PRs landed, 684 tests passing throughout.

Added — public surfaces

PR Surface Why it matters
#87 /changelog route renders the repo's CHANGELOG.md via marked (?raw import, prerendered) Marketing-grade transparency — what a prospect's procurement team looks for before signing
#88 /api-docs public reference for the JSON API (replaces the raw /api/controls link in the footer) Documents the two open endpoints in full + lists the cookie-bound authenticated surface + versioning contract

Added — operator surfaces

PR Surface Why it matters
#89 /admin audit-log gains organisation + action verb filters (server-validated; invalid filter → 400, no silent ignore) Recent Activity used to dump 50 rows globally; filters fix that without rebuilding the surface

Added — §4-6 audit-foundation registers

PR Clause Surface Migration
#90 §6.2 Information security objectives /objectives register with lifecycle: planned → in_progress → achieved / missed / cancelled. Terminal-state reopen returns 409. 0019
#91 §4.2 Interested parties /interested-parties register with the addressed enum mapping directly to §4.2(c): fully / partially / not_yet / not_applicable. Soft-retire preserves audit trail. 0020
#92 §4.3 ISMS scope statement /isms-scope — single record per org with append-only version history. Editing publishes version+1, never mutates in place. 0021

Added — surfacing the new registers

PR Surface Why it matters
#93 Dashboard rollup tiles for objectives, interested parties, ISMS scope Without this, the §4-6 surfaces would be invisible from /dashboard (the primary surface a tenant returns to)
#94 audit_log writes for every §4-6 mutation; verbs humanised in /audit-log and dashboard Recent Activity audit_log is what the ISO certifier inspects to see "who changed what". Status-change PATCHes get lifecycle-specific verbs (e.g. objective.achieved).
#95 Cmd+K palette extended with Objectives (§6.2) and Interested parties (§4.2) groups Discovery — the new registers are now searchable from anywhere via ⌘K

Added — auditor handoff, MSP triage, test coverage, docs

PR Surface Why it matters
#97 XLSX export for §6.2 objectives, §4.2 interested parties, §4.3 ISMS scope (full version history) Auditors expect spreadsheet handoffs; matches existing risks + SoA pattern. Scope export carries every revision, not just current.
#98 Dashboard Getting Started checklist reordered: §4.3 → §4.2 → §6.2 inserted between verify and first-control, each tagged with its clause Auditor walks foundations before controls; the checklist now matches that order. 5 steps → 8 steps.
#99 MSP portfolio §4-6 gap chip per child org with foundationGaps count + tooltip listing missing surfaces Customer at 95% control-readiness without scope/parties/objectives still fails cert; chip surfaces the gap directly.
#100 +38 unit tests for iso-objective-helpers (26) + interested-party-helpers (12) covering the "no reopen" lifecycle invariant, enum rejection, null-clears Locks in the 409 / 400 behaviour the API depends on.
#101 ADR-0016 captures the design decisions; README ISMS-coverage table updated with the new surfaces at the top in auditor walk order Closes the documentation loop — design rationale + surface tally both reflect the new work.
#103 /iso clause-reference page points §4.1 / §4.2 / §4.3 / §6.2 at the new dedicated surfaces (was /documents + /tasks) New customers visiting /iso to map the standard to the product land on the right register instead of the controlled-documents catch-all.
#104 MSP portfolio gains a "§4-6 gaps" filter chip that hides every fully-grounded customer Triage tool — stop scrolling, see only the customers who need foundation help before their audit window.
#105 /api-docs documents the §4-6 endpoints + new Spreadsheet exports (XLSX) section listing all five export.xlsx paths with curl example and response-header reference Customer engineers + auditors who want programmatic spreadsheet retrieval can find it in the docs surface, not just the codebase.
#106 Operator console All Orgs table gains a §4-6 indicator column (S/P/O dots, emerald = present, hollow-red = missing, muted for non-active orgs) Same triage signal MSPs have for their portfolio, but for SF staff across every tenant.
#107 +10 unit tests lock in the /iso clause-map § 4-6 pointers from #103 so a future refactor can't silently revert them 554 → 564 tests.
#108 RUNBOOK §4.1 adds a CSM follow-up cadence (Day 0 / 3 / 14 / 30) for §4-6 foundation work + pointers to the /admin/audit-log filter chips for week-on-week tracking Converts "we have the signal" into "here's the playbook for acting on it".
#110 apps/web/src/lib/audit-log-verbs.ts consolidates the machine-verb → human-phrase map; /audit-log + /dashboard now both import the same helper instead of carrying duplicate (and drifting) copies Closes the drift risk that recently bit the §4-6 verbs ('a party' vs 'an interested party' between the two pages) — single source of truth + 29 new tests covering every lifecycle / enum verb.
#111 /admin Recent Activity panel humanises the verb too, with the raw verb as a small muted suffix so operators filtering still see the literal Operator console reader gets the same readability the tenant gets, without losing the verb-as-literal needed for the adjacent filter bar (#89).
#112 +22 parametrised tests for activity-route.ts covering every entityType the audit_log API currently emits — §4-6 + Graph + Stripe entity types and their aliases Catches the silent regression mode where removing a case from the helper makes activity rows non-clickable for that entity type. 593 → 615 tests.
#114 /trust renders the existing docs/security-whitepaper.md via the /changelog ?raw + marked pattern First page a prospect's procurement team hits — no more "coming soon"; one source of truth between the whitepaper file the README points at and the public URL
#115 /privacy real content sourced from new docs/privacy.md (v1, pending external legal review) covering APP 12/13 commitments, NDB scheme procedure, sub-processor list, Object Lock retention carve-outs Australian SaaS bound by Privacy Act 1988 + the NDB scheme cannot ship a "coming soon" privacy page
#116 /terms real content from new docs/terms.md (v1, pending external legal review) covering NSW law, payment-failure auto-suspend tied to #86, 99.5% availability target + service-credit remedy, Object-Lock 7-year carve-out on close, ACL warranty + 12-month liability cap Procurement matrix needs a real terms-of-service link, not a stub
#117 /status polls /healthz every 30s and shows live green/red badge + ms-latency + "verify yourself" curl command + no-incidents commitment The only one of the four placeholders that genuinely needed runtime; pairs with the SLA in /terms §6
#118 Cleanup: remove unused ComingSoonRoute component (−85 lines) All four pages were the only callers; dead code now
#120 /.well-known/security.txt (RFC 9116), /robots.txt, /sitemap.xml — three Hono-served discovery surfaces with 11 new format-invariant tests Security researchers + procurement teams expect a coordinated-disclosure signal; SEO crawlers need an allow-list + sitemap; deny-list explicitly closes every tenant-bound surface to crawlers as a belt-and-braces alongside the <meta name=robots> in __root.tsx. 615 → 626 tests.
#122 Per-page Open Graph + Twitter overrides for the 6 public pages via new lib/page-meta.ts helper + Route.head() on each route Sharing a link to /privacy in Slack used to unfurl as the marketing landing-page card; now each page gets its own title + description + canonical URL. +10 unit tests for the helper. 626 → 636 tests.
#124 Operator audit-handover .zip bundle at GET /admin/orgs/:id/export.zip — bundles risks + SoA + §6.2 objectives + §4.2 parties + §4.3 scope (full version history) + a manifest.json into one cleanly-named zip; writes org.exported_bundle to the tenant's audit_log Terms §7.1 + Privacy §4 commit to handing the customer their data on account closure. Now a single URL instead of six curl commands.
#125 "Bundle" button in the /admin All Orgs table actions column wires #124 into the UI with per-row spinner + Content-Disposition filename parsing Operator doesn't have to type the URL by hand or copy the org id.
#127 OpenAPI 3.1 spec at /openapi.json (Hono-served, 12-hour edge cache) covering the 13 documented endpoints — catalogue + exports + authenticated registers — with schemas, security schemes, and tag groups Machine-readable companion to /api-docs. Procurement scanners + Postman + Swagger UI + Redoc consume it directly. +22 tests pin the shape so a new endpoint can't silently miss the spec. 636 → 660 tests.
#128 /api-docs header gains a pointer to /openapi.json Developers reading the page can decide between the human-readable docs (this page) and the machine-readable spec without having to guess that one exists.
#130 Operator bundle refactor: extract sanitiseOrgNameForFilename, bundleFilename, buildBundleManifest as exported pure helpers + consolidate canonical ratingFor in lib/risk-export.ts; fixes the score-5 rating drift between /api/risks (low) and the operator bundle (was medium) A customer with a 1×5 risk would have appeared medium in the operator-zipped XLSX and low in their own UI — fixed by consolidation. +24 boundary-pinning tests cover the canonical bucketing + sanitisation edge cases + manifest stability. 660 → 684 tests.
#132 Continuous deployment.github/workflows/deploy.yml: push to main → frozen install → full tests → prod migrations → wrangler deploy → live smoke test (/healthz + /openapi.json + /robots.txt). Skips with a notice until the two repo secrets exist; RUNBOOK §3.0 documents the flow Deploys stop depending on a laptop with fresh wrangler OAuth. The first run applied migrations 0019/0020/0021 and shipped this entire push to production in one pass.
#133 Fix: export routes were shadowed by /:id/api/{risks,objectives,interested-parties}/export.xlsx returned 400 invalid … id for every caller because Hono matches in registration order and GET /:id was registered first. Routes moved above /:id with a ⚠ ROUTE ORDER comment; +10 dispatch-order regression tests Found by the CD pipeline's post-deploy smoke test on day one — the risks Export button had been silently broken since the export first shipped. 684 → 694 tests.

Migration sequence (this push)

0019  iso_objectives           §6.2 objectives register
0020  interested_parties       §4.2 stakeholder register
0021  isms_scope_versions      §4.3 scope, append-only versions

No new secrets, no new bindings. Just pnpm --filter @sf/db migrate against your Neon branch, then redeploy.

v0.3 — operational layer push (PRs #70–#82)

After the register sweep landed, this push hardens the operational layer needed to bill a customer for it. Eight production features + five new ADRs, all shipped autonomously over two days. Frame: Lens 2 in the "how close are we to billing customers" review — see ADR-0013 for the security-review artefact prospects will ask for first.

Added — UX

PR Surface Why it matters
#71 Nav redesign — 4 primary + Registers ▾ + Programme ▾ + user chip + mobile sheet 14 inline links overflowed the bar; UX was unusable on the dashboard
#72 Dashboard register rollup (8 new tiles, attention-coloured) Programme registers were invisible without leaving the dashboard
#73 Org name in the user chip + MSP-gated Portfolio Orientation across multiple customer orgs in the same session
#74 Clickable Recent-activity rows One-click jump from activity to the entity that changed
#75 Cmd+K global search palette across every register Vanta-class navigation; ⌘K + type + ↑↓ + ↵ to land on any control / policy / risk
#76 ? shortcut → keyboard-shortcuts help dialog + dashboard "View all" → /audit-log Discoverability for the palette
#77 MSP Portfolio: sort (worst-first / best-first / A-Z) + status filter chips Triage flow for MSPs juggling many customer orgs

Added — features

PR Surface Why it matters
#79 Microsoft Graph admin-consent flow + /integrations page Unblocks the cron evidence pipeline for real customer tenants — three-check invariant on the callback (HMAC signature, persisted-state match, Entra tenant id match)
#82 Stripe billing MVP — webhook receiver + /billing page + 11 signature-verifier tests Operator-managed billing flow (Stripe-as-source-of-truth pattern); ready to charge a customer today

Added — observability + security

PR Surface Why it matters
#78 ADR-0013withServiceRole BYPASSRLS call-site audit Sales-grade security review; every bypass site documented with trust root + isolation invariant + failure mode
#80 ADR-0014 observability strategy + RUNBOOK §9 (Logpush) + §10 (DR) Three-layer story (L1 Logpush→R2, L2 Analytics Engine, L3 audit_log), DR procedures (Neon PITR, wrangler rollback, quarterly drill)
#81 Per-tenant access log on requireSession / maybeSession Forensic access trail — single JSON line per authenticated request, ready for Logpush ingestion

Added — chores

PR Surface Why it matters
#70 biome.json $schema 2.0.0 → 2.4.15 + ADR-0012 + RUNBOOK refresh Cleared the only lint info; documented why Queues are off (Workers Paid prereq)

Migration sequence

0017  graph_consent_state         (#79)
0018  subscriptions               (#82)

0017 adds three columns to organisations for tracking Microsoft Graph admin-consent state (graph_consent_granted_at, graph_consent_granted_by_user_id, graph_consent_state). 0018 adds five columns + two partial indexes for Stripe subscription mirroring.

Operator setup to use this push

  1. Apply migrations 0017 + 0018 to prod Neon.
  2. Add https://<host>/auth/adminconsent/callback to the Entra app registration's Redirect URIs (Microsoft Graph admin-consent flow).
  3. wrangler secret put STRIPE_WEBHOOK_SECRET and add a Stripe webhook endpoint pointing at https://<host>/auth/stripe/webhook for the six event types listed in ADR-0015.
  4. (Once on Workers Paid) Provision the Logpush job per RUNBOOK §9.1 and the error-rate alert per §9.2.

Test coverage

507 unit tests by the end of the push (up from 422 pre-push). The 85 new tests cover: the Cmd+K search-route mapping (29), the Stripe webhook signature verifier (11), the activity-route helper (29), the dashboard register-rollup (16).

Added — documentation

  • ADR-0012 — Queues + cron bindings disabled until Workers Paid.
  • ADR-0013withServiceRole call-site audit (15 sites in #78 + 3 more in #79 + 3 more in #82 = 21 total at end of push).
  • ADR-0014 — Three-layer observability strategy.
  • ADR-0015 — Stripe billing MVP: Stripe-as-source-of-truth pattern, no Checkout yet, operator-managed onboarding for first cohort.

v0.2 — ISMS register sweep (PRs #31–#45)

The post-Slice-9 build-out of every remaining ISO/IEC 27001:2022 register the ISMS needs for certification. Each register follows the same architecture (tenant table + Drizzle schema + migration + Hono API + pure-helper validators

  • TanStack Start route + 15–30 unit tests pinning the security-critical invariants). See ADR-0011.

Added — features

PR Surface ISO ref
#31 Manual evidence delete (soft-delete, R2-Object-Lock-safe) A.5.36
#32 Recent activity panel on org dashboard A.5.36 / 8.15
#33 Per-control bulk status update (PATCH /api/controls/state/bulk) A.5.36
#34 Per-policy editor refinements (Cmd+S, dirty guard, approval lock, outline, word count) A.5.1
#35 Per-user invitations + /team page A.6.1
#37 Asset inventory A.5.9, A.5.12
#38 Task tracker UI (existing schema, new API + route) (operational layer)
#39 Incident register A.5.24–A.5.27
#40 Supplier register A.5.19–A.5.23
#41 Audits + management reviews §9.2 + §9.3
#42 Findings + corrective action (stacked on #41) §10.1, §10.2
#43 Training register (catalogue + completions, validity auto-compute) A.6.3
#44 Tenant-side /audit-log viewer (filterable, cursor-paginated) A.8.15
#45 Document register (controlled documents, §7.5) §7.5

Added — fixes

  • #36fix(db): grant app_service cross-tenant reads. Repairs the live /api/msp 500 (operator role had no SELECT on organisation_controls) + the silent operator-audit-log gap (app_service had no INSERT on audit_log). DB-only migration 0009, idempotent, no Worker redeploy.

Added — documentation

  • ADR-0010 — Per-user invitations security model: same-tenant only in v1, email-match (not token) binds invites in bootstrap, no silent demotions.
  • ADR-0011 — Register architecture pattern: the standard shape every register PR in this sweep follows (table + helpers + tests + route).

Test coverage

Roughly 250 unit tests across the codebase by the end of the sweep (up from 41 at PR #30). Every register's pure helpers are exercised parametrically — enum whitelists, length caps, UUID guards, status-transition matrices, soft-delete invariants, and audit-trail preservation rules.

Migration sequence

Migrations land in the order PRs merge to slice-5c-3-prerender. Numbers 0008–0016 are reserved by this sweep:

0008  invitations              (#35)
0009  app_service grants       (#36 — DB-only, run on prod first)
0010  assets                   (#37)
0011  incidents                (#39)
0012  suppliers                (#40)
0013  audits                   (#41)
0014  findings                 (#42 — depends on #41 for audit_id FK)
0015  training                 (#43)
0016  documents                (#45)

PR #44 (audit-log viewer) has no schema changes — it reads the existing audit_log table.

Known limitations

  • feat-audit-log-writes is a parallel branch that writes audit_log rows on tenant actions (control state, policy approve, risk, evidence). Today most rows only come from operator activations. The /audit-log viewer fills in immediately once that branch lands.
  • Dashboard rollup cards for the new registers are not yet stitched together — each register page has its own count chip, but /dashboard still surfaces only the original (controls / policies / risks / evidence) summary.

Slices 1–9 (v0.1 baseline pre-sweep)

The original build plan from CLAUDE.md §5, plus operator console + audit log writes. Recorded for completeness; full commit history is on the merged slice branches.

  • Slice 1 — Monorepo + auth + RLS scaffold. Hello-world Entra login, RLS coverage test, CI green.
  • Slice 2 — Core data model. 93 controls seeded from xlsx; all tenant tables RLS-enabled.
  • Slice 3 — Org provisioning with out-of-band verification (ADR-0002).
  • Slice 3Bapp_user NOBYPASSRLS hot-fix (migration 0006).
  • Slice 4 — 10-control Microsoft Graph evidence wedge.
  • Slice 4B — Cron + Queues orchestration for evidence collection (ADR-0009).
  • Slice 5 — Control catalogue UI; per-control state.
  • Slice 5C — TanStack Start SSR foundation (ADR-0006), prerendered public pages (ADR-0008).
  • Slice 6 — Policy library: 14 templates clone + edit + docx export.
  • Slice 7 — Risk register + SoA. Per-risk → Annex A control linkage, xlsx SoA export.
  • Slice 8 — MSP cross-tenant readiness view.
  • Slice 9 — ISMS dashboard at-a-glance summary.
  • Operator console/admin cross-tenant view for SF operators (verify orgs, suspend, audit log). BYPASSRLS bounded to listed call sites.