The four questions your auditor will ask before you sign.
One short page for the finance, healthcare, and government buyers we now onboard. Where customer data lives — and what is never stored. Encryption at every hop. The exact Microsoft Graph permissions Wardwell requests, with per-scope justification. And the documented audit-log retention policy.
Data residency & what is never stored
Tenant scan data sits on a managed Postgres instance. DATABASE_URL resolves from the platform's rdbms capability (declared in polsia.toml), and data residency inherits the managed region that provider runs in. Wardwell does not co-locate a database anywhere else.
Sub-processors
- Managed Postgres host. One: the platform's rdbms capability. Tenant scan rows (Scan, ScanIssue, ScanFinding), waitlist signups, and the better-auth session table all live there.
- Microsoft Graph.The scan walker reads site and file metadata against the signed-in admin's own Entra tenant. Token issuance starts and ends inside that tenant — Wardwell never holds a multi-tenant application token.
What is never stored
- Raw file bodies.The walker reads SharePoint file metadata (path, etag, last-modified stamp) and walks redirect chains — it never copies the file contents into Wardwell's database.
- End-user personal contact data. The scan does not pull Graph users, contacts, calendars, or any person-record API. The data subject is the tenant admin who registered the app.
- End-user browsing history.Per-session activity outside the scanned hub URLs is out of scope; Wardwell's scan walker is bounded to the site list the admin registered.
- End-user direct messages. Teams chat, Outlook mail bodies, and OneDrive personal content are never read. Wardwell holds no Graph scope that would let it.
Encryption at rest & in transit
Encryption at every hop. The platform's reverse-proxy layer (proxy.ts) enforces a strict-transport posture on the edge, and the database-side encryption is layered with table-level AES for the columns that carry PII.
What this means in practice
- At rest.The managed Postgres default encryption covers the full disk; table-level AES is layered on for any column carrying PII (such as a scan URL path that resolves to a person's page).
- In transit. proxy.ts sets a per-request CSP nonce, connect-src 'self' caps outbound calls, and all fetch() traffic from route handlers is forced TLS. http:// URLs are dropped at the build gate.
- Header posture. A strict frame-ancestors 'none'policy blocks embedding; the CSP nonce rotates per request so a stale script tag cannot reuse yesterday's permission.
TLS end-to-end
Microsoft Graph permissions & justification
Wardwell operates on a delegated Microsoft Graph token, not an application token. The scope list is exactly six values — nothing broader is requested, and nothing broader can land in the cache. The Entra app registration is single-tenant and consent is granted once per tenant by the registered admin.
openid profile email Files.Read.All Sites.Read.All offline_accessPer-scope justification
- Sites.Read.All— read SharePoint site data across the registered hub's site collection. Used to enumerate the hub URLs Wardwell patrols. Read, never write; no Sites.ReadWrite.All or any write-capable scope is anywhere on the list.
- Files.Read.All — read file metadata (path, etag, last-modified stamp). Used to assemble the redirect-chain picture per URL and surface broken-link candidates. File bodies are not pulled; only the metadata header is.
- openid + profile + email— OIDC identity. Wardwell reads who signed in to display their name and email on the admin's profile. No contact graph, no People API.
- offline_access— keeps the delegated token alive across runs. The cached refresh token is per-admin and rotated by Microsoft's token endpoint; revocation happens at account deletion.
Delegated, not application
Audit-log retention
The ScanFinding rolling-history table keeps the per-finding record an admin can read on the /issues page. Older rows are pruned by the platform's background job to a 30-day rolling window — that is the documented posture for any regulated buyer who asks "how long do you keep finding rows".
30-day rolling window
What this is — and is not
- The 30-day window covers the per-finding rolling history only — ScanFinding rows. The parent Scan and the per-scan ScanIssue rows stay for as long as your tenant is on board; they are not part of the rolling window.
- A weekly digest email (jobs/weekly-issues-digest.js) ships the same finding rows out of band so the audit trail isn't lost when the rolling window ticks over.
- Better-auth's /api/authhandler records each session's issuance, refresh, and revocation — sign-in events carry a wall-clock stamp and stay for the session's documented lifetime.
Next step
Get on the security review list.
Drop your work email and we'll come back with the SOC 2 mapping, the full scope-by-scope justification, and a security-review call within one business day. NDA on request.
We use this address only to schedule the security review. No marketing list, no re-marketing.