Content-Addressable Storage with Deduplication
**Problem:** Without content-addressable storage, identical artifacts are stored multiple times, inflating costs. The current container registry uses instance-wide deduplication which creates expensive cross-partition GC operations. The current package registry has no deduplication at all.
**Scope:** Implement the blob storage layer with organization-scoped content addressing and deduplication, following the hashed storage layout defined in the blueprint.
**Key deliverables:**
- `blob_storage_blobs` and `blob_storage_attachments` tables with organization-scoped unique constraints on SHA256/SHA1
- Hashed storage path layout: `artifacts/{org_hash_shard}/{org_hash}/objects/{obj_hash_shard}/{obj_hash}`
- Two-phase upload flow (temporary upload → content-addressed final location)
- Deduplication check on publish: verify digest exists within org before creating new blob
- Reference counting via `blob_storage_attachments` for safe garbage collection
- Dedicated `artifact_registry` object storage bucket configuration
- Garbage collection worker: org-scoped, processes orphaned blobs after all attachments removed
- Backend support for GCS, S3, and Azure Blob Storage
**Acceptance criteria:**
- Uploading identical content within an org results in a single physical blob with multiple attachments
- Uploading identical content across orgs results in separate physical blobs (org isolation)
- GC worker correctly identifies and removes orphaned blobs without impacting referenced content
- Storage path structure matches the blueprint's hashed layout
- Move/rename operations validated against all supported backends
**Dependencies:** Object storage configuration, backend-specific adapter evaluation
epic