
Every claim here is traceable to code.
Security posture, data retention, sub-processors, and incident response. Forward-looking items say so. Nothing on this page is aspirational copy.
Processed and stored in Canada.
Your text travels over TLS to the detection API (Fly.io, Toronto), is scored by Google Vertex AI in Montreal, and the block/allow decision comes back. On that path the prompt is not written to any database. What we do keep, and for how long, is the table below, and each row is enforced in code or config.
On residency, including the part most vendors skip. Storage is Canadian: the API, the dashboard and the database all run on Fly.io in Toronto. Detection is Canadian too, and that is worth stating separately, because it is the half that is most often quietly untrue.
Scoring runs on Google Vertex AI pinned to the Montreal region. Google commits to performing ML processing, explicitly including inference, in the region where the data is stored. That is a stronger guarantee than a provider merely having a Canadian address, and it is the distinction we would encourage you to test on any vendor you assess, including us.
We put it that way because we got it wrong ourselves. Detection previously ran on a deployment type that let our provider serve a request from any region worldwide while the underlying resource sat in Canada. We found it on 30 July 2026, published the failure on this page the same day rather than waiting until it was fixed, and moved detection to Montreal. Our code now refuses to start against a non-regional endpoint, so it cannot recur silently.
| Data class | What it is | Kept for |
|---|---|---|
| Prompt text in flight | The text being scored | Not persisted. Scored, then discarded. |
| Session entity codes | Entity category codes for cross-message detection ("an email was seen"), never values (DB constraint + CI test) | 24 hours |
| Redaction maps | Original values mapped to tokens, only when you use tokenize/restore | 30 days, plus on-demand DSAR deletion |
| Incident captures | Raw prompt text. Off by default. Written only if one of your admins switches it on, and only when a user overrides a block with Send Anyway. Encrypted (server-side key, or client-side zero-knowledge where we never hold the key). | Content cleared at 90 days, row at 365 |
| Audit metadata | Entity-type codes, platform, was-blocked. Never raw PII. | 365 days |
| Usage metering | Per-key request counts for billing. No prompt content. | Retained for billing |
| Operational logs | No prompt or reply text, only lengths, hashes, counts | Provider log window |
| Backups | Rolling database snapshots | 5-day rolling window |
Erasure on request: a data-subject erasure hard-deletes audit and incident rows and cascades to the detection API to delete redaction maps immediately rather than waiting out the 30-day TTL. If any step fails, the erasure is reported as failed, never as complete.
How it is built.
The decision is server-side
Detection, scoring, combination rules and the block decision all execute in the API. Nothing that calls it can override or weaken that decision, because there is no policy logic on the caller to tamper with. This holds whatever sits in front of the API, so how you deploy does not change the security properties on this page.
Prompts are discarded after scoring
On the detection path, text is sent to the detector, scored, and discarded. It is not written to a database. Session memory stores entity category codes only, enforced by a database constraint and a CI test. One boundary on that claim, stated in full below: your team can deliberately switch on incident capture.
The one way a prompt gets stored, and you control it
There is exactly one path that writes prompt text to a database, and it is off unless an admin on your team turns it on. It fires only when someone overrides a block by clicking Send Anyway, which is the event a governance team actually needs to review. Cancelling stores no text at all. When it is on, content is encrypted before storage, either with a server-side key or in a zero-knowledge mode where we never hold the key. If the encryption key were ever missing, the record is written without content rather than in the clear. Content is erased at 90 days and the row at 365. We would rather show you this switch than have you find it.
Nothing sensitive in logs
No prompt or reply text is ever logged. Unreadable detector replies are logged as a length and a hash prefix, never content. Retention sweeps and erasures log counts only.
Encrypted in transit and at rest
TLS everywhere, including the database connection. Provider disk encryption at rest on Fly.io and Google Cloud. Opt-in incident content gets application-layer encryption on top (server-side key, or client-side zero-knowledge where we never hold the key). Application-layer encryption of redaction maps is in progress and stated as roadmap, not claimed as shipped.
Fail-safe by design
An unreadable detector reply fails closed (blocks). An upstream outage returns HTTP 503 within seconds instead of hanging or silently allowing.
Access is one person, disclosed
PII Shield is operated by a single person with MFA on every production account (GitHub, Fly, Google Cloud, Clerk, Stripe), secrets held only in provider secret stores, and no customer PII touched in operations outside the audited DSAR path. We disclose the bus factor rather than hide it; wind-down terms are in the incident-response doc.
What your IT team has to do.
Less than most vendors ask for, because the service is a single stateless HTTPS call that returns a decision.
One outbound destination
One HTTPS endpoint to allowlist. We never open a connection into your network, so there is no inbound rule, no VPN and no tunnel to approve.
One key, scoped to you
Keys are issued from the dashboard and stored hashed, and a key resolves to your team so your custom terms and policy apply and nobody else's do.
Straight about the state of this: per-key enforcement and rate limiting on the detection endpoint are the work in front of us and are not switched on yet. What that does and does not mean matters. The detection path holds no state, reaches no customer record and writes nothing to a database, so what is exposed is our own cost and model quota, not your data and not another tenant's. The paths that do touch stored data already require a signed token today.
Nothing to host
No appliance, no agent on your servers, no certificate exchange, nothing to patch. If you want to try it before any of the above, the endpoint speaks ordinary JSON and you can call it from a terminal.
Who else touches data.
We notify customers before adding a sub-processor that touches their data. The AI sub-processor sees prompt text in order to score it. Nobody stores it on the detection path, and per Google Cloud's Vertex AI terms prompts are not used to train their models.
Each vendor below states whether its data terms are actually in force, rather than one blanket sentence covering all five. We read each vendor's own agreement to establish this. Most bind automatically as part of the terms we accepted at signup, so there is nothing separate to execute. One does not, and it says so.
Google Cloud (Vertex AI)
Montreal (northamerica-northeast1). Google commits to performing inference in the region where data is stored.AI detection engine. Prompt text in transit for the scoring call; not retained by us.
Data terms: Yes, incorporated into the Google Cloud agreement
Read their termsFly.io
Toronto (yyz); US entityApplication + database hosting. Data at rest (Postgres) and the runtime.
Data terms: Not yet. Fly pre-signs and it activates on our counter-signature, which is being done now. We would rather say so than let you find it.
Read their termsClerk
USDashboard authentication. Dashboard user identity (email, auth), not prompt data.
Data terms: In force. Incorporated into the Agreement, effective on its effective date.
Read their termsStripe
US / globalBilling / payments. Billing contact and payment data (Stripe-held), not prompt data.
Data terms: In force. Forms part of the Agreement we accepted.
Read their termsResend
USTransactional email. Recipient email address and message content we send.
Data terms: In force. Binding on entering the Agreement.
Read their termsThe 72-hour commitment.
On confirming a personal-data breach that affects customer data, we notify affected customers without undue delay and within 72 hours of confirmation, with the nature of the breach, the data classes involved, the likely consequences, and the mitigation taken.
This aligns with PIPEDA breach-of-security-safeguards obligations and the GDPR Article 33 standard. Because prompts are not stored and session data is metadata-only, the blast radius of most incident classes is structurally limited, and investigations use metadata (counts, hashes, timestamps), not your content.
The honest SOC 2 answer.
No, and at this price we will not pretend to. Here is our security overview, sub-processor list and data-flow, and we will sign your DPA. SOC 2 is on the roadmap once revenue supports the audit cost. If SOC 2 is a hard requirement today, we are not your vendor yet.
A completed CAIQ-Lite self-assessment (the industry-standard middle ground) and a pre-answered security questionnaire are available on request, alongside the full trust pack.
On the DPA
We do not publish our own. Send us yours and we will sign it, subject to review. That is deliberate rather than a gap: at this size a bespoke agreement we authored is a document your legal team would replace with their own anyway, and we would rather spend the time on your paper than on ours. Our five sub-processors are listed above and ready to be named and approved at signature.
Want the full pack?
Security overview, retention tables, sub-processor detail, incident-response plan and the CAIQ-Lite self-assessment. We send it as-is, no NDA required.
Request the trust pack