Adventure Labs Logo
Scout Docs

Storage Access Control

Policies for interacting with stored files.

Storage objects RLS uses private.has_herd_ability(auth.uid(), herd_id, ability_key) where herd_id comes from get_herd_id_from_path(name). Device API keys use path helpers. Legacy has_good_* / public.role removed (migration 113).

events bucket

Operationhas_herd_ability ability keyOther paths
SELECTmedia.readOwning device API key in herd
INSERTmedia.writeOwning device API key in herd
UPDATEmedia.writeOwning device API key in herd
DELETEmedia.writeOwning device API key in herd

artifacts bucket

Operationhas_herd_ability ability keyOther paths
SELECTmedia.readOwning device API key in herd
INSERTmedia.writeOwning device API key in herd ({device_id}/…)
UPDATEmedia.writeOwning device API key in herd
DELETEmedia.writeOwning device API key in herd

artifact_thumbnails / artifact_proxies / event_thumbnails / event_proxies / embedding_thumbnails buckets

All five resolve through private.media_derivative_object_access, which parses the device id out of the {device_id}/… path. Writes also admit approved gateway keys, matching the artifacts and events table policies:

Operationhas_herd_ability ability keyOther paths
SELECTmedia.readOwning device API key in herd
INSERTmedia.writeOwning device API key or approved gateway in herd
UPDATEmedia.writeOwning device API key or approved gateway in herd
DELETEmedia.writeOwning device API key or approved gateway in herd

Also: service_role full access; platform superadmin all.

Document buckets

One bucket per document owner type. Objects live at {owner_id}/{file}, and the public.documents row stores the bucket-qualified path in uri. All five resolve through private.document_object_access, which parses the owner id out of the path and defers to private.document_access — the same predicate the documents table policies use.

BucketOwner columnPath segmentAbility
user_documentsuser_iduser uuidSelf, or user_documents.<op> in a herd the owner is active in
herd_documentsherd_idherd idherd_documents.<op>
device_documentsdevice_iddevice iddevice_documents.<op> on the device's herd
product_documentsproduct_idproduct idproduct_documents.<op> in any herd
operating_context_documentsoperating_context_idoperating context idoperating_contexts.<op> on the context's herd

All five document buckets accept any MIME type.

Products are not herd-scoped, so product_documents.<op> resolves through private.has_herd_ability_any_herd against any active membership rather than one herd.

SELECT on user_documents and device_documents is broadened by private.document_shared_related_read for shared templates and related herds. Platform superadmins bypass all five. There is no UPDATE policy on any of them; see Invariants below.

compliance_resources bucket

Regulator publications, map layers, and blank forms for a localization, at {compliance_resource_id}/{file}. Not path-scoped to a herd.

OperationRequirement
SELECToperating_contexts.read in any herd, or platform superadmin
INSERT / UPDATE / DELETEPlatform superadmin

SELECT matches public.compliance_resources, so the row and its bytes answer to one ability.

herd_credentials / user_credentials buckets

Objects at {herd_id}/{file} and {user_id}/{file} respectively.

BucketSELECTINSERT / UPDATE / DELETE
herd_credentialscredentials.read on the path's herdcredentials.write on the path's herd
user_credentialsSelf, or credentials.read in a herd the owner belongs toSelf, or credentials.write in a herd the owner belongs to

session_incidents / maintenance_requests buckets

Both admit device API keys as well as users, so their policies are not restricted to the authenticated role.

BucketPathRequirement
session_incidents{session_id}/{file}content.<op> on the session's herd, or the owning device key or approved gateway
maintenance_requests{device|part}/{id}/{file}maintenance_requests.<op> on the target's herd, or the owning device key or approved gateway

Platform superadmin all on both.

user_photos bucket

Objects at {user_id}/{file}. SELECT for the owner, herd co-members, or a platform superadmin; INSERT, UPDATE, and DELETE for the owner or a platform superadmin.

product_images bucket

Objects at {product_id}/{file}. DB column products.image_uri stores the bucket-qualified path.

OperationRequirement
SELECTAny authenticated user
INSERT / UPDATE / DELETEPlatform superadmin

Writes match public.products, whose INSERT, UPDATE, and DELETE are also superadmin-only.

software_build_artifacts bucket

OperationRequirement
SELECTherds.read in any herd (has_view_role_any_herd), or platform superadmin
INSERT / UPDATE / DELETEPlatform superadmin

Writes match public.versions_software, whose INSERT, UPDATE, and DELETE are also superadmin-only. Reads on that table remain open to every authenticated user.

Invariants

  • documents.uri matches its owner. Enforced by the documents_uri_matches_owner CHECK: the value is always {bucket}/{owner_id}/{file}, where the bucket is fixed by whichever owner column is set. Table RLS and storage RLS therefore resolve the same herd for a given row. Inserting the bare {owner_id}/{file} object key is still accepted — private.documents_normalize_uri prepends the bucket, since the owner column already determines it.
  • Document contents are frozen at upload. The five document buckets have no UPDATE policy, and private.documents_content_immutable rejects changes to uri and size_bytes. Superseding a document means inserting a new row and retiring the old one via lifecycle.
  • Approval is a separate ability for user documents. Setting is_approved, approved_by, or approved_at — or moving valid_from / valid_to on an already approved user document — requires document_approve.binary in a shared herd, enforced by private.documents_approval_guard. Holding user_documents.write is not enough. Herd, device, product, and operating-context documents are approved on insert by the same guard and follow their owner's write ability.
  • Approved user documents cannot be destroyed by their owners. The documents DELETE policy requires user_id IS NULL OR NOT is_approved, and the document buckets refuse DELETE on an object an approved user-document row points at. Platform superadmins retain both.

public.events / public.artifacts rows use the same media.read / media.write keys on the device herd.

On this page