Security review
What goes where, what is kept, and who can read it.
Written for the reviewer filling in a vendor questionnaire. It describes the system as it runs today; items not live yet are marked Available after launch. The public summary is on the security page and the privacy notice.
Architecture
Data flow for one message
- A staff member pastes or sends text on a protected AI site. The extension sends it to the gate over HTTPS, with the device credential and the conversation's sealed vault.
- The gate runs the firm's industry template, names settings, topics and client list. Identifiers with checksums (cards, routing numbers, IBANs, VINs) count only when the checksum holds.
- Caught values become placeholders such as
[SSN_1]. Passwords, keys and privileged material stop the message outright. Anything set to Flag goes as written and is reported to the owner. - The gate returns the placeholder text and the updated vault, sealed. The extension puts the placeholder text in the chat box, and the AI site receives only that.
- When the answer comes back with placeholders, the extension asks the gate to restore them for display. The real values are shown only by Safe-Desk's own display, inside a closed shadow root the site's scripts can't read; the chat page itself keeps the placeholders.
- The owner's log gets one line: who, when, the outcome, which site, and the names of the rules that fired. Never the text, never a value.
The same gate serves the extension, the Safe-Desk chat, the connector and the Gate API.
What is stored, and for how long
| Data | Kept |
|---|---|
| Chat messages, AI answers, and the values caught in them | Never stored |
| The swap table (vault) | Never stored on the server. It travels sealed with the conversation: in the extension's session storage, which the browser clears when it closes, or with the API caller |
| Files uploaded for the connector: protected text and sealed values, never the original file | 7 days, or until the person deletes them |
| Documents finished through the connector | 7 days, encrypted, visible only to the person who made them |
| Owner's log (who, when, outcome, rule names) | 400 days |
| Activity trail (sign-ins, staff, settings, keys, billing) | 400 days |
| Client list | Only as one-way keyed hashes, until the owner replaces it |
| Accounts: names, work emails (encrypted), password hashes | While the account exists |
| Browser and API credentials | Only their hashes, until revoked |
| Connected AI apps | Name, return address, when connected and last used; up to 30 days after the connection ends |
| Error records, scrubbed of client data | 90 days after last seen |
| Usage counts and hosted AI cost per month | For billing, while the account exists |
| The firm's own AI key, if the owner adds one | Until removed; encrypted, only the last four characters shown |
| Daily backups of the database, encrypted whole | 14 days. Deleted files and documents are gone from backups within 14 days |
| Card details | Never; Stripe holds them |
Retention runs when the server starts and then daily; expired files and documents are purged every hour and are never served once expired.
Encryption
- In transit: HTTPS only, with HSTS. Session cookies are HttpOnly, Secure and SameSite=Strict, and a session lasts 12 hours.
- At rest: AES-256-GCM with a random 96-bit nonce per item and a full 16-byte tag, so sealed data can't be read or altered by whoever holds it. This covers the vault, stored files and documents, staff names and emails, the activity trail, the firm's AI key and whole backup files.
- Keys: one 32-byte master key, from
DESK_DATA_KEYor a key file kept beside the data folder, never inside it, readable by the service account only. A separate key is derived for each purpose with HKDF-SHA256 (for example vault, audit, backup), so one purpose's key can't open another's data. - Lookups: an email at sign-in and client names are found by keyed HMAC-SHA256 hashes, not stored in the clear.
- Passwords: scrypt with a per-user salt. Sessions, device credentials, API keys and OAuth tokens are random 256-bit values; only their hashes are stored.
Firm isolation
- Every query is scoped to the signed-in person's firm. Owner routes check the role at the server and refuse staff; platform-admin routes answer 404 to everyone else.
- A vault is sealed together with the id of the person it was made for, and one made for anyone else is refused.
- Files, finished documents and connector tokens belong to one person. The owner can't open a staff member's files or documents.
- State-changing requests from the web app must be JSON from Safe-Desk's own origin. The pages run under a strict content security policy: no inline script, nothing loaded from other sites.
- An automated attack suite runs before every release. It covers cross-firm access, roles, origins, tampered history, path traversal, brute force and values leaking into logs.
Audit trail
Sign-ins and failed sign-ins, staff changes, template and mode changes, API key and device changes, connected apps and billing changes are appended to the firm's Activity tab, which the owner reads. The actor and details are encrypted, and details are scrubbed of secrets before they are written. The application only ever appends to it.
Available after launch A tamper-evident chain over the owner's log and the activity trail. Each entry gets a sequence number and a SHA-256 hash over the previous hash and the stored row, sealed fields included as ciphertext, so checking the chain needs no key. A changed row breaks its link; a deleted row leaves a gap; removing the newest rows is caught by a keyed head record and daily anchors. The owner can verify the chain on demand, and the monthly report prints the head hash so the firm holds a copy nobody at the server can change.
Subprocessors
| Service | Purpose | What it receives |
|---|---|---|
| Hostinger | Hosting | The application and its database |
| An AI routing service | The chat inside Safe-Desk, when a firm uses Safe-Desk's hosted models | Message text with caught values already replaced by placeholders (Flag items as written), passed to the model the firm picks. Under SOPHIA XT's account |
| Anthropic, OpenAI, Inception | The model behind that chat, by the firm's choice; or Anthropic or OpenAI directly when the owner adds the firm's own key | The same placeholder text. Inception (Mercury) also receives topic names when an owner asks it to suggest phrases |
| Stripe | Billing | The owner's name, email, billing address and card, seat count, and hosted AI cost in cents. Never message text or a value |
| ntfy.sh | Phone alerts when the owner turns them on, and our own error alerts | Firm name, staff name and rule names; a private topic appears by number, not name. Error alerts are scrubbed. Never a value |
| The push service of the owner's browser maker Available after launch | Phone and browser alerts without a third-party app | The same alert line as ntfy, encrypted to the owner's device key (RFC 8291) so the push service can't read it. Only owners can subscribe |
The AI chat sites staff use through the extension (ChatGPT, Claude and the rest) are the firm's own tools under the firm's own terms. They receive only the placeholder version of what staff paste or send.
Not done yet
- No SOC 2 report. We are building to the Trust Services Criteria; when a report exists it will be shared under NDA.
- Two-factor sign-in, off-site backups with a second provider, and a third-party penetration test are planned.
- Names, places and dates outside the firm's client list are detected from context, a best effort each firm sets to off, flag or protect.
Incident and security contact
Email contactus@sophiaxt.com with "Security" in the subject, for a suspected incident, a vulnerability report, or a questionnaire. We read every report and take no action against good-faith research.