Object Storage Key Governance
Mandate owner-scoped, hierarchical object-storage (R2/S3) keys so every stored binary has a deterministic, traceable, owner-scoped URL
Tags
Overview
Purpose
Mandate owner-scoped, hierarchical object-storage (R2/S3) keys so every stored binary has a deterministic, traceable, owner-scoped URL
Rules
OSK-001: Every object-storage key MUST be owner-scoped and hierarchical: {owner}/{ownerId}/{facet}/{objectId}.{ext} — {owner} = owning entity collection name (plural, matching table/route); {ownerId} = that entity UUID; {facet} = asset role within the owner (cover, gallery, photo, qr); {objectId} = object UUID; {ext} = stored format.
Owner-scoped prefixes give per-entity prefix listing, prefix-based lifecycle/cleanup (delete entity → delete its prefix), self-describing traceable URLs, and good request partitioning. The public URL is literally https://{cdn}/{key}, so a correct key IS a correct URL.
Verification: buildKey / UploadInput require owner+ownerId+facet; grep object keys — none match a flat ^{word}/{uuid} pattern.
OSK-002: System/brand assets with no entity owner use the reserved owner namespace "system": system/{facet}/{objectId}.{ext} (e.g. system/watermark/{id}.png). There is NO "general/" catch-all and NO flat {purpose}/{uuid} — an unowned object is a domain-modeling gap, not a storage category.
Forces every asset to declare an owner at upload time, eliminating the orphan-image class of bug.
Verification: ObjectStore consumers pass an explicit owner; no "general" prefix/purpose exists.
OSK-003: FDD2.PERSIST MUST produce an object-key/URL scheme (conforming to OSK-001/002) for any entity that owns binary assets, and it MUST be validated against this rule before implementation.
The flat scheme slipped in because the persistence-model step was read as "DB schema only". Object-key/URL design IS part of the persistence model for media-owning entities.
Verification: The page-model / persistence artifact lists the conforming key scheme for every media-owning entity in scope.