Skip to content

Add opt-in storage limits for the upload directory - #161

Open
craig-b wants to merge 1 commit into
zhongkaifu:mainfrom
craig-b:feat/upload-storage-limits
Open

Add opt-in storage limits for the upload directory#161
craig-b wants to merge 1 commit into
zhongkaifu:mainfrom
craig-b:feat/upload-storage-limits

Conversation

@craig-b

@craig-b craig-b commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

The upload directory grows without bound: /api/upload accepts up to 500MB per request from any client, multimodal chat requests materialise base64 attachments to disk, generated outputs land there too, and nothing is ever deleted — any client can fill the disk, and honest use leaks storage indefinitely.

Three knobs, all defaulting to today's behavior (naming follows the responses-store precedent):

  • --upload-max-mb / TS_UPLOAD_MAX_MB — per-file cap on client-originated writes (multipart uploads and decoded base64 attachments), checked before bytes hit disk; defaults to 500, the existing request-body limit. Rejected with 413 stating the cap.
  • --upload-quota-mb / TS_UPLOAD_QUOTA_MB — total directory budget counting client writes and server-generated outputs; a startup scan seeds an in-memory tally, client writes reserve bytes atomically, generation endpoints gate at request start. Rejected with 507 (message deliberately does not disclose the budget).
  • --upload-ttl-hours / TS_UPLOAD_TTL_HOURS — background sweep deleting files older than the TTL; off by default.

Verified live (413 / 200 / 507 across upload + Ollama/OpenAI base64 attachments) and environment-independent lane 1161 passed / 0 failed on current main.

The upload directory previously grew without bound: /api/upload accepts
files from any client at up to 500MB per request, every multimodal chat
request materialises its base64 attachments to disk, generated outputs
(edited images, videos) are written there too, and nothing is ever
deleted — so any unauthenticated client could fill the disk, and even
honest use leaked storage indefinitely.

Three knobs, all defaulting to today's behavior (naming follows the
responses-store precedent, TS_RESPONSES_STORE_TTL_MINUTES etc.):

- --upload-max-mb / TS_UPLOAD_MAX_MB: per-file cap on client-originated
  writes (multipart uploads and decoded base64 attachments), checked
  before bytes hit disk. Defaults to 500, the existing request-body
  limit. Rejected with 413 and a message that states the cap, since the
  client can act on it.
- --upload-quota-mb / TS_UPLOAD_QUOTA_MB: total directory budget,
  counting client writes AND server-generated outputs. One directory
  scan at startup seeds an in-memory tally; client writes reserve their
  bytes atomically up front, and generation requests (image edit,
  video) are gated at request start so a full quota fails in
  milliseconds instead of after minutes of GPU work. Rejected with 507;
  the message deliberately does not disclose the configured budget
  (operator config, in the startup log instead). Default: off.
- --upload-ttl-hours / TS_UPLOAD_TTL_HOURS: background sweep deleting
  files whose mtime exceeds the TTL (interval TTL/4 clamped to 1-15
  min, one sweep at startup). Deliberately off by default: a live
  conversation may still reference an old attachment path for
  re-generation, so the operator opts in with eyes open.

UploadStoragePolicy owns the tally and all admission decisions; the
chat parsers throw UploadLimitExceededException from the base64 path
and each protocol adapter translates it into its own error shape.

Verified against a live server: 413/200/507 on /api/upload, 413/507 on
Ollama and OpenAI base64 attachments, startup scan counting pre-existing
files, and the quota gate on generation endpoints.
@zhongkaifu

Copy link
Copy Markdown
Owner

@craig-b It seems there are some conflicts, could you please resolve it ? Thanks for this update.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants