Architecture: How should R2 buckets be organized — per MongoDB database, p...
Documentation for Sessions
🏗️ Architecture: How should R2 buckets be organized — per MongoDB database, p...
Date: 2026-03-10
Time: 17:59:22 UTC
Session: gspace1-198.5
Agent: backend
Confidence: 93%
Status: ✅ Documented
📋 Decision Context
Question:
How should R2 buckets be organized — per MongoDB database, per space, or by domain?
Task: S198.1: Multimodal vision + Cloudflare R2 storage
🎯 Decision Made
Chosen:
6 buckets organized by domain boundaries with different lifecycle/access policies. NOT per-MongoDB-database (most DBs have zero binary data). NOT per-space (1000 bucket hard limit from Cloudflare). Space isolation via key prefixes using unique spaceSlug.
Options Considered:
- 1:1 R2 bucket per MongoDB database
- Per-space R2 bucket
- Single bucket with key prefixes
- Domain-based buckets (6) with spaceSlug key prefixes
- Match MongoDB databases selectively (only those with binary data)
💭 Reasoning
1:1 MongoDB mapping was rejected because most databases (ai-agents, ai-mcp-dev, ai-context, ai-memory, gograb-analytics, gograb-core, vitto-mind) contain zero binary data — creating empty buckets adds configuration overhead with no value. Per-space buckets hit Cloudflare's hard 1000 bucket limit. Domain-based buckets work because: different TTLs (chat-media permanent, gen 30d, backups 90d), different access control (assets admin-only, space-production rate-limited), different compliance (GDPR for end-user data). Space isolation achieved via spaceSlug key prefix — verified safe because spaceSlug has a unique MongoDB index with collision handling (appends timestamp). Future premium tenants get isolated gograb-tenant-{spaceId} buckets with separate credentials.
📁 Files Involved
libs/libs-api/r2-storage-core/src/lib/r2-storage.constants.tsAPIs/backend-api/src/app/spaces/spaces.schema.ts
🔗 Related
- Decision ID:
dev_1773165562388_8jvgjp8 - Session: 198
- Workspace: gspace1
Auto-generated by MCP Smart Consolidation