The API reference shows the action on each endpoint’s own page. Both come
from the same place: the authorization call in the service, read at build
time.
Volumes, snapshots and policies
These follow their endpoint names, and the resource is the CRN of the volume,
snapshot or policy being acted on.
Buckets and objects
The object side takes its action names from S3, so several of them do not match the endpoint they guard. These are the ones worth reading before writing a policy.Four things the names do not tell you
Listing objects needsstorage:ListBucket, not a ListObjects action. The
same action covers listing object versions and listing multipart uploads. This
is S3’s naming: the action is named for the thing you are listing inside, not
for what comes back.
Listing your buckets is storage:ListAllMyBuckets. It is checked against
the account, so it cannot be scoped to particular buckets — granting it lets a
caller see that every bucket exists, and nothing more.
Removing a bucket sub-resource takes the Put action, not a delete action.
DELETE /v1/buckets/{bucket}/cors requires storage:PutBucketCORS; the same
holds for lifecycle, encryption, tagging and object-lock. Clearing a
configuration is writing it to empty.
DELETE on an object key has two answers. Deleting the object needs
storage:DeleteObject, but DELETE /v1/buckets/{bucket}/objects/{key}?tagging
— which removes the object’s tags and leaves the object — needs
storage:PutObject. Removing tags is a mutation of the object, not a deletion
of it. A credential granted storage:DeleteObject alone will find the tagging
form refused.
Two layers on the object side
Every object request is decided by IAM and by the bucket policy, in this order:1
Anonymous callers
The bucket policy alone decides. No policy means no access.
2
Authenticated callers
An explicit
deny in either layer refuses the request outright. Failing
that, an explicit allow from either layer is enough to proceed. With
neither, the request is refused — an implicit deny.Resources
Storage CRNs are name-based and carry the region and account:bucket/reports/2026/* — the same way it scopes to a naming
convention on volumes: crn:storage:*:my-account:volume/prod-*.
Conditions
Volumes, snapshots and policies take atags map, and both tag context keys
apply:
basalt:RequestTag/<key>— what a caller may label a resource as, which is what makes a create authorizable before the resource exists.basalt:ResourceTag/<key>— which existing resources an action may touch.
s3:RequestObjectTag/<key>— what a write is trying to set.s3:ExistingObjectTag/<key>— what is already on the object.basalt:TagKeys— the set of tag keys a request carries.
What a denial looks like
A failed check answers403:
Requests on the S3 protocol endpoint answer in S3’s own error format rather
than this one — see the S3 error codes. The
decision is the same; only the envelope differs.