Last updated 29 June 2026 · Reviewed quarterly · Last internal audit June 2026 (round 2)
This page describes how we look after the data you trust us with — what controls we have in place today, what we're improving, and how to get in touch if you have a security concern or a request about your data.
We believe transparency is a security control in itself. Where we're not yet doing something, we'd rather tell you than hide it.
These are the promises Cambs Fire Safe Ltd makes about how we handle your data. They sit above the specific technical controls listed further down — those change as we improve; these don't.
Our pledge
We will never sell your data
Not to brokers, not to insurers, not to marketers, not to anyone. Your data is used to deliver your fire risk assessment and to run our business — nothing else.
Our pledge
We will never train AI on your data without consent
Our AI providers (Anthropic, Groq) are bound by their commercial terms not to train on customer prompts. We don't opt in to any "improve the model with my data" option on your behalf.
Our pledge
No tracking inside the tools
The signed-in tools — the Hub, your Customer Portal, our on-site Inspection app — run no analytics, no advertising, and no third-party tracking. The only browser storage they use is what's needed to keep you signed in and to hold your in-progress work. Our public marketing site at
robinsfiresolutions.com does use Google Tag Manager to measure which marketing pages bring in real bookings; it's disclosed in our cookies notice and it never follows you into the tools or your data.
Our pledge
Least-privilege access, always
Only the assessor who needs a given customer's data has access to it. Admin operations are limited to a single named admin. Access is reviewed when team members join, change role, or leave.
Our pledge
72-hour breach notification
If we discover a personal data breach likely to affect your rights, we'll notify the ICO within 72 hours and inform you without undue delay — as UK GDPR Article 33–34 requires.
Our pledge
Vulnerability reports answered within 2 working days
If you find a weakness in any of our tools and tell us privately, we'll acknowledge within two working days and keep you updated through the fix. We don't take legal action against good-faith researchers.
Our pledge
Data subject requests answered within 30 days
Access, correction, erasure, portability — handled manually today, within 30 days of a verified email request, usually much faster.
Our pledge
Quarterly internal security audit
Every three months we walk the codebase top-to-bottom looking for fresh weaknesses, regressions, and hygiene gaps. Anything material we find moves onto the "in progress" grid below within the same week.
Our pledge
Honest scope
We're a small UK SMB, not a hyperscaler. We tell you what we do well, what we're improving, and what we don't yet do — without overstating any of it.
Our pledge
Data stays in the UK / EU where possible
Where a subprocessor offers an EU- or UK-region deployment, we'll move to it when reasonable. Cross-border transfers are protected by the UK International Data Transfer Addendum.
Our pledge
10-year retention, then proper deletion
FRA records are kept 10 years from delivery to meet professional indemnity and regulatory needs — after that, either anonymised or fully deleted. Earlier on request, where we don't have a legal obligation to retain.
Our pledge
No silent changes to this page
When a commitment on this page changes, we update the "Last updated" date and — for substantive changes — email portal account holders directly.
Our pledge
We fix what we find before we ship more
Each quarterly audit ships its fixes before we start building new features. The audit-to-fix loop is part of the development rhythm, not a separate "we'll get to it" backlog. The grid below is updated in the same commit as the underlying fix.
Our pledge
Diagnostic endpoints are never anonymous
Anything that probes our internal state — email pipeline status, render-stack version, API key health — sits behind sign-in, no exceptions. We'd rather an honest assessor be inconvenienced than have an anonymous caller fingerprint our infrastructure.
✓ Live
Encryption in transit
All traffic to robinsfirehub.co.uk and our subprocessors is encrypted with TLS 1.2+.
✓ Live
Encryption at rest
Database and file storage encrypted at rest by our infrastructure provider (Supabase).
✓ Live
Two-factor authentication for staff
Mandatory TOTP 2FA for everyone on the assessor / reviewer team. 12-character password minimum.
✓ Live
Webhook signature verification
SmartHub inbound webhooks are validated with a shared secret; mismatched requests are rejected.
✓ Live
Secrets in environment configuration
All API keys, tokens, and credentials live in Railway environment variables — never in source code or shared with the browser.
✓ Live
Parametrised database queries
All Postgres queries go through Supabase's PostgREST layer with proper parameter quoting — protects against SQL injection.
✓ Live
Server-side rate limiting
Cost-sensitive AI and transcription endpoints are rate limited per IP to prevent abuse and surprise bills.
✓ Live
Least-privilege access
Only assessors who need a given customer's data have access. Admin operations are limited to a single named admin.
✓ Live · enforcing since June 2026
API-level authentication gate
Every protected endpoint — staff tools and the on-site Inspection Tool PWA — requires a verified Supabase session token before responding. Validated against Supabase Auth on each request; cached short-window so latency stays sub-50ms. Unauthenticated requests rejected with HTTP 401.
✓ Live
Strong session management
Sessions issued by Supabase Auth (industry-standard JWT). Tokens refreshed automatically; signing out invalidates the session everywhere. Long-lived storage uses the browser's localStorage; no third-party cookies.
✓ Live
Unique-key data integrity
Postgres UNIQUE constraints on idempotency keys prevent duplicate captures during mobile retries. Schema-level enforcement, not just application-layer.
⌛ In progress
Self-service data export & deletion
A "request a copy of my data" and "delete my account" flow inside the Customer Portal. Today these requests are processed manually within 30 days of email.
⌛ In progress
Native iOS app for inspections
Wraps the existing PWA in a native shell. Gives silent camera-roll backup of every site photo plus more durable offline storage.
✓ Live · June 2026 sweep
CORS allow-list
Cross-origin responses now echo a specific Origin (with Vary: Origin) when the caller is in the known allow-list — the Hub, the Customer Portal subdomain, the marketing site, and the native app. Unknown origins still receive a wildcard so legitimate-but-unlisted server-side callers don't break; this is hygiene + future-proofing, since auth-gated endpoints already protect responses via Bearer tokens.
⌛ In progress
CSRF protections on state-changing endpoints
Authenticated endpoints today rely on the Bearer-token convention (tokens aren't auto-sent cross-origin), but we're layering on explicit CSRF protection for any future cookie-authenticated route.
✓ Live · June 2026 sweep
File-upload type and size limits
Multipart upload endpoints now enforce a per-kind size cap (15MB photos, 30MB audio, 300MB video) plus a content-type and file-extension allow-list, with a global deny-list blocking .html, .js, .svg, .exe, and similar regardless of the declared content type. The legacy FRA PDF processor is capped at 50MB so a single oversized blob can't tie up the pdfplumber/pdf2image pipeline.
⌛ In progress
Immutable audit log
Every data change recorded in a tamper-evident log so we can show exactly who did what and when.
✓ Live · June 2026 sweep
Broader rate limiting across the API surface
Per-IP quotas now cover every AI call (Hub Assistant, Flint, client-summary), every PDF render, every file upload, the heavy legacy PDF processor, and the public customer + strategy share links — not just the original four endpoints.
✓ Live · June 2026 sweep
Operational diagnostic endpoints locked down
Every internal "is X working" diagnostic — the AI-key length check, the email-config status, the renderer status, the customer-premises list, the test-email sender — now sits behind the same sign-in gate as every other API endpoint. The renderer probe also no longer scans the filesystem for shared libraries; it just reports the import state.
✓ Live · June 2026 sweep
Static-file allow-list on the application server
The application server now URL-decodes the path before checking, treats the directory deny-list case-insensitively, and collapses repeated slashes — so encoded-dot tricks like /%2e%2e/x, mixed-case probes like /SUPABASE/x.sql, and double-slash variants like /supabase//x all return 404. Internal directories (config, schema, docs, native shell, video renders) are blanket-blocked; only the tool HTML, CSS, JS, images, and fonts are reachable.
✓ Live · June 2026 sweep
Per-user scoping on personal preferences
Every endpoint that writes one staff member's personal data — AI preferences, section vocabulary, saved-clause favourites — now refuses when the body's user email doesn't match the signed-in user. One assessor can no longer poison another assessor's saved AI directives, pollute their favourites, or write to their vocab list, even with a valid session token.
✓ Live · June 2026 sweep
SSRF defence on the PDF renderer
When the server builds a report PDF it has to fetch every embedded photo over HTTP. We now refuse any fetch URL that resolves to a non-public address — loopback, link-local, private RFC1918 ranges, the cloud-provider metadata service. Without this guard, a signed-in user could in theory point a finding photo at an internal URL and recover the response from the rendered PDF.
✓ Live · June 2026 sweep
Project-ownership checks on video studio
The Content Studio (our in-house short-form video tool) now verifies that the signed-in user owns a project before letting them open it, save changes, delete it, or mint a download URL for its media. Previously another teammate's project id was the only thing standing between you and their in-progress work.
✓ Live · June 2026 sweep
Browser hardening headers
Every response (HTML, API, errors) now carries X-Content-Type-Options: nosniff, X-Frame-Options: DENY, and Referrer-Policy: same-origin. Cheap defence-in-depth: stops MIME-sniffing attacks, blocks the tools being framed inside a third-party site, and keeps internal Hub URLs from leaking to outbound links.
✓ Live · June 2026 sweep
Cap on the legacy PDF processor
The 50MB upload cap on the FastField PDF processor already protected against the obvious file-size DoS. We've added a hard 250-page cap on top — an attacker packing 5,000 trivial pages into a 50MB file can no longer tie up the renderer for minutes per request. Real FRA PDFs top out around 100 pages.
✓ Live · June 2026 sweep
Dependency version floors
Production dependencies that ship into the PDF pipeline (Jinja2, Pillow, Bleach) now carry minimum-version floors that pull in the security patches published since the original install. Future Railway builds will pick up the fixed releases instead of whatever happened to be cached.
✓ Live · June 2026 sweep
Outbound TLS certificate verification
Every server-to-server call from the Hub — to Supabase, Anthropic, Groq, SmartHub, and Resend — performs full hostname and certificate-chain verification against an explicit CA bundle (certifi). A misissued or stolen certificate, or a hijacked DNS response pointing us at a proxy, is caught and the request rejected. There is no emergency-revert override — verification is always on.
✓ Live · June 2026 sweep
Path-traversal guard on uploaded media
Inspection photo/audio uploads and Content Studio asset uploads file into folders keyed by inspection or project id. Those ids now have to look like ids — strictly alphanumeric, hyphen, underscore, dot, no slashes, no .., no smuggled path segments. A malicious upload can't smuggle its file into another folder. Content Studio uploads additionally re-check project ownership before writing.
✓ Live · June 2026 sweep
Hard daily ceiling on AI proxy spend
The Hub's AI proxy (which forwards staff and customer-portal AI calls to Anthropic) now carries a per-IP daily ceiling on top of the existing per-minute burst limit, plus a hard cap on the response size a single call can request. The proxy also refuses oversized prompt bodies. Without these, a runaway client or a single rogue IP could quietly drain the AI budget overnight.
⌛ Identified · June 2026 audit (round 2)
Webhook replay protection
Our inbound webhook secret check is constant-time and tight, but it doesn't yet stop someone who captured one valid request from replaying it. A timestamp + freshness window on every inbound webhook is the next layer — once SmartHub starts sending one, we'll start enforcing it.
✓ Live · June 2026 sweep
Content Security Policy
Every staff tool and our legal / trust pages now tell the browser exactly which domains it's allowed to load scripts, styles, fonts, images, and audio from. A stored-content injection that managed to land a <script src="https://attacker.example/x.js"> tag in any of our tools would be blocked by the browser before it ever ran. Pages can also no longer be embedded inside a third-party iframe, and their <base> tag can't be hijacked to redirect every relative link on the page.
✓ Live · June 2026 sweep
Server-side sanitisation on rich-text fields
Finding descriptions, action plans, relevant-information blocks, and every text-bearing block in the Fire Strategy Builder now pass through a second bleach sanitisation pass on the server before being persisted — independent of the Notes Tool editor's browser-side sanitiser. The safety boundary now holds even if the front-end is bypassed via direct database access or a future API client.
✓ Live · June 2026 sweep
PII masking in operational logs
Customer email addresses, phone numbers, and full residential addresses are now masked before being written to Railway's application logs. Logs still carry enough breadcrumb — opportunity / appointment / property keys — to investigate any individual job, but the readable PII stays in the database only and never leaks to anyone who later gains read access to the log stream.
✓ Live · June 2026 sweep
Legacy Gmail poller removed entirely
The Gmail poller that historically watched for FastField "inspection complete" + workflow notifications has been retired. The on-site Inspection Tool drives every stage transition directly through authenticated API calls now, so the Hub no longer logs into a Gmail inbox, no longer parses email subject lines to mutate job state, and no longer needs the inbox credentials to be configured. One whole attack surface — credentials in storage, subject-injection risk, IMAP login on every poll cycle — gone.
✓ Live · June 2026 sweep
Concurrency cap on PDF rendering
PDF renders (the FRA report, the Client Summary, the Fire Strategy, the Cinder gap-check) now share a server-wide concurrency cap of three. A single user firing many concurrent render requests can no longer exhaust the Railway dyno's memory budget — additional requests queue cleanly instead.
⌛ Identified · June 2026 audit (round 2)
Magic-byte verification on uploaded files
Photo and audio uploads are currently checked by file extension and the browser-declared content-type. We're adding a check against the actual file's first few bytes so a renamed executable can't slip through as a JPEG.
✓ Live · June 2026 sweep
Per-email abuse caps on the public gap-check
The free fire-safety gap-check on our marketing site was already rate-limited by IP. We now also throttle by email address — three reports per five minutes and ten per day for any single email — so an attacker rotating through residential IPs can't repeatedly mint PDFs and CRM contacts against the same address. A lightweight verification widget on the form itself is a future follow-up that needs a coordinated change with the marketing site.
✓ Live · June 2026 sweep
Timing-safe webhook verification
Inbound webhook signature checks now use a constant-time comparison (hmac.compare_digest), so timing differences in the secret comparison can't be exploited as a side-channel.
✓ Live · June 2026 sweep
Generic error responses
Every server-side crash now returns a generic "internal_error" response with a short reference id; the full Python traceback stays in our private Railway logs. Postgres schema text, file paths, and library internals no longer leak to the client.
✓ Live · June 2026 sweep
Shorter media signed-URL lifetime
Default lifetime for media signed URLs (inspection photos / voice notes / videos, content studio assets) reduced from 1 hour to 15 minutes — long enough for any real review session, short enough that an intercepted URL has a narrow window. The customer-facing Cinder gap-check report retains its intentional 7-day link so the customer can come back to it.
✓ Live · June 2026 sweep
Sign-out clears the offline cache
When an assessor signs out of the Inspection Tool PWA — or any other staff tool — we now wipe every browser-side artefact that holds customer data: the cached job list, the cached prep briefings, the in-progress inspection state, and the offline upload queue carrying any photos or audio captured before signal returned. If the same tablet is later picked up by a colleague (or anyone else), there's nothing in the browser to read back. Sign-in config and the Supabase session itself are revoked separately by the auth provider.
✓ Live · June 2026 sweep
Auth-by-default at the code level
Every protected endpoint requires a verified session both in production and at the code level — the default is "fail closed". A misconfigured deploy, a fresh environment, or a future engineer forgetting to set the auth flag all land on the same safe answer: refuse the request rather than serve it.
✓ Live · June 2026 sweep
Sanitisation of AI-generated content in PDFs
AI-generated narrative fields (the Client Summary narrative and the Executive Summary body) now pass through a bleach-based sanitiser before being rendered. The allow-list keeps every formatting tag the assessor or the model would legitimately emit (paragraphs, emphasis, lists, in-text links) and strips anything else — script, style, iframe, onerror, javascript: URLs. Same text is now safe to preview in the browser too.
We use a small number of trusted subprocessors to run the service. Each one is bound by contract to use your data only on our instructions.
We deliberately build on infrastructure providers that carry the certifications a small SMB couldn't realistically maintain in-house. That doesn't make us SOC 2 certified, but it does mean the layers our data sits on top of are independently audited.
Our own infrastructure isn't currently certified — we're a small team and the cost-to-customer of a formal audit doesn't pay off at our scale yet. The day we're large enough that it matters to a buyer, we'll engage an auditor. In the meantime we're glad to share evidence of any of the controls listed on this page, on request.
From a single FRA engagement, here's where the data flows and when each piece is removed.
We log enough to investigate an incident properly and we don't log so much that the logs themselves become a liability.
Honest realism: we're a small operator, not a major SaaS. The realistic continuity plan looks like this.
If you're considering white-labelling the platform or assessing it under a DPIA, supplier review, or other diligence process, please reach out and we'll happily walk you through:
We treat security diligence as a normal part of the sales conversation, not a hurdle. The earlier you raise concerns the easier they are to address.
If you've found a security weakness in any of our tools, please tell us privately rather than disclosing publicly. Email marco.fiore@cambsfiresafe.com with as much detail as you can share (URL, steps to reproduce, screenshots). We'll acknowledge within two working days and keep you updated as we fix it. We don't currently run a paid bounty programme but we'll happily credit you publicly if you'd like.
If we discover a personal data breach that's likely to result in a risk to your rights and freedoms, we'll notify the Information Commissioner's Office within 72 hours and notify affected individuals without undue delay, as required by UK GDPR Article 33–34.
The status pills above describe the actual state of the system, not aspirations. When we add a control, we move its pill to ✓ Live and update the last-reviewed date at the top. When we discover a gap, we add an ⌛ In progress pill so you know about it.
If you ever read something here that doesn't seem to match your experience, please flag it.