Skip to main content
View as Markdown

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.

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.