Professional Services

A security review answered in an afternoon

An accountancy practice stopped losing three weeks per client to evidence-gathering by making the trail a by-product of the work.

This is an illustrative composite. It is drawn from delivery patterns across our work rather than published as a named client reference, and the figures describe the pattern rather than one audited engagement. We would rather label it than imply a reference we cannot put you in touch with.

Size
72 staff
Platforms
ForgeAtlas

Context

A 72-staff accountancy practice doing outsourced finance for mid-market clients. Each client's annual review, and every new client's onboarding, required an evidence pack: who accessed what, under what authority, with what approval.

The problem

Assembling one pack took about three weeks of a senior manager's time, spread across screenshots, email threads and a spreadsheet of access grants maintained by hand. The spreadsheet was the authoritative record and it was wrong often enough that the practice had stopped fully trusting it.

What we did

  1. 01Stopped treating the evidence pack as a document to be assembled and started treating it as a query over records the system already had.
  2. 02Moved access grants out of the spreadsheet into the database, where a grant is a row with an approver, a reason and an expiry.
  3. 03Made every privileged action write an audit row at the database rather than in application code, so it cannot be bypassed by a new code path.
  4. 04Built the evidence pack as a scoped export: a date range and a client produce the trail, signed and timestamped.
  5. 05Ran the first three packs in parallel with the manual process and reconciled every discrepancy before retiring the spreadsheet.

Architecture

Nothing here is a black box. You can read the architecture before you sign, and you own it after.

  1. 01GrantsAccess as rows with approver, reason and expiry. An expired grant stops working without anyone revoking it.
  2. 02AuditAppend-only audit rows written by database trigger, not by application code — a new code path cannot skip them.
  3. 03IsolationPostgres RLS with deny-by-default policies; cross-client reads return zero rows and are proved to in tests.
  4. 04ExportThe evidence pack is a scoped query over grants and audit rows, timestamped and signed on generation.
  5. 05RetrievalJJ Atlas answers reviewer questions over the practice's own policies, citing the clause it read.

Results

The numbers the work closed against.

target time to answer a 90-question security review
4 hourstarget time to answer a 90-question security review

Basis: Three weeks is the observed manual assembly. Four hours assumes the pack is a scoped query over grants and audit rows already in the database, leaving only review of the generated export.

of privileged actions carrying a database-written audit row
100%of privileged actions carrying a database-written audit row

Basis: A structural constraint: the row is written by trigger, so a new application code path cannot skip it. Coverage is a property of where the write lives, not a rate.

stale access grants a first migration is expected to surface
31stale access grants a first migration is expected to surface

Basis: Modelled from the spreadsheet's observed drift rate against headcount changes over the period it was maintained by hand.

run in parallel with the manual process before it is retired
3 packsrun in parallel with the manual process before it is retired

Basis: A delivery standard, not a forecast: every discrepancy is reconciled before the spreadsheet stops being authoritative.

Stack

  • Next.js
  • Supabase Postgres
  • Row Level Security
  • pgTAP
  • pgvector
  • Vercel

Does this look like your business?

Tell us what is slow and what it is costing. We will point you at the closest precedent we have and say plainly where yours would differ.

Scope it with us