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
| Operation | has_herd_ability ability key | Other paths |
|---|---|---|
| SELECT | media.read | Owning device API key in herd |
| INSERT | media.write | Owning device API key in herd |
| UPDATE | media.write | Owning device API key in herd |
| DELETE | media.write | Owning device API key in herd |
artifacts bucket
| Operation | has_herd_ability ability key | Other paths |
|---|---|---|
| SELECT | media.read | Owning device API key in herd |
| INSERT | media.write | Owning device API key in herd ({device_id}/…) |
| UPDATE | media.write | Owning device API key in herd |
| DELETE | media.write | Owning 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:
| Operation | has_herd_ability ability key | Other paths |
|---|---|---|
| SELECT | media.read | Owning device API key in herd |
| INSERT | media.write | Owning device API key or approved gateway in herd |
| UPDATE | media.write | Owning device API key or approved gateway in herd |
| DELETE | media.write | Owning 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.
| Bucket | Owner column | Path segment | Ability |
|---|---|---|---|
user_documents | user_id | user uuid | Self, or user_documents.<op> in a herd the owner is active in |
herd_documents | herd_id | herd id | herd_documents.<op> |
device_documents | device_id | device id | device_documents.<op> on the device's herd |
product_documents | product_id | product id | product_documents.<op> in any herd |
operating_context_documents | operating_context_id | operating context id | operating_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.
| Operation | Requirement |
|---|---|
| SELECT | operating_contexts.read in any herd, or platform superadmin |
| INSERT / UPDATE / DELETE | Platform 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.
| Bucket | SELECT | INSERT / UPDATE / DELETE |
|---|---|---|
herd_credentials | credentials.read on the path's herd | credentials.write on the path's herd |
user_credentials | Self, or credentials.read in a herd the owner belongs to | Self, 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.
| Bucket | Path | Requirement |
|---|---|---|
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.
| Operation | Requirement |
|---|---|
| SELECT | Any authenticated user |
| INSERT / UPDATE / DELETE | Platform superadmin |
Writes match public.products, whose INSERT, UPDATE, and DELETE are also superadmin-only.
software_build_artifacts bucket
| Operation | Requirement |
|---|---|
| SELECT | herds.read in any herd (has_view_role_any_herd), or platform superadmin |
| INSERT / UPDATE / DELETE | Platform 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.urimatches its owner. Enforced by thedocuments_uri_matches_ownerCHECK: 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_uriprepends 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_immutablerejects changes touriandsize_bytes. Superseding a document means inserting a new row and retiring the old one vialifecycle. - Approval is a separate ability for user documents. Setting
is_approved,approved_by, orapproved_at— or movingvalid_from/valid_toon an already approved user document — requiresdocument_approve.binaryin a shared herd, enforced byprivate.documents_approval_guard. Holdinguser_documents.writeis 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
documentsDELETE policy requiresuser_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.
Related table RLS
public.events / public.artifacts rows use the same media.read / media.write keys on the device herd.