← Back to SOMA

Security

Last reviewed 30 September 2026.

Where your data lives

Postgres, authentication and file storage are in eu-west-1 (Ireland), read from the Supabase project on 2026-09-30. Edge functions run on Supabase's edge network in the region nearest the caller (not pinned).

Where AI runs

The assistant's text models run on GreenPT, in this order of preference: deepseek-v4-flash-0731, then glm-5.2, then mistral-medium-3.5-128b. Other text also leaves: Groq classifies accounting lines when a consultant links an accounting connector and transcribes voice dictation; DeepSeek receives document titles, the first 2,000 characters and knowledge-base chunks for knowledge-base indexing; Hugging Face computes embeddings of knowledge text. Each is listed in the table below.

Every image sent to the assistant or attached to a data request, and every scanned page, is sent to Anthropic (claude-haiku-4-5) in the United States, under Anthropic's commercial terms and data processing agreement. This includes scanned documents uploaded by recipients through the data portal.

Request path

The assistant's model calls and the other /api routes run through Vercel serverless functions in iad1 (US) before reaching a model, even when the model is in the EU. Scanned pages go from the browser to a Supabase edge function and from there to Anthropic; connector classification and knowledge-base indexing run inside Supabase edge functions. The rate-limit counters for /api are kept in Upstash in us-east-1.

Tenant isolation

Postgres row-level security is enabled on every organisation table, so a signed-in user can only read or write data of organisations they belong to. This was closed out on 2026-09-04.

The model proxy has required authentication since 2026-09-03 and is rate limited to 90 requests per minute per user (since 2026-09-09).

Records

The assistant's chat turns are written to an EU AI Act Article 12 log: model, token counts, hashes of the input, and the message text, the model's reply and the system prompt (truncated to 10,000, 20,000 and 15,000 characters), kept 180 days. Document extraction, scanned-page reading and edge-function model calls are not in this log. Consultant actions (approvals, corrections) are in a separate audit log that is not part of the 180-day deletion.

Encryption and storage

Traffic is encrypted in transit with TLS and HSTS. Data at rest is encrypted by the provider (Supabase managed Postgres and Storage).

Uploaded files are stored in Supabase Storage in eu-west-1, with access governed by storage policies.

Telemetry

Error monitoring uses Sentry (EU, de.sentry.io) and product analytics uses PostHog (EU). Browser events have e-mail addresses, tokens and secrets scrubbed, and recipient portal links redacted, before they are sent. Analytics autocapture is off. PostHog session recording is off. Session replay (Sentry) records only when an error occurs and never on recipient pages.

What Sentry receives, as recorded in the subprocessor list:

  • browser error events, scrubbed before transmission (e-mail addresses, JWTs, secrets, token URLs)
  • server-side error events from the Vercel functions and the listed edge functions carry the error message (up to 8000 characters) and stack (up to 4000 characters) with e-mail addresses, JWTs, secrets and portal-token URLs redacted before sending; other text in the message is sent as written
  • on-error replay with all text, inputs and media masked (sampling: session replay 0, on-error 0.2)

Sign-in

Sign-in uses a password with an optional TOTP second factor. Once a factor is enrolled, the app requires it at every sign-in (since 2026-10-01); the database policies do not yet require the second factor.

Data-subject requests

Account holders can export their data and delete their account from the Account security page, served by the gdpr-export and gdpr-delete functions.

Not in place

As of the review date above:

  • No ISO 27001 certification.
  • No SOC 2 report.
  • No independent penetration test report is published.
  • The API functions do not run in an EU region.
  • No data protection impact assessment (DPIA) is published.

Subprocessors

Every third party that receives data from SOMA, as of 30 September 2026. Regions are stated as read; "Not verified" means we could not read it from our side.

