Skip to content

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 JWT sub.
  • 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 anon key with no user) is rejected on these buckets.
  • The service_role key 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)PathResult
aliceuser-files/alice/report.pdfallowed
aliceuser-files/bob/report.pdf403
anonymoususer-files/alice/report.pdf403
service_roleany pathallowed (bypass)
// Signed in as alice; her own folder is readable/writable
await 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 automatically
await db.storage.from('user-files').list() // only alice/* entries

Beyond 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_role bypasses 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 a user--prefixed bucket with public: true returns 400.
  • The render/image authenticated variant honors the same owner-prefix check.