File Storage
Purpose
Design how files enter, live in, and leave the system: upload path, validation, storage backend, key scheme, access control, and serving — with the server enforcing every limit clients claim to respect.
When to Use
- When the system accepts uploads or serves stored files (avatars, documents, media, exports).
- Not for static app assets shipped with the build.
Inputs
- File use cases: types, size ranges, volume, retention, who may read each class.
- Deployment shape (object storage availability), authorization model (
ownership-authorization).
Discovery Questions
- What file classes exist, with max size, allowed types, and expected volume?
- Public files (avatars) vs private (documents) — who may read each?
- Do large uploads justify presigned direct-to-storage upload, or is proxy-through-API fine?
- Post-upload processing (resize, scan, transcode) — sync or background?
Responsibilities
- Choose backend: object storage (S3-compatible) for anything multi-instance or durable; local disk only for single-node/dev with a recorded migration path.
- Choose upload path per class: proxy-through-API (small files, simplest control) vs presigned upload (large files; constrain type/size in the presign policy, verify after upload).
- Validate server-side: size limits, allowed types by content inspection (magic bytes), not extension or client MIME; reject or sanitize dangerous types (SVG/HTML execute in browsers).
- Define keys: generated, non-guessable, scoped (
tenant/user/uuid.ext) — never client-supplied filenames as keys; original name stored as metadata only.
- Enforce access control on read: private files served via ownership-checked endpoints or short-lived signed URLs (
ownership-authorization); public classes explicitly marked.
- Plan lifecycle: orphan cleanup (upload without commit), deletion propagation, retention; heavy processing goes to
background-jobs.
Required Workflow
- Build the file-class table (type, size, visibility, volume, retention).
- Choose backend + upload path per class.
- Define validation (size, magic-byte type, sanitization) and key scheme.
- Define read-side access control + serving (signed URLs/CDN for public).
- Plan processing + lifecycle jobs.
- Specify tests: oversized rejected, spoofed type rejected, cross-user file read rejected, presign constraints enforced.
Decision Rules
- Content-type by magic bytes; the extension is a hint, never proof.
- Presigned uploads still get server-side post-verification (size/type of what actually landed).
- Serving user uploads from your app origin risks stored XSS — serve from a separate domain/CDN with correct
Content-Type and Content-Disposition.
- Files are resources: cross-user/cross-tenant negative tests apply exactly as in
ownership-authorization.
Rules
- Every upload endpoint has recorded size/type limits enforced server-side.
- No client-controlled storage paths or keys.
- Upload endpoints are rate-limited (
rate-limiting — expensive class).
Anti-Patterns
- Trusting client MIME type or extension.
- Serving private files by unguessable URL alone ("security by URL").
- Storing user uploads inside the app's public/static directory.
- Unbounded upload sizes; no orphan cleanup.
- Original filename used as storage key (collisions, traversal, injection).
Validation Checklist
Definition of Done
A recorded file design — classes, backend, upload paths, server-enforced validation, key scheme, read-side access control, lifecycle — with negative tests covering size, type-spoofing, and cross-user access.
Related Skills
ownership-authorization, backend-validation, rate-limiting, background-jobs, backend-security, third-party-integrations (storage provider), ../../database/relational-schema-design (file metadata).
Related Knowledge
../../../knowledge/ (file classes, retention policy).
Related References
../../../references/backend/files/ (class table, when populated).
Context Loading Guidance
- Requires: file use cases, visibility rules, deployment shape.
- Does not require: image-processing details, CDN vendor docs.
- May load:
ownership-authorization (read control), background-jobs (processing).
- Stop when: class table, paths, validation, and access control are recorded.
Token Efficiency Guidance
The file-class table drives everything; decide per class, don't re-derive per endpoint.
1---2name: file-storage3description: Use to plan file upload and storage — object storage vs disk, upload paths (direct vs presigned), server-side validation (type/size/content), key naming, access control on files, and serving (signed URLs, CDN).4---56# File Storage78## Purpose910Design how files enter, live in, and leave the system: upload path, validation, storage backend, key scheme, access control, and serving — with the server enforcing every limit clients claim to respect.1112## When to Use1314- When the system accepts uploads or serves stored files (avatars, documents, media, exports).15- **Not** for static app assets shipped with the build.1617## Inputs1819- File use cases: types, size ranges, volume, retention, who may read each class.20- Deployment shape (object storage availability), authorization model (`ownership-authorization`).2122## Discovery Questions2324- What file classes exist, with max size, allowed types, and expected volume?25- Public files (avatars) vs private (documents) — who may read each?26- Do large uploads justify presigned direct-to-storage upload, or is proxy-through-API fine?27- Post-upload processing (resize, scan, transcode) — sync or background?2829## Responsibilities3031- Choose backend: **object storage** (S3-compatible) for anything multi-instance or durable; local disk only for single-node/dev with a recorded migration path.32- Choose upload path per class: proxy-through-API (small files, simplest control) vs **presigned upload** (large files; constrain type/size in the presign policy, verify after upload).33- Validate server-side: size limits, allowed types by **content inspection** (magic bytes), not extension or client MIME; reject or sanitize dangerous types (SVG/HTML execute in browsers).34- Define keys: generated, non-guessable, scoped (`tenant/user/uuid.ext`) — never client-supplied filenames as keys; original name stored as metadata only.35- Enforce **access control on read**: private files served via ownership-checked endpoints or short-lived signed URLs (`ownership-authorization`); public classes explicitly marked.36- Plan lifecycle: orphan cleanup (upload without commit), deletion propagation, retention; heavy processing goes to `background-jobs`.3738## Required Workflow39401. Build the file-class table (type, size, visibility, volume, retention).412. Choose backend + upload path per class.423. Define validation (size, magic-byte type, sanitization) and key scheme.434. Define read-side access control + serving (signed URLs/CDN for public).445. Plan processing + lifecycle jobs.456. Specify tests: oversized rejected, spoofed type rejected, cross-user file read rejected, presign constraints enforced.4647## Decision Rules4849- Content-type by magic bytes; the extension is a hint, never proof.50- Presigned uploads still get server-side post-verification (size/type of what actually landed).51- Serving user uploads from your app origin risks stored XSS — serve from a separate domain/CDN with correct `Content-Type` and `Content-Disposition`.52- Files are resources: cross-user/cross-tenant negative tests apply exactly as in `ownership-authorization`.5354## Rules5556- Every upload endpoint has recorded size/type limits enforced server-side.57- No client-controlled storage paths or keys.58- Upload endpoints are rate-limited (`rate-limiting` — expensive class).5960## Anti-Patterns6162- Trusting client MIME type or extension.63- Serving private files by unguessable URL alone ("security by URL").64- Storing user uploads inside the app's public/static directory.65- Unbounded upload sizes; no orphan cleanup.66- Original filename used as storage key (collisions, traversal, injection).6768## Validation Checklist6970- [ ] File-class table recorded (type/size/visibility/retention).71- [ ] Backend + upload path chosen per class.72- [ ] Magic-byte validation + sanitization planned.73- [ ] Generated, scoped key scheme; original names as metadata.74- [ ] Read-side access control + serving domain decided.75- [ ] Lifecycle/processing jobs planned.76- [ ] Negative tests: oversize, spoofed type, cross-user read.7778## Definition of Done7980A recorded file design — classes, backend, upload paths, server-enforced validation, key scheme, read-side access control, lifecycle — with negative tests covering size, type-spoofing, and cross-user access.8182## Related Skills8384`ownership-authorization`, `backend-validation`, `rate-limiting`, `background-jobs`, `backend-security`, `third-party-integrations` (storage provider), `../../database/relational-schema-design` (file metadata).8586## Related Knowledge8788`../../../knowledge/` (file classes, retention policy).8990## Related References9192`../../../references/backend/files/` (class table, when populated).9394## Context Loading Guidance9596- **Requires:** file use cases, visibility rules, deployment shape.97- **Does not require:** image-processing details, CDN vendor docs.98- **May load:** `ownership-authorization` (read control), `background-jobs` (processing).99- **Stop when:** class table, paths, validation, and access control are recorded.100101## Token Efficiency Guidance102103The file-class table drives everything; decide per class, don't re-derive per endpoint.