Security & regulatory posture
How Caelith protects regulated fund-manager data, evidences its claims, and constrains its AI surface. This page is maintained alongside the codebase. Last updated: 2026-04-25.
IsolationRow-Level Security on every query
- PostgreSQL
SET LOCAL app.tenant_idbinds every connection to its tenant before any data query runs. Cross-tenant reads are refused by the database tier under Row-Level Security policies — not relied on at the application tier alone. Verified by the unit invariant suite + cross-tenant e2e specs. - Application-level helpers
queryWithTenant()andexecuteWithTenant()enforce a tenant clause and set the RLS context together. Directpool.query()calls are forbidden for tenant data. - JWT auth middleware rejects any token missing
tenantId. There is no anonymous path to tenant data. - RLS is enforced by default in every environment. Local development requires an explicit
RELAX_RLS_ON_STARTUP=1opt-out.
Tamper-detectabilitySHA-256 append-only audit chain
Every sealed compliance decision is hashed and linked to its predecessor. The structure is identical to a blockchain primitive: integrity_hash = SHA-256(canonical_record + previous_hash).
- A single edit to any record breaks every downstream hash and is detected on the next chain verification pass.
- Internal control, not third-party-attested. The chain is an internal tamper-detection primitive: an honest verifier with read access can re-compute SHA-256 to detect post-hoc modification. It is not externally timestamped (no RFC 3161 TSA anchor, no eIDAS-qualified electronic signature per Reg. (EU) 910/2014 Arts. 25-34 and no qualified electronic timestamp per Art. 42) and is not, on its own, regulator-admissible without independent third-party attestation. We expose per-tenant verification via
GET /integrity/verify(authenticated, scoped to the caller's tenant). A public anonymous verification endpoint with anchored timestamping is on the roadmap; until it ships we do not assert public verifiability. - The genesis block uses an explicit zero-hash so the chain has no implicit anchor. Records cannot be retroactively prepended.
- PII fields in audit records are pseudonymized (logged as
{changed: true}rather than full values) per GDPR Art. 32(1)(a).
AuthenticationCookies, rotation, lockout
- JWT access token (30 min) plus rotated refresh token (7 days). Both delivered via
httpOnlySameSite=Laxcookies, secure flag in production. JavaScript cannot read them. - Passwords hashed with bcrypt (cost ≥ 10). No plaintext storage; no reversible derivation.
- Account lockout: 5 attempts per IP, 25 globally per email within 15 minutes.
- Sandbox sessions issue a per-visit
sandboxSessionIdembedded in the JWT for audit attribution. Sandbox tokens expire after 2h and the tenant is auto-cleaned on the next login.
ResidencyEU-West only
- Application and database hosted on Railway in the EU-West region (Netherlands (EU-West region)). Data does not leave the EU.
- Postgres connections enforce SSL/TLS; the
PG_SSL_REJECT_UNAUTHORIZED=falseexemption is required only because Railway internal proxies use self-signed certs — the underlying transport is still encrypted. - Sub-processors are listed in a single canonical registry at
docs/legal/sub-processors.md(authoritative for this page, /privacy, /dpa, /trust, anddocs/legal/dpa-template.md). Currently active: Anthropic (AI inference, USA endpoint, EU-US DPF), Railway (hosting, EU-West Netherlands (EU-West region)), OpenAI (optional embeddings only), Sentry (conditional error monitoring), OpenSanctions (sanctions screening, EU-based — mocked in sandbox), Plausible (cookieless analytics, EU). Previously evaluated but no longer active: MiniMax (reverted 2026-05-17 before any production data was processed); full audit trail and re-disclosure conditions per GDPR Art. 28(2) recorded in canonical registry §2.
AI governanceEU AI Act Art. 13/14 transparency
- Caelith's agent provides assistive automation for compliance workflows. It does not constitute legal, regulatory, or compliance advice. All outputs require independent verification by a qualified professional.
- Destructive or irreversible actions (create filing, submit filing, mutate task) require explicit human confirmation via a four-tool gate before execution.
- Every agent action is recorded in the tamper-detectable audit chain with the prompt, the model, the inputs considered, and the outcome.
- The agent does not train on tenant data. Anthropic is configured for zero-retention inference where supported.
- Sanctions screening output carries explicit
_provider_mode,_match_type, and_disclaimerfields so consumers can't accidentally treat heuristic matches as authoritative.
NetworkEdge controls
- HSTS preload, Content-Security-Policy, Permissions-Policy, Referrer-Policy, X-Content-Type-Options, X-Frame-Options DENY, COOP same-origin, X-Request-Id are all set on every response. Verifiable from any browser DevTools tab.
- Rate limits run on a shared DB-backed store so they hold consistently across Railway replicas, not per-process memory.
- The
/api/auth/meendpoint distinguishes 401/403 (genuine auth failure) from 5xx/network errors so a transient backend blip does not silently log a visitor out.
Filing pipelineState machine + four-eyes gate
- Workflow states:
draft → validated → pending_approval → ready_to_submit → submitted → acknowledged → accepted/rejected. Every transition is recorded with actor, timestamp, and hash entry. - Annex IV XML is validated against the official ESMA schema (
AIFMD_DATAIF_V1.2.xsd) before any submission attempt. - BaFin filing uses WS-Security SOAP with a
dryRunmode for non-production tenants. Sandbox tenants always operate indryRun; outputs are valid XML but never reach a real regulator. - Eight NCAs supported: DE and LU automated; AT, FR, NL, IE, IT, ES manual XML upload.
Open sourceAnnex IV serializer
The Annex IV XML serializer (Zod schemas, XSD validation, 107 unit tests) is published as an Apache-2.0 standalone package at packages/open-annex-iv. Any AIFM, vendor, or regulator can audit, reuse, or fork it independently of Caelith.
Last verified against code on 2026-05-20.
ReportingVulnerability disclosure
Security issues: security@caelith.tech. We commit to acknowledging receipt within 48h. Coordinated disclosure preferred; we will not pursue legal action against good-faith research that respects user data and service availability.