File Operations
Every bucket exposes a REST API for the core file lifecycle: upload, download, and delete. Operations are authenticated against the bucket's permissions, hardened against path-traversal and XSS, and integrated with Sovrium's soft-delete pattern.
The {bucket} path parameter references a bucket name declared in your buckets[] array. See Buckets Overview.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/buckets/{bucket}/files |
Upload a file (multipart form data). Returns 201 with the generated file key. |
GET |
/api/buckets/{bucket}/files/{key} |
Download a file by key. Returns the file content with the correct Content-Type. |
DELETE |
/api/buckets/{bucket}/files/{key} |
Delete a file by key from storage. |
GET |
/api/admin/buckets/quota |
(Admin) Return current storage usage in bytes. |
Upload
Send the file as multipart form data. The server validates it against the bucket's maxFileSize and allowedMimeTypes, generates an unpredictable key, and stores it in the configured backend.
curl -X POST https://app.example.com/api/buckets/documents/files \
-H "Authorization: Bearer $TOKEN" \
-F "file=@report.pdf"
{
"key": "documents/9f3c1e2a-7b44-4d10-9e21-8a6f0c1d2e3b.pdf",
"filename": "report.pdf",
"size": 245760,
"mimeType": "application/pdf"
}
| Behavior | Result |
|---|---|
| Valid file within limits | 201 with the generated file key. |
| Empty request body | 400 Bad Request. |
| Exceeds bucket or global max size | 413 Payload Too Large (rejected early, before the full body is read). |
Exceeds STORAGE_MAX_TOTAL_SIZE quota |
507 Insufficient Storage. |
| Unauthenticated request to an authenticated bucket | 401 Unauthorized. |
Keys are randomized. Uploads always receive a UUID-based key, so files never overwrite one another and storage URLs are unguessable. Two uploads of the same filename produce two distinct keys.
Download
curl https://app.example.com/api/buckets/documents/files/{key} \
-H "Authorization: Bearer $TOKEN" -O
| Behavior | Result |
|---|---|
File exists, caller has download permission |
200 with file content and correct Content-Type. |
| Non-existent key | 404 Not Found. |
| Private file, no auth and no signed URL | 403 Forbidden. |
Responses include a Content-Disposition header — non-image files are served as attachment to prevent the browser from rendering them inline.
Delete
curl -X DELETE https://app.example.com/api/buckets/documents/files/{key} \
-H "Authorization: Bearer $TOKEN"
| Behavior | Result |
|---|---|
File exists, caller has delete permission |
200, file removed from storage. |
| Non-existent key | 404 Not Found. |
Security Hardening
File operations apply the recurring pre-launch security baseline for uploads automatically:
| Measure | Behavior |
|---|---|
| Path traversal | ../ sequences in filenames are sanitized or rejected. |
| Null bytes | Filenames containing null bytes are rejected. |
| Special / unicode characters | Encoded safely for storage. |
| Randomized keys | UUID-based keys prevent guessable URLs. |
X-Content-Type-Options: nosniff |
Sent on every served file. |
Content-Security-Policy |
Sent on every served file. |
| XSS-prone types (HTML, SVG) | Served with a safe Content-Type or forced to download as an attachment. |
File Lifecycle
Attachments follow Sovrium's soft-delete-by-default lifecycle. File presence tracks the owning record:
| Action on the record | File behavior |
|---|---|
| Soft-delete record | File preserved in storage. |
| Restore record | File access restored. |
| Hard-delete (purge) | File removed from storage. |
| Replace attachment | Old file removed from storage. |
Clear attachment (set null) |
File removed from storage. |
| Shared reference | File preserved while any other record references it. |
Large Uploads & Quota
Uploads stream rather than buffering wholesale, so large files do not spike memory. Limits are operator-controlled via environment variables.
| Variable | Default | Purpose |
|---|---|---|
STORAGE_MAX_FILE_SIZE |
104857600 (100 MB) |
Maximum single file size in bytes. A bucket's maxFileSize overrides this per-bucket. |
STORAGE_MAX_TOTAL_SIZE |
Unlimited | Maximum total storage per application in bytes (multi-tenant safety). |
STORAGE_MAX_FILE_SIZE=52428800
STORAGE_MAX_TOTAL_SIZE=10737418240
The admin quota endpoint reports usage:
curl https://app.example.com/api/admin/buckets/quota \
-H "Authorization: Bearer $ADMIN_TOKEN"
{ "usedBytes": 524288000, "maxBytes": 10737418240 }
Deleting files reduces tracked usage. When no STORAGE_MAX_TOTAL_SIZE is configured, uploads are unrestricted.
Related Pages
- Buckets Overview — declaring buckets, backends, and permissions.
- Signed URLs — time-limited download and direct browser uploads.
- Image Transforms — resizing and reformatting on download.
- Attachment Fields — files attached to table records.
- Form File Uploads — accepting uploads through forms.
- Environment Variables — size and quota configuration.