Storage Overview
AnvilBase Storage gives each project S3-compatible object storage, isolated in
its own backing bucket (bucket-<project_id>) with its own server-side encryption
key. Use it for user uploads, avatars, documents, exports — anything file-shaped.
The default backend is MinIO; an opt-in overlay swaps in RustFS (a Rust S3 server) — see Installation → Storage backend.
Concepts
- Backing bucket — each project has one physical S3 bucket, isolated and encrypted (SSE-S3).
- Logical buckets — within a project you organize objects into named buckets
like
avatarsordocuments. These are key prefixes inside the project’s backing bucket, so you can have as many as you like without provisioning. Bucket metadata (public flag, size/MIME limits) lives in a per-projectstorage.bucketstable — see Bucket Management. - Object path — the key within a logical bucket, e.g.
avatars/user-123/photo.jpg. - Reserved
user-buckets — a bucket whose id starts withuser-opts into owner-prefix RLS: objects are confined to<bucket>/<user_id>/…and it cannot be made public.
The API
Base path: /v1/storage/<project_id>/.... Core operations:
| Operation | Method & path |
|---|---|
| Upload | PUT /v1/storage/<id>/object/<bucket>/<path> |
| Download | GET /v1/storage/<id>/object/<bucket>/<path> |
| Delete | DELETE /v1/storage/<id>/object/<bucket>/<path> |
| List | POST /v1/storage/<id>/object/list/<bucket> |
| Signed URL | POST /v1/storage/<id>/object/sign/<bucket>/<path> |
| Move / Copy | POST /v1/storage/<id>/object/{move,copy} |
| Public URL | GET /v1/storage/<id>/object/public/<bucket>/<path> (public buckets) |
| Image transform | GET /v1/storage/<id>/render/image/{authenticated,public}/<bucket>/<path> |
| Bucket CRUD | GET/POST /v1/storage/<id>/bucket, GET/PUT/DELETE …/bucket/<bucket>, POST …/bucket/<bucket>/empty |
| Resumable upload (large files) | OPTIONS/POST /v1/storage/<id>/upload/resumable, HEAD/PATCH/DELETE …/upload/resumable/<id> (TUS 1.0.0) |
With the SDK it’s the familiar db.storage.from('<bucket>') API — see
Working with Objects.
Authentication & access control
Storage requests carry your project key (apikey) and, for a user, their JWT.
You have three complementary options:
- Signed URLs — mint a time-limited URL for exactly the object a user owns (the right default for private files). See Working with Objects → Signed URLs.
- Public buckets — flip a bucket’s
publicflag and serve objects over an unauthenticated public URL (avatars, public assets). - Owner-prefix RLS — for a private bucket named
user-…, objects under<bucket>/<user_id>/...are accessible only by that authenticated user;service_rolebypasses. See Access Control.
Use the service_role key only in trusted server code.
Security: content-type handling
To prevent stored-XSS via uploaded files, AnvilBase classifies content types on download:
- Safe inline types (
png,jpeg,gif,webp,avif,pdf,json,text/plain) are served inline. - Dangerous types (
html,svg,xml, …) are forced toContent-Disposition: attachmentand served withX-Content-Type-Options: nosniff, so a browser downloads rather than executes them.
This happens automatically — you don’t configure it, but it’s why an uploaded
.html file won’t render in place.
Encryption
Objects are encrypted at rest with SSE-S3 using a per-project key (configured
by MINIO_KMS_SECRET_KEY). See Encryption. Combine
with TLS in transit at Traefik.
Quotas
Project storage usage is enforced against max_storage_size_mb
(project quotas). The quota is
checked against the incoming object size (Content-Length, or the TUS
Upload-Length for resumable uploads), so a project cannot overshoot its cap by
one object. When a project is over quota, writes return 413 Payload Too Large with a JSON body:
{ "error": "storage_quota_exceeded", "bucket": "…", "used_bytes": 0, "limit_bytes": 0 }Limits
- Direct uploads through the API are buffered, with a per-request size cap of
50 MB. For larger files, use resumable uploads (the TUS 1.0.0 protocol,
backed by S3 multipart) — this lifts the 50 MB cap and uploads big objects in
resumable chunks. The supabase-js resumable
uploadworks drop-in. See Resumable Uploads.
What’s next
- Bucket Management — create/configure/delete buckets and per-bucket upload limits.
- Working with Objects — upload, download, list, delete, move, copy, sign, in every client.
- Resumable Uploads — large files in resumable chunks via the TUS 1.0.0 protocol (supabase-js compatible).
- Public URLs — serve public-bucket objects without auth.
- Access Control — owner-prefix RLS for private buckets.
- Image Transformations — on-the-fly resize/format with imgproxy.