Security
What is true today
Every claim below names the code, the test or the document behind it, and a test opens all of them before this page ships. A security page that claimed more than the product does would be the first thing we got wrong, so what is missing is on this page too.
Reporting a problem: /.well-known/security.txt.
Built
In the product now
Credentials encrypted at rest
builtEvery stored key and token is sealed with AES-256-GCM before it reaches the database, under SHAI_ENCRYPTION_KEY. A deployment with no encryption key refuses to store a credential rather than storing it in the clear, and the refusal names what is missing.
- platform/packages/connectors/src/secrets.ts
- platform/packages/connectors/src/secrets.test.ts
Encrypted in transit
builtEvery connection to the database requires TLS, and the application, the worker and the comms service are reached over HTTPS only.
- platform/packages/db/src/client.ts
- docs/platform/architecture.md
A run's environment is stripped before the model sees it
builtA skill runs in a subprocess whose environment is built by denying every name that reads as a credential or a connection string — the platform's encryption key, the pooled database URL, the queue, the identity provider's key — and then layering on the one secret that process needs: your own model key. That key is in the environment, and saying otherwise would be untrue.
- platform/packages/agent-runtime/src/subprocess-env.ts
- platform/packages/agent-runtime/src/subprocess-env.test.ts
A run is refused when it tries to print that environment
builtBecause your model key is in that environment, a PreToolUse hook sits in front of every shell command a run asks to make and refuses the ones that read the environment — env and printenv however they are spelled, bare set, export -p, declare -x, the per-process environ file under /proc, an inline node or python program that reads process.env or os.environ, PowerShell's env: drive, and any command expanding a key-shaped variable name. Anything ambiguous in that family is refused too. The model is told in one sentence why and what to use instead, the refusal is written to the run's transcript and to your audit log, and the run carries on. It is not a sandbox: a program already on disk can still read its own environment, and a run only ever executes in a checkout of your own instance repository.
- platform/packages/agent-runtime/src/env-guard.ts
- platform/packages/agent-runtime/src/env-guard.test.ts
- platform/apps/worker/src/worker.test.ts
Connector tokens are injected at execution, not handed to the model
builtThe tools a run may call are resolved in the worker, per run, from your own connections. The call is made on the worker's side and the model is handed the app's answer — the connector's token is never put into the subprocess environment, the prompt, or a run event.
- platform/apps/worker/src/connections.ts
- platform/apps/worker/src/connections.test.ts
- platform/packages/agent-runtime/src/run-tools.ts
Tenancy enforced by the database client, not by callers
builtThe scoped client writes the tenancy predicate itself, so no request names its own tenant and a business's rows are invisible from another business. A test reads the schema rather than a list, so a table added next month is classified or the build fails.
- platform/packages/db/src/scope.ts
- platform/packages/db/src/tenancy.test.ts
- platform/packages/db/src/scope-audit.test.ts
Per-business scoping, including for the people you let in
builtA client approver or a guest is refused any workspace they were not granted, before a query is built — so the refusal holds for every screen at once rather than for the ones somebody remembered to guard.
- platform/apps/web/src/server/roles.test.ts
- platform/apps/web/src/server/trpc.ts
No training on your data, and your vendor terms are the ones that apply
builtSHAI trains no model on anything. Runs go to Anthropic on your own API key, so your agreement with Anthropic — not ours — governs what happens to that text; the same is true of any other model vendor whose key you add.
- docs/compliance.md
- platform/packages/agent-runtime/src/claude-runner.ts
Export and deletion, by you, from the product
builtYour rows export as JSON from settings, credentials redacted. Deleting removes every table this account owns, unlinks your instance repository rather than deleting it, leaves one audit tombstone, and tells you what is left to do by hand. A test asserts that every table in the schema is either deleted with the account or named as the operator's, with a reason.
- platform/apps/web/src/server/tenant-data.ts
- platform/apps/web/src/server/audit.test.ts
An audit log with its own screen and a CSV
builtConnections, approvals, exports, deletions, role changes and billing edits are recorded with who did them, to what and in which workspace. Owners and admins can read and download the whole log; it never carries a credential, a prompt or a client's own words.
- platform/apps/web/src/server/routers/audit.ts
- platform/apps/web/src/server/audit.test.ts
- platform/apps/web/src/app/api/audit.csv/route.ts
Retention, written down and swept
builtRun logs — what you asked for and what the agent produced — are cleared after 90 days by the worker, leaving the run's skeleton so your own usage record survives. Messages are kept until you delete the account. Nothing else is swept, and nothing is kept that this says is gone.
- platform/apps/worker/src/retention.ts
- docs/platform/architecture.md
Spend limits that stop a run
builtA daily ceiling per account across every model provider, an optional cap per person, per-run turn and time limits from each agent's own manifest, and an approval — not a failure — when a run would cross one.
- platform/packages/billing/src/spend.ts
- tools/billing-acceptance.mjs
Rate limits and webhook verification
builtPublished third-party rate limits are respected per connector, and every inbound webhook is verified against the raw bytes before anything parses them — Stripe, Slack, Teams, Discord and Resend each with their vendor's own scheme.
- platform/packages/connectors/rate-limits.json
- platform/apps/web/src/server/webhook-auth.ts
- platform/packages/billing/src/stripe.ts
We never hold a card
builtPayment happens on Stripe's own pages. There is no column for a card anywhere in this platform's schema — it holds a customer id, a subscription id, a plan and a period — and an acceptance check greps every billing table to keep it that way.
- tools/billing-acceptance.mjs
- platform/packages/db/src/schema.ts
A compliance posture written down per agent
builtOfficial APIs only, no bot-detection evasion, consent before contact where the law requires it, AI disclosure to anyone who is not you, and professional-advice disclaimers where they belong.
- docs/compliance.md
A standing audit of every secret the system can hold
builtWhat each secret is, what protects it, what proves it, and the gaps still open — kept as a document that is updated when the code is, rather than as a claim.
- docs/platform/secrets-audit.md
Backups and a restore drill
builtThe database's backup policy, its point-in-time window, the retention rule and the restore drill are written down, and the drill was run.
- docs/platform/architecture.md
A security.txt, regenerated every build
builtRFC 9116 at /.well-known/security.txt, with an Expires field computed per build so it cannot quietly go stale, and a contact that is a real place a report reaches a person.
- platform/apps/web/src/app/.well-known/security.txt/route.ts
Not yet
What SHAI does not have
SOC 2
not yetNo audit has been done and no report exists. The readiness work is done and written down — every control mapped to a file or a named setting — and the audit itself is a separate engagement that has not been booked.
docs/platform/soc2-readiness.md is ready for the vendor the owner picks
Single sign-on (SAML)
not yetSHAI's side is built and tested: an enterprise connection per account, domain verification, and a refusal when the email domain is not yours. The identity provider itself is a Clerk plan the operator has not bought, so no live SSO sign-in has happened.
Contract C C33 — the live half is the operator's Clerk dashboard
A public trust portal and a third-party attestation
not yetThere is no trust portal and nothing from an auditor to publish.
after the SOC 2 engagement above
Per-user, per-connection privacy controls
not yetConnections scope to an account and a business today. Per-user connections and per-user retention controls are not built.
Contract C C8
EU data residency
not yetThe platform runs in the United States. There is no EU residency option and none is promised.
not scheduled
Sub-processors
Who else touches your data
Read from this deployment’s own configuration — the same configuration the health endpoint reads — so this list cannot name a vendor that is not in use.
Every deployment uses these
- Vercel — serves the web application
- Railway — runs the worker and the comms service
- Neon — the database — every row this platform stores
- Upstash — the queue a run waits in
- Clerk — sign-in and session verification
- Anthropic — the model that does the work, on your own key — so Anthropic's terms with you, not with us, govern it
- GitHub — your instance repository, in your own account
Connected on this deployment
- Pipedream — the managed OAuth hop, and the tool call, when you connect a tool through the catalogue
Only when the operator or you connect it
- Stripe — subscriptions and invoices; it holds the card, this platform never does
- Composio — the managed OAuth hop when you connect a tool through the catalogue
- Cloudflare R2 — object storage for the files a run produces
- Resend — the inbox a document can be forwarded to
- Browserbase — a hosted browser, when you connect one instead of the worker's own
- Twilio — calls and text messages, on the operator's own account
- Slack — SHAI in a Slack workspace
- Microsoft — SHAI in Microsoft Teams
- Discord — SHAI in a Discord server
- OpenAI — bulk and mechanical work, on your own key
- Google — bulk and mechanical work, on your own key
- DeepSeek — bulk and mechanical work, on your own key
- OpenRouter — bulk and mechanical work, on your own key
Hosting
- Neon (the database) — us-east-2 — recorded in the deployment handoff
- Upstash (the queue) — us-east-1 — recorded in the deployment handoff
- Vercel (the web application) — the region the operator selected
- Railway (the worker and comms) — the region the operator selected
The platform runs in the United States. There is no EU data-residency option and none is promised.
Reporting a problem
The contact is in security.txt, and the contact page reaches the same people. There is no bug-bounty programme.