- apps/web/ — Next.js 16 frontend (App Router, Tailwind v4, shadcn/ui)
- Dashboard with stats, upload chart, recent uploads
- File upload with drag-and-drop, progress tracking
- File browser with preview, download, delete
- Dark mode via
next-themes
- services/api/ — FastAPI backend (layered architecture)
- REST API for file upload, listing, deletion
- B2 S3 integration via boto3
- File metadata extraction (images, PDFs)
- Health check endpoint with B2 connectivity verification
- Structured JSON logging with request tracing
- Prometheus-format metrics endpoint
- packages/shared/ — TypeScript type definitions
- Mirrors Pydantic models from the API
- Consumed by
apps/web/as workspace dependency
The API follows a strict layered architecture:
types/ Pydantic models — no logic, no imports from other layers
|
config/ Settings (pydantic-settings) — depends only on types
|
repo/ Data access (boto3 B2 client) — no business logic
|
service/ Business logic — calls repo, returns types
|
runtime/ FastAPI routes — calls service, never repo directly
- Dependencies flow downward only:
types->config->repo->service->runtime - No backward imports (e.g., service must not import from runtime)
boto3only allowed inrepo/layer- All boundary data uses Pydantic models (no raw dicts across layers)
- Each file stays under 300 lines
services/api/
main.py App entrypoint, middleware, router registration
app/
types/ Pydantic models (FileMetadata, UploadStats, etc.)
config/ Settings loaded from environment
repo/ B2 S3 client (data access layer)
service/ Business logic (upload, files, metadata)
runtime/ FastAPI route handlers
tests/ pytest tests (structural + integration)
- No external SDK leakage:
boto3is only imported inapp/repo/. All other layers interact with B2 through the repo interface. - No raw dicts at boundaries: All data crossing layer boundaries uses typed Pydantic models.
- No mutable globals: Configuration is read-only after init. No module-level mutable state shared between layers.
- Validated inputs: All HTTP inputs validated by FastAPI/Pydantic. All file keys validated against prefix allowlist.
- Local dev —
pnpm devruns both services viaconcurrently- Web:
localhost:3000 - API:
localhost:8000
- Web:
- Railway — two services from the same repo
- See
infra/railway/README.mdfor configuration
- See
- Backblaze B2 — object storage (S3-compatible API)
- All uploaded files stored in a single bucket
- File listing and metadata via S3
list_objects_v2/head_object - No application database — B2 is the sole data store
- Backblaze B2 S3 API — file storage, retrieval, deletion, presigned URLs
See docs/SECURITY.md for full security documentation.
- Frontend -> API — CORS-restricted to configured origins
- API -> B2 — authenticated via application keys, signature v4
- Client -> B2 — presigned URLs for download (10-min expiry, forced attachment)
- Upload: Browser ->
POST /upload(multipart) -> API validates -> service orchestrates -> repo writes to B2 -> metadata extracted -> response - List: Browser ->
GET /files-> service calls repo -> returns file list - Download: Browser ->
GET /files/{key}/download-> service validates key -> repo generates presigned URL -> browser downloads - Delete: Browser ->
DELETE /files/{key}-> service validates key -> repo deletes from B2
- Structured JSON logging on all requests with
request_id - Request timing middleware (logs duration per request)
/metricsendpoint (Prometheus format: request count, latency, upload count)/healthendpoint (B2 connectivity check)
- Layered API handler:
services/api/app/runtime/upload.py - Service orchestration:
services/api/app/service/upload.py - B2 data access (repo layer):
services/api/app/repo/b2_client.py - Pydantic models:
services/api/app/types/(files.py,upload.py,stats.py,formatting.py) - Config (pydantic-settings):
services/api/app/config/settings.py - Structural tests:
services/api/tests/test_structure.py - Frontend API client:
apps/web/src/lib/api-client.ts - Shared TypeScript types:
packages/shared/src/types.ts
- docs/SECURITY.md — security principles and implementation
- docs/RELIABILITY.md — reliability expectations
- AGENTS.md — architectural invariants and agent instructions