NameRoleRegionData sentTerms
SupabaseDatabase, authentication, file storage and edge functionseu-west-1 (Ireland), read from the Supabase management API on 2026-09-30 for both production projectsall customer data stored in the platform (emissions ledger, requests, contacts, reports); account credentials and session tokens; uploaded files
VercelWeb hosting, CDN and the /api serverless functionsStatic files from Vercel's CDN; the /api functions run in iad1 (US East, N. Virginia), read from the x-vercel-id header of /api/health on 2026-09-30every request to /api/* (model-proxy prompts, uploads and exports pass through the function); request metadata (IP address, user agent)
GreenPTText language models (the assistant, extraction, report drafting)EU, per GreenPT's own statement (greenpt.com/privacy, read 2026-09-30): models self-hosted on Scaleway (France) and Verda (Finland). The same page says EU and US regions are now available; which region serves api.greenpt.ai for our key is not verified from the boxassistant prompts and the context built into them (figures, document text, question text); text only
AnthropicImage and scanned-page reading (claude-haiku-4-5)United States (the api.anthropic.com endpoint); no regional endpoint is configuredevery uploaded image and every scanned page (chat drop, portal recipient upload, extract-vision); any /api/ai request that carries an image, including the text of that request
GroqVoice-dictation transcription and accounting-line classification (llama-3.3-70b-versatile)United Statesaudio recorded by the user for dictation; line description, account name, amount and currency of synced accounting lines
DeepSeekReference-corpus indexing: one-to-two-sentence context written for each knowledge-base chunkNot verified from the box (the api.deepseek.com endpoint); the provider is headquartered in Chinadocument title, the first 2000 characters of the document and the chunk, for rows of the knowledge_base table and scraped enforcement pages
ResendTransactional e-mail (data requests, reminders, invitations, replies)United States (Resend's default); the sending domain's region cannot be read with the send-only API key we hold (GET /domains answers 403, 2026-09-30) and is an owner itemrecipient address, subject and body of each e-mail SOMA sends
CloudflareDNS for somaai.earth and inbound e-mail routing (Email Routing and an Email Worker forward replies to the receive-email function)Cloudflare's global network. It does not proxy the application hosts (app and esg resolve to Vercel, 2026-09-30)DNS queries for the platform's hostnames; inbound reply e-mails (sender, subject, body, attachments) while they are routed to SOMA
UpstashRate-limit counters for /api routesus-east-1 (database soma-mvp-ratelimit, HISTORY #191)sliding-window counters whose keys are SHA-256 hashes (first 32 hex characters) of the user id, portal token, partner API key id (api/v1/ai-factors.ts) or client IP address, never the raw value (no message content)
SentryError monitoring and on-error session replayEU (Germany): the production DSN host is ingest.de.sentry.io, read 2026-09-30browser error events, scrubbed before transmission (e-mail addresses, JWTs, secrets, token URLs); server-side error events from the Vercel functions and the listed edge functions carry the error message (up to 8000 characters) and stack (up to 4000 characters) with e-mail addresses, JWTs, secrets and portal-token URLs redacted before sending; other text in the message is sent as written; on-error replay with all text, inputs and media masked (sampling: session replay 0, on-error 0.2)
PostHogProduct analytics (autocapture disabled)EU: VITE_POSTHOG_HOST is eu.i.posthog.com in both production environments, read 2026-09-30named product events and page views with personal data scrubbed; no session recordings
Hugging FaceText embeddings (BAAI/bge-large-en-v1.5 through router.huggingface.co)Not verified from the box (provider-routed inference)up to 2000 characters of text per call: reference-corpus chunks (knowledge_base rows) from the indexing functions, and the query text when generate-embedding is called
ClimatiqEmission-factor lookup for a connected Climatiq account (only when a consultant connects one)Not verified from the boxan activity identifier plus an amount and unit
WRI AqueductPhysical water-risk lookup for a siteNot verified from the boxthe latitude and longitude of the site being assessed
VirusTotalUploaded-file reputation checkNot verified from the boxthe SHA-256 hash of an uploaded file; the file itself is not sent
CNaughtCarbon credit purchases (carbon edition only)Not verified from the boxportfolio choice, tonnes and beneficiary name of a purchase the consultant confirms
XeroAccounting connector (only when a consultant connects a Xero organisation)Not verified from the boxOAuth tokens; the accounting data SOMA reads back from the connected organisation
Intuit QuickBooksAccounting connector (only when a consultant connects a QuickBooks company)Not verified from the boxOAuth tokens; the accounting data SOMA reads back from the connected company
SageAccounting connector (only when a consultant connects a Sage business)Not verified from the boxOAuth tokens; the accounting data SOMA reads back from the connected business
PayhawkExpense connector (only when a consultant connects a Payhawk account)Not verified from the boxAPI credential; the expense data SOMA reads back from the connected account
HoldedAccounting connector (only when a consultant connects a Holded account)Not verified from the boxAPI credential; the accounting data SOMA reads back from the connected account
GoogleSign-in with Google for Drive export (the drive.file scope) when a consultant chooses to save a report to their own DriveNot verified from the boxthe report file the consultant chooses to upload to their own Google Drive; Google OAuth token held in the browser
Logo and favicon lookup (icon.horse, Google favicon service)Company logo shown for a clientNot verified from the boxthe domain name of the client's website, in the image URL

Read the privacy policy

Reporting a vulnerability

Write to the address below with details. The machine-readable contact file is linked as well.

security@somaai.earth · /.well-known/security.txt