Storage Locations
Technical reference for Certivant's per-file storage locations — how ID documents, certificates, capture photos and call recordings keep pointing at the right bucket or storage account after the platform storage settings or a tenant's storage region change.
Every stored file records the location it was written to, next to its storage key. A location is one concrete place files are kept — an S3 bucket, an Azure storage account and container, or a local folder — identified by a stable id that contains no secrets:
| Provider | Location id |
|---|---|
| AWS S3 / S3-compatible | s3:aws:ca-central-1/kyc-documents, s3:minio.example.com:ca-central-1/kyc |
| Azure Blob (each region of a multi-region setup is its own location) | azure:certivantca/kyc |
| Local disk | local:./storage |
Because reads always go back to the recorded location, these changes never strand old files:
- the super admin changes Settings → Storage (new provider, new bucket, new account);
- a tenant is moved to another storage region (multi-region Azure);
- a storage key is rotated (the location keeps its id; the new key is used).
Where it is recorded
| Table | Column |
|---|---|
kyc_document | storageLocation |
kyc_certificate_of_verification | storageLocation |
kyc_capture_token | storageLocation |
kyc_verification_conference | recordingStorageLocation (the chunks and the assembled recording) |
Each location's connection settings are kept in their own encrypted
SystemSetting row, storage.location:<id>. Rows are never removed — files
may still point at them. Saving the storage settings remembers the old
settings' locations first, then the new ones.
In code
packages/kyc-engine/src/services/storage-location.service.ts:
- Write —
storageForNewFile(organizationId)returns{ storage, location }: the tenant's region when the platform uses multi-region storage, otherwise the configured place. Savelocationwith the key. - Read / delete —
storageForFile(row.storageLocation), or the shortcutsdownloadStoredFile(row),deleteStoredFile(row)andsignedUrlForStoredFile(row)for any row withstorageKey+storageLocation. - A row without a location (saved before locations were recorded) resolves to
where it was written then: the platform default place — or, for
certificates, the tenant's place (
storageForFile(location, organizationId)). - An unknown location throws instead of reading from the wrong place.
- Upload now, save later (public forms upload each file before the
applicant submits) —
locateUploadedFile(organizationId, key)checks the tenant's current place first (fileExists, a metadata check, no download); if the storage settings or the tenant's region changed while the applicant was filling the form, it checks every remembered place and returns the one that has the file. Submit and the upload preflight both use it, so the recorded location is always where the file really is.
Existing data
scripts/backfill-storage-locations.ts records the location of every file
saved before this change (documents, capture photos and recordings → the
default place; certificates → the tenant's place) and remembers the current
locations. It is idempotent — dry run by default, --apply to write:
npx dotenv -c -e .env -- packages/database/node_modules/.bin/tsx scripts/backfill-storage-locations.ts --apply
Run it on each environment before changing the storage settings or a tenant's region for the first time.