Where data lives
What this covers / who it's for. The physical homes of platform data — the primary database, document storage, audit exports, mail — what protects each, and which data leaves the platform for the provider. Read this to answer "where is X stored, who can reach it, and is that posture live today or deploy-bound".
The map
| Data | Where it lives today | Protection | Ship-state notes |
|---|---|---|---|
| All domain data (tenants, users, accounts, payouts, cases, mirrors, logs) | One PostgreSQL database | Tenant isolation enforced by the database itself (Row-Level Security): a tenant's session physically cannot read another tenant's rows (Tenancy & RLS) | Live |
| Document bytes (KYB/KYC uploads) | A private local-disk store (the configured documents disk) | Per-file envelope encryption: each file sealed under its own AES-256-GCM key; two integrity checks on every read (documents README) | Live. In production the disk is intended to be a private S3 bucket — deploy config |
| Document key wrapping (the KEK) | The application key, via a local key manager | Each file key is wrapped before storage; destroying a file's key makes its ciphertext permanently unrecoverable (the RTBF handle — Data lifecycle guarantees) | AWS-KMS is an unimplemented seam — the abstraction exists, the real KMS client is not built. Do not describe the platform as KMS-backed today (app-modules/documents/config/documents.php) |
| Direct-to-object-storage document transfer | An S3-compatible bucket (MinIO locally) | Bucket-default SSE-KMS encryption is the stated production posture (deploy config); presigned, short-lived upload/download URLs | Ships disabled (documents.direct_transfer.enabled = false); the endpoints refuse until an environment turns it on |
| Audit WORM export (tamper-evident off-platform copy) | A write-once (Object-Lock) bucket | Segment-signed, independently verifiable off-platform | Ships disabled until the bucket is provisioned (app-modules/audit/config/audit.php); the in-DB audit chain is live regardless |
| Statement / export artifacts | Object storage under the payments module's keys | Downloads only via the app's own permission-gated route — never a raw storage link (payments README) | Live |
| Cache, queues, rate cache | Valkey (Redis-compatible) | Transient operational state only — losing it loses queued-but-unprocessed jobs and warm caches, not domain records | Live |
| Outbound email | Env-driven mail transport | Local dev delivers to Mailpit; production transport is chosen per environment — SMTP, SES, Postmark, and Resend are all available (config/mail.php) | Transport choice is deploy config |
Documents: the honest posture
The document vault's guarantees are worth stating precisely, because they are the strongest and the most misquoted (documents README):
- Envelope encryption is live. Every stored document is sealed under its own AES-256-GCM data key; the wrapped key travels in the document's metadata. Tampering with the ciphertext fails the read (auth tag + a second content-hash check) rather than serving corrupted bytes.
- The key manager is local today. The per-file keys are wrapped with the application key. Real AWS-KMS (a cloud CMK as the key-encryption key) is a drop-in implementation of the same seam and is not built — it is deploy work, not a config flip.
- Downloads are never a raw file link. Every download is a signed URL valid for roughly
90 seconds (
documents.download_url_ttl = 90), and every serve is logged — one row per download in the document access log (document_accesses), feeding the DLP egress trail. - The direct-transfer path exists but is off. When enabled, clients move bytes straight to and from the bucket via presigned URLs (upload URLs live 300 seconds) and the app keeps issuing credentials, logging every access, and owning lifecycle. Until enabled, all bytes proxy through the app.
- Malware-scan quarantine also ships off — with it off, uploads are admitted immediately; with it on, documents are quarantined until scanned (a config decision, same README).
What reaches the provider
The platform forwards to Conduit exactly what the rail needs to run KYB/KYC and money movement:
- KYB/KYC data — business and person details submitted during onboarding (onboarding README).
- KYB/KYC documents — stored in our vault first, then forwarded asynchronously (Async by design).
Once forwarded, the provider holds its own copies under its own retention and its KYC vendor's — deleting or crypto-shredding our copy does not reach theirs. That boundary matters for any privacy or data-subject conversation (Providers & rails).
What never leaves
Teams, roles and permissions, notification preferences and the comms log, compliance cases and notes, the audit trail, DLP logs, drift records, and every platform-only construct live solely in our PostgreSQL — the provider has no view into them.