Access Control (RLS on Storage)
Objects aren’t table rows, so AnvilBase doesn’t run Postgres RLS policies against them. Instead, storage offers a simple, deterministic owner-prefix model that covers the most common Supabase pattern: per-user folders.
The owner-prefix model (v1)
A bucket opts into owner-prefix access control by naming it with a user-
prefix (e.g. user-files, user-uploads). For such a bucket:
- Objects live under
<bucket>/<user_id>/..., where<user_id>is the authenticated user’s JWTsub. - A user can read, write, list, sign, move, and copy only objects under their
own
<user_id>/prefix. - Access outside the caller’s prefix returns
403 Forbidden. - An anonymous caller (an
anonkey with no user) is rejected on these buckets. - The
service_rolekey bypasses the check entirely — use it only in trusted server code.
Buckets without the user- prefix are unaffected: their access is governed by
your application logic, signed URLs, and the
bucket’s public flag, exactly as before.
Example
With a bucket user-files:
Caller (JWT sub) | Path | Result |
|---|---|---|
alice | user-files/alice/report.pdf | allowed |
alice | user-files/bob/report.pdf | 403 |
| anonymous | user-files/alice/report.pdf | 403 |
service_role | any path | allowed (bypass) |
// Signed in as alice; her own folder is readable/writableawait db.storage.from('user-files').upload('alice/report.pdf', bytes)await db.storage.from('user-files').download('alice/report.pdf')
// Listing is scoped to the caller's prefix automaticallyawait db.storage.from('user-files').list() // only alice/* entriesBeyond owner-prefix: the storage policy engine
Owner-prefix is the zero-config default. For arbitrary per-path / per-role
rules — the Supabase CREATE POLICY … ON storage.objects model — AnvilBase now
ships a storage policy engine: admin-authored,
per-bucket, per-operation rules whose boolean expressions reference
auth.uid() / auth.role() / auth.jwt() / bucket_id / name.
The two models compose cleanly and additively:
- A bucket with no storage policies behaves exactly as described above
(owner-prefix v1 for
user-buckets, unrestricted otherwise) — adding the engine changes nothing for existing buckets. - As soon as a bucket has one or more policies for an operation, those policies decide access (permissive-OR), and the owner-prefix fallback no longer applies to that operation.
service_rolebypasses both models.
See Storage policies for the full model, expression reference, and the v1 list-scoping limitation.
Combining with other controls
- Owner-prefix RLS applies to a private bucket. Making a
user-bucket public would expose every owner’s objects through the unauthenticated public/render routes, so AnvilBase rejects it by construction: creating or updating auser--prefixed bucket withpublic: truereturns400. - The render/image
authenticatedvariant honors the same owner-prefix check.