[ security ]
Security overview
Summary of how we host, isolate, encrypt and back up Oshu. The full technical and organisational measures (TOMs) under Art. 32 GDPR and the Data Processing Agreement are available to authenticated tenants at /settings/legal/toms and /settings/legal/dpa. The sub-processor list stays public so changes are easy to audit.
Hosting and jurisdiction
- Application, database and backups on Hetzner Online GmbH servers in Germany (Nuremberg / Falkenstein). No third-country transfer.
- Inference and OCR via Mistral AI SAS on EU API endpoints (France). Mistral does not train on data submitted via the API. BYOK plan: requests routed via the customer's own Mistral key.
Tenant isolation
- Workspaces isolated at the row
level via Postgres Row-Level Security (RLS). The application role is
NOINHERITso RLS cannot be bypassed. - Every tenant-scoped query runs
inside a transaction with
app.tenant_idset; no shared cache, no implicit cross-tenant reads. - Administrative cross-tenant actions are audited in a separate audit log.
Encryption
- TLS 1.2+ for all external traffic (browser ↔ app, app ↔ Mistral, app ↔ database).
- BYOK Mistral keys encrypted at rest with AES-256-GCM; master key supplied via environment, never stored in the database.
- Off-site backups encrypted with
age(X25519) before leaving the host; private identity stored outside the production environment.
Authentication and access
- SSH-key login only for production hosts. Password login disabled.
- MFA on every administrative account: hosting provider, Git host, deployment panel.
- Application logins via Better Auth, optional MFA. Passwords stored as cryptographic hashes only. Sessions are HttpOnly, Secure, SameSite cookies.
- Widget session tokens for the chat-history endpoint are HMAC-signed and short-lived.
Backups and recovery
- Daily
pg_dumpbackups, encrypted and shipped off-site viarclone. - Recovery point objective (RPO): 24 hours. Recovery time objective (RTO): under 8 hours from full host loss.
- Quarterly restore tests against an isolated environment to verify recoverability.
Data lifecycle
- Chat logs auto-deleted after 12 months. Customers can also wipe their chat logs on demand.
- Account deletion removes the workspace and all associated data. Backups retain deleted records only for the documented retention window, then expire.
- API keys, HTTP Authorization headers and similar secrets are stripped from logs before persistence.
Widget embed
The chat widget is loaded from https://oshu.eu/widget.js — a first-party
endpoint on the same operator and infrastructure as the rest of Oshu.
No third-party CDN, no Cloudflare, no Google Fonts, no analytics
beacons. At runtime the widget talks only to the Oshu API over TLS;
it loads no further remote code.
- Served over HTTPS only. HSTS is
enabled on
oshu.eu, so downgrade attacks are rejected by the browser before the script is fetched. - No external JavaScript, fonts or stylesheets pulled at runtime. Markdown rendering, sanitisation and KaTeX are bundled into the widget itself, so the script surface a customer embeds is exactly what we ship.
- Strict same-origin API calls from
the widget to
oshu.eu. The widget cannot be repointed to a third-party backend at runtime.
Subresource Integrity (SRI)
The default embed snippet intentionally omits the integrity= (SRI) attribute. SRI pins the
embed to one specific bundle hash and blocks the browser from running
anything else. Because /widget.js is a
rolling endpoint that receives security fixes and improvements
continuously, pinning a hash would force every customer to update
their embed on every release — and an unupdated embed would silently
break for end users. The default embed therefore optimises for fast
delivery of security patches over per-bundle pinning.
The substituted protections are: first-party hosting on the same operator that runs the rest of the platform; TLS with HSTS; no third-party code paths inside the widget; and standard host hardening (SSH-key login only, MFA on the deployment panel, encrypted off-site backups) covered above.
If a customer's security policy requires an SRI-pinned embed, we
will publish a versioned bundle
(/widget/v<semver>.js) together with
its SHA-384 hash so the embed can be pinned and rotated on the
customer's schedule. Email oshu@beyondsimulations.com to request this.
Incident response
Affected customers are notified in text form without undue delay, within 48 hours of confirmed discovery at the latest, with the minimum information required by Art. 33 (3) GDPR. We support the customer (as controller) in any onward notification to the supervisory authority under the 72-hour Art. 33 deadline.
Want to sign the DPA bilaterally, or have a security questionnaire to fill out? The full TOMs are at /settings/legal/toms; for everything else email oshu@beyondsimulations.com.