--- phase: 17-object-storage plan: 01 subsystem: infra tags: [s3, minio, aws-sdk, object-storage, docker, presigned-urls] # Dependency graph requires: [] provides: - "S3 storage service (uploadImage, deleteImage, getImageUrl, withImageUrl, withImageUrls)" - "MinIO in Docker Compose (dev and prod) with automatic bucket creation" - "S3 environment variable configuration" affects: [17-02, 17-03, image-routes, image-service, mcp-tools] # Tech tracking tech-stack: added: ["@aws-sdk/client-s3@3.1024.0", "@aws-sdk/s3-request-presigner@3.1024.0", "MinIO (quay.io/minio/minio:RELEASE.2025-09-07T16-13-09Z)"] patterns: ["S3Client singleton with forcePathStyle", "Presigned URL injection via withImageUrl/withImageUrls helpers", "Docker Compose init container for bucket creation"] key-files: created: ["src/server/services/storage.service.ts", "tests/services/storage.service.test.ts"] modified: ["docker-compose.yml", "docker-compose.dev.yml", ".env.example"] key-decisions: - "Private bucket with presigned URLs (1h default, configurable via S3_PRESIGN_EXPIRY)" - "MinIO pinned to quay.io RELEASE.2025-09-07T16-13-09Z (last stable before archival)" - "No console port exposed in production compose" patterns-established: - "S3 storage functions are pure async functions, no HTTP awareness" - "withImageUrl/withImageUrls helpers enrich records with presigned imageUrl field" - "Docker init container pattern using mc CLI for bucket setup" requirements-completed: [IMG-01, IMG-04] # Metrics duration: 2min completed: 2026-04-05 --- # Phase 17 Plan 01: S3 Storage Service and MinIO Infrastructure Summary **S3 storage abstraction with uploadImage/deleteImage/getImageUrl using @aws-sdk/client-s3, plus MinIO in Docker Compose with automatic bucket creation** ## Performance - **Duration:** 2 min - **Started:** 2026-04-05T10:14:02Z - **Completed:** 2026-04-05T10:16:24Z - **Tasks:** 2 - **Files modified:** 7 ## Accomplishments - Storage service wrapping @aws-sdk/client-s3 with forcePathStyle for MinIO compatibility - Presigned URL helpers (withImageUrl, withImageUrls) for enriching API responses - MinIO in both Docker Compose files with mc init container for automatic bucket creation - S3 environment variable documentation in .env.example ## Task Commits Each task was committed atomically: 1. **Task 1: Install S3 SDK and create storage service** - `f845f87` (feat) 2. **Task 2: Add MinIO to Docker Compose and update env config** - `88f988c` (chore) ## Files Created/Modified - `src/server/services/storage.service.ts` - S3 storage abstraction with 5 exported functions - `tests/services/storage.service.test.ts` - 8 unit tests with mocked S3Client - `docker-compose.dev.yml` - Added MinIO + minio-init with fixed dev credentials - `docker-compose.yml` - Added MinIO + minio-init with env var credentials, removed uploads volume - `.env.example` - Added S3 configuration section - `package.json` - Added @aws-sdk/client-s3 and @aws-sdk/s3-request-presigner - `bun.lock` - Updated lockfile ## Decisions Made - Private bucket with presigned URLs (no public-read) for security - 1-hour presigned URL expiry default, configurable via S3_PRESIGN_EXPIRY env var - No console port (9001) exposed in production compose, only API port (9000) - Dev compose uses fixed minioadmin/minioadmin credentials for simplicity - Production compose removed uploads volume (replaced by MinIO object storage) ## Deviations from Plan None - plan executed exactly as written. ## Issues Encountered None ## User Setup Required None - no external service configuration required. MinIO starts automatically via Docker Compose. ## Next Phase Readiness - Storage service ready for Plan 02 (image route refactoring) to call uploadImage/deleteImage/getImageUrl - withImageUrl/withImageUrls helpers ready for API response enrichment - Docker Compose MinIO available for integration testing --- *Phase: 17-object-storage* *Completed: 2026-04-05*