Most security pages are a wall of adjectives. This one is organised around a single rule: every protection described here is implemented in code, and the page that describes it fails our build if that code is ever removed. Where we have not done something, it is listed under what we don’t claim rather than left out.
The short version
- One account can never read another. Isolation is enforced by the database itself, so a bug in our application code still cannot leak your records.
- We never see your password and never hold a card number.
- Your figures cannot be quietly changed. Totals are recomputed on our server, every update is applied once, and your export carries a checksum anyone can verify.
- We do not sell your data, do not share it for advertising, and do not train AI models on it.
- Your customers’ records are yours. Export them whenever you like; ask for deletion and it happens.
1. Your account cannot see another account
Every table enforces tenant isolation in the database itself, not just in application code.
Row-level security on every table
Isolation is enforced by Postgres row-level security on all 40+ application tables, so a query that forgot its tenant filter returns nothing rather than someone else's records. There is not one permissive catch-all policy anywhere in the schema.
Implemented insupabase/migrations/013_stage2_security.sqlsupabase/migrations/042_crm_connectors.sql
Every API route is scoped, and a test proves it
Each route authenticates, checks the caller's role, rate-limits, validates its input against a schema, and scopes every query to the caller's workspace. That sequence is enforced statically over the whole API surface — a new route that skips a step fails the build, and each deliberate exception has to be named in code.
Implemented insrc/lib/security/route-contract.tssrc/lib/security/__tests__/route-contract.test.ts
Role-based access inside your account
Owners, managers, sales reps and accounting each reach only the screens and data their role needs. A sales rep sees their own pipeline, not the company's books.
Implemented insrc/lib/auth/roles.ts
2. Passwords, keys and tokens
We never see your password, never store a card number, and encrypt third-party tokens before they touch the database.
We never see your password
Authentication is handled by our identity provider; passwords are hashed there and never reach our application code or our logs. Password reset is theirs too, which means there is no reset flow of our own to attack.
Implemented insrc/lib/auth/verified-user.ts
Card numbers never reach us
Payment details go directly from your browser to Stripe. We receive a subscription status and nothing that could be used to charge a card.
Implemented insrc/lib/billing/stripe.ts
Connected-CRM tokens are encrypted at rest
The access tokens that let BookRoofs talk to your CRM are encrypted with AES-256-GCM before being written to the database, using a key held only in the server environment. A copy of the database alone does not yield a working token.
Implemented insrc/lib/crypto/tokens.tsscripts/test-encryption.js
Secrets cannot ship to the browser
Every production build is scanned for server-side secrets in the client bundle, and the build fails if one is found. This is the mistake that leaks a database key, so it is checked by a machine rather than by review.
Implemented inscripts/check-bundle-secrets.mjs
3. In transit, and in the browser
HTTPS everywhere, a strict content-security policy, and signed webhooks so nothing outside can write into your account.
HTTPS enforced, for two years
All traffic is TLS. The app sends HSTS with a two-year lifetime, so a browser that has visited once refuses to connect over plain HTTP afterwards.
Implemented insrc/lib/security/headers.ts
A real content-security policy
Scripts, styles, fonts, media and network calls are restricted to named origins, the page cannot be framed by anyone, and injected forms have nowhere to post. The policy is a single tested function rather than a string copied between files.
Implemented insrc/lib/security/headers.tssrc/lib/security/__tests__/headers.test.ts
Signed, verified webhooks
Inbound webhooks are verified by signature with a constant-time comparison before anything is written. An outside party who knows the URL still cannot put data into your account.
Implemented insrc/lib/crm/webhooks/verify.tssrc/lib/crm/__tests__/webhooks.test.ts
Customer document links are capability URLs
An estimate or invoice you send is reachable at a 192-bit random link, unguessable in practice, excluded from search engines, and carrying only that one document. Your customer reads it without needing an account.
Implemented insrc/app/estimate/[token]/page.tsxsrc/app/invoice/[token]/page.tsx
4. Your numbers stay the numbers you entered
Money is recomputed on the server, every event is applied once, and your data export carries a checksum.
Totals are recomputed on the server
When an estimate is saved, the subtotal, tax and total are calculated again on our server from the line items, by tested code. A total sent from a browser is never trusted, so an edited request cannot change what your customer is quoted.
Implemented insrc/app/api/rep/estimate/route.tssrc/lib/sales-math.tssrc/lib/__tests__/sales-math.test.ts
Every change is applied exactly once
Updates from your CRM and from Stripe are stored against their event id with a database uniqueness rule, so a delivery that arrives twice cannot double a job or a payment. Out-of-date and replayed events are refused.
Implemented insupabase/migrations/042_crm_connectors.sqlsupabase/migrations/037_billing.sqlsrc/lib/crm/webhooks/verify.ts
An accepted estimate stays accepted
Acceptance is a one-way change guarded in the database update itself, so two clicks or two devices at once cannot accept an estimate twice or rewrite who accepted it and when.
Implemented insrc/app/api/estimate/accept/route.ts
Your export proves it is complete and unedited
The data export includes a SHA-256 checksum of its contents and how to check it. Anyone you hand the file to can confirm, without trusting us, that no row was changed or removed after it left BookRoofs.
Implemented insrc/lib/account/export-integrity.tssrc/lib/account/__tests__/export-integrity.test.tssrc/app/api/account/export/route.ts
5. A record of who did what
Security-sensitive actions are written to an append-only audit trail, and abuse is rate-limited.
Append-only audit trail
Connecting or disconnecting a CRM, minting an API key, inviting or revoking a team member, importing leads and accepting an estimate all write an audit row with the actor, the workspace and the time. Nobody — including us — can edit or delete those rows through the application.
Implemented insrc/lib/audit.tssupabase/migrations/049_audit_logs.sql
Rate limiting on every mutation
Write endpoints are rate-limited per account, and public endpoints a stranger can reach, such as estimate acceptance, are limited per IP address. Sign-in attempts are throttled separately by our identity provider, which is the system that actually sees them.
Implemented insrc/lib/rate-limit.tssrc/lib/__tests__/rate-limit-pipeline.test.ts
A published way to report a vulnerability
There is a machine-readable security contact at /.well-known/security.txt. If you find something, there is a documented place to send it and you will get an answer.
Implemented insrc/app/.well-known/security.txt/route.ts
6. What we do with your data — and what we don't
We do not sell it, do not share it for advertising, and do not train models on it.
Never sold, never shared for advertising
We do not sell personal information and we do not disclose it for cross-context behavioural advertising. There is no revenue line that depends on your data leaving the product.
Implemented insrc/app/privacy/page.tsx
Your data is not training data
Neither your business data nor your customers' records are used to train machine-learning models. The AI Caller sends only what it needs to conduct the call in front of it.
Implemented insrc/app/privacy/page.tsx
You are the owner, we are the processor
The homeowner records in your account are yours. We hold them on your instructions, and "you can export them at any time" means a button in Settings, not a support ticket — one file with every lead, deal, inspection, job financial and commission, plus a manifest naming what it contains and what it leaves out. Deletion on request is part of the contract rather than a favour.
Implemented insrc/app/api/account/export/route.tssrc/lib/account/export-manifest.tssrc/app/terms/page.tsx
Every subprocessor is named
The privacy policy lists each company that touches customer data and what it does, rather than hiding behind "our service providers". You cannot assess a supply chain you are not allowed to see.
Implemented insrc/lib/legal/company.ts
What we don’t claim
This is the section other vendors leave out, and the reason you can trust the rest of the page. Everything below is something we are not doing, stated before you have to ask.
| We do not claim | What is actually true |
|---|---|
| SOC 2 or ISO 27001 certified | We are not, and we will not imply it with a badge. Those are audits of a company, they take months and cost real money, and we would rather tell you the truth now than show you a logo you cannot verify. The controls on this page are real and you can ask us about any of them; the independent attestation is not there yet. |
| HIPAA or PCI compliant | Neither applies to roofing work, and claiming them would be noise. Card data is handled entirely by Stripe, who are PCI compliant, which is why we never hold a card number ourselves. |
| Penetration tested by a third party | Not yet. What exists today is a static security contract over every API route, an automated secret scan on every build, and a test suite that is a hard release gate. That is good engineering discipline; it is not the same as an outside firm trying to break in, and we will not describe it as though it were. |
| A published uptime SLA | We run on Vercel and Supabase, both of which publish their own status, but we do not yet offer a contractual uptime figure of our own. If your contract needs one, ask and we will talk about it rather than quietly printing a number nobody measures. |
| End-to-end encrypted | Your data is encrypted in transit and at rest, which is not the same thing. We can read your records, because features like estimates, the Advisor and the AI Caller require the server to process them. Any vendor offering both end-to-end encryption and server-side intelligence is describing one of the two inaccurately. |
Who else touches your data
You cannot assess a supply chain you are not allowed to see, so every company that processes customer data on our behalf is named — not described as “our service providers”. Each is bound to act only on our instructions.
| Provider | What they do | Location |
|---|---|---|
| GoHighLevel (HighLevel Inc.) | CRM records, contacts, calendars and messaging | United States |
| Supabase | Application database, authentication and file storage | United States |
| Vercel | Application hosting and delivery | United States |
| Telnyx | Telephone numbers, call carriage and AI voice for the AI Caller | United States |
| Anthropic | The language model that conducts AI Caller conversations | United States |
| Stripe | Subscription billing and payment processing | United States |
| Resend | Transactional email such as sign-in and password reset | United States |
This list is current as of 23 October 2026. We will tell you before adding a provider that processes customer data.
Questions people actually ask
Who can see my customers' phone numbers and addresses?
The people you invite to your own workspace, at the level their role allows. No other BookRoofs account can reach your records — that is enforced by row-level security in the database, so it holds even if application code gets it wrong. A small number of our engineers can access production to operate and support the service; that access is logged and limited to people who need it.
What happens to my data if I cancel?
Take it with you before you go: Settings → Account → Your data downloads the whole workspace as one file, any time, no ticket and no waiting. After termination we hold it for 30 days so you can still ask for a copy, then we may delete it. Ask for deletion sooner and we will do it. Backups roll over on their own schedule, so deleted data can persist in them briefly before being overwritten — any vendor who tells you otherwise either has no backups or is not being careful with the word.
Do you use my data to train AI?
No. Not your business data, not your customers' records, not your call recordings. The AI Caller passes a model only what it needs for the call it is on, and the model provider is contractually barred from training on it.
Are call recordings legal where I operate?
That depends on your state, and it is genuinely your call to make, not ours. Some states require every party to a call to consent. You are the caller, so making the required disclosure is your responsibility — we give you the controls and say so plainly in the Terms rather than burying it.
Can I get a DPA signed?
Yes. Email privacy@bookroofs.com and we will send one. If your own customers are asking you for a subprocessor list, the privacy policy already names everyone who touches the data.
How do I report a security problem?
security@bookroofs.com, or the machine-readable contact at /.well-known/security.txt. Tell us what you found and how to reproduce it. We will acknowledge it, we will not threaten you, and we will tell you when it is fixed.
Reporting a vulnerability
If you find a security problem, email security@bookroofs.com — there is also a machine-readable contact at /.well-known/security.txt. Tell us what you found and how to reproduce it.
We will acknowledge your report, we will not threaten you for sending it, and we will tell you when it is fixed. Please give us a reasonable chance to fix it before publishing, and do not access or alter data belonging to anyone else while testing.
If something does go wrong
No system is perfectly secure, and a page that implied otherwise would be the least trustworthy thing on this site. If a breach affects personal information in your account we will notify you and the relevant regulators as the law requires, tell you what we know and when we knew it, and say what we are doing about it — including the parts that reflect badly on us.
Contact
Security: security@bookroofs.com
Privacy requests and DPAs: privacy@bookroofs.com
Everything else: support@bookroofs.com
See also the Privacy Policy for what we collect and your rights over it, and the Terms of Service for who owns what.