Skip to content

Storage Service

The storage service provides tenant-scoped document management using AWS S3.

Source: backend/app/services/storage_service.py


Overview

The storage service handles file uploads, downloads, and management for application documents, company logos, and exported reports. All files are organized by tenant to enforce multi-tenant isolation.


Key Path Convention

All S3 keys follow a tenant-scoped prefix pattern:

s3://{bucket}/tenant-{tenant_id}/{category}/{filename}

For example:

s3://carbon-connect-documents/tenant-abc123/applications/executive-summary.pdf
s3://carbon-connect-documents/tenant-abc123/logos/company-logo.png
s3://carbon-connect-documents/tenant-abc123/reports/carbon-audit-2024.xlsx

This ensures strict tenant isolation at the storage layer.


Features

Feature Description
Upload Upload files with automatic key generation
Download Stream file downloads
Presigned URLs Generate time-limited signed URLs for direct browser access
Delete Remove files by key
List List files within a tenant prefix
Tenant isolation All operations are scoped to tenant ID
Content type detection Automatic MIME type detection from file extension

Usage

from backend.app.services.storage_service import StorageService, get_storage_service

storage = get_storage_service()

# Upload a document
key = await storage.upload_file(
    tenant_id="abc123",
    category="applications",
    filename="proposal.pdf",
    content=file_bytes,
    content_type="application/pdf",
)

# Generate presigned URL (valid for 1 hour)
url = await storage.get_presigned_url(key, expiration=3600)

# Download a file
content = await storage.download_file(key)

# List files in a category
files = await storage.list_files(
    tenant_id="abc123",
    category="applications",
)

# Delete a file
await storage.delete_file(key)

S3 Buckets

The infrastructure provisions two S3 buckets:

Bucket Purpose Lifecycle
documents Application documents, reports, exports Standard -> Intelligent-Tiering -> Glacier
media Company logos, profile images Standard (no lifecycle)

Lifecycle Policies

Document storage follows a cost-optimized lifecycle:

0-30 days:    S3 Standard
30-90 days:   S3 Intelligent-Tiering
90+ days:     S3 Glacier Deep Archive

Configuration

Environment Variable Description Default
AWS_S3_DOCUMENTS_BUCKET Documents bucket name carbon-connect-{env}-documents
AWS_S3_MEDIA_BUCKET Media bucket name carbon-connect-{env}-media
AWS_S3_REGION S3 region eu-north-1
S3_PRESIGNED_URL_EXPIRY Presigned URL expiration (seconds) 3600

Security

  • All buckets have public access blocked by default
  • Files are encrypted at rest using AES-256 (S3 managed keys)
  • Presigned URLs have configurable expiration times
  • IAM policies restrict access to the ECS task role
  • Cross-tenant access is prevented by validating tenant_id in all operations

Integration

flowchart LR
    A[Frontend] -->|Upload| B[API Endpoint]
    B -->|Store| C[S3 Bucket]
    B -->|Save metadata| D[(PostgreSQL)]
    A -->|View| E[Presigned URL]
    C -->|Serve| E