SHAI

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

built

Every 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

built

Every 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

built

A 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

built

Because 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

built

The 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

built

The 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

built

A 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

built

SHAI 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

built

Your 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

built

Connections, 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

built

Run 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

built

A 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

built

Published 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

built

Payment 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

built

Official 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

built

What 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

built

The 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

built

RFC 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 yet

No 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 yet

SHAI'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 yet

There is no trust portal and nothing from an auditor to publish.

after the SOC 2 engagement above

Per-user, per-connection privacy controls

not yet

Connections 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 yet

The 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

  • Vercelserves the web application
  • Railwayruns the worker and the comms service
  • Neonthe database — every row this platform stores
  • Upstashthe queue a run waits in
  • Clerksign-in and session verification
  • Anthropicthe model that does the work, on your own key — so Anthropic's terms with you, not with us, govern it
  • GitHubyour instance repository, in your own account

Connected on this deployment

  • Pipedreamthe managed OAuth hop, and the tool call, when you connect a tool through the catalogue

Only when the operator or you connect it

  • Stripesubscriptions and invoices; it holds the card, this platform never does
  • Composiothe managed OAuth hop when you connect a tool through the catalogue
  • Cloudflare R2object storage for the files a run produces
  • Resendthe inbox a document can be forwarded to
  • Browserbasea hosted browser, when you connect one instead of the worker's own
  • Twiliocalls and text messages, on the operator's own account
  • SlackSHAI in a Slack workspace
  • MicrosoftSHAI in Microsoft Teams
  • DiscordSHAI in a Discord server
  • OpenAIbulk and mechanical work, on your own key
  • Googlebulk and mechanical work, on your own key
  • DeepSeekbulk and mechanical work, on your own key
  • OpenRouterbulk 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.