A bucket has a rule for each action. You set them in Studio when you create the bucket (Who may read, upload new objects, list, replace, delete). This page explains how they behave and how to design keys around them. The rule language is in Access rules; storage specifics are in Rules on storage. The difference between upload and replace: uploading to a key that doesn’t exist uses the upload rule; uploading to one that does uses the replace rule. Use that to let people add files but not overwrite others’.

Design keys around rules

Put the owner first: <user id>/…. Then one custom rule protects the whole bucket:
Compare values… $object.segments.0 equals $auth.user_id
Use it as Who may upload new objects, replace, delete and read for a private bucket. In practice:
For organizations, compare $object.segments.0 with $auth.org and use keys like <org slug>/….

Public, but not browsable

Public bucket lets anyone with a file’s address read it. Set Who may list objects to Signed-in users (or a role) and the bucket still can’t be enumerated: strangers can only see files whose address they hold.

What a refusal looks like

The rule runs before the file is looked up, so a refusal never reveals whether a key exists.

Check a rule

Try something that must fail, signed in as an ordinary person:
If a call fails and you think it shouldn’t, find the request in Studio’s Activity with error.requestId and read which rule decided (Debugging rules).

Studio isn’t bound by the rules

Studio’s file browser acts with its own authority, so a bucket only your server may write to can still be filled from Studio.

Rules on storage

More examples.

Bucket settings

Where rules are set.