Quota / rate-limit for POST /api/v1/uploads #24

Open
opened 2026-05-13 21:38:37 +02:00 by arne · 0 comments
Owner

Why

`POST /api/v1/uploads` (#22) is authenticated but has no per-token
quota or rate limit. A compromised bearer token, or a buggy client
retrying with re-encoded bytes (fresh hash each time → idempotency
doesn't help), can fill the daemon's disk:

  • 10 MiB per request × N requests/sec → linear disk growth.
  • Garbage collection of unreferenced uploads is also out of scope today
    (filed separately), so the floor never moves down.

Options

  1. Per-token byte quota (preferred). Track `SUM(file size)` of
    uploads owned by a token; reject `POST /uploads` with 413 once the
    threshold is exceeded. Needs a token-id column on uploads and a
    quota config knob. Most informative for clients.
  2. Per-token request rate limit. Reuse the `golang.org/x/time/rate`
    token bucket pattern already used by `api.IPRateLimiter`. Cheaper to
    implement but doesn't bound total bytes — N requests under the limit
    still equals N × 10 MiB.
  3. Per-IP rate limit on the upload path, layered with auth.
    Cheap, but punishes shared-IP devices.

Recommend (1) for production, (2) as a stopgap if it lands sooner.

Acceptance

  • A token that has uploaded more than the configured quota gets a 413
    with `error: "quota-exceeded"` (or similar stable code).
  • Integration test covers the threshold-crossing case.
  • The quota is bypassable for the avatar PUT (avatars are bounded to one
    file per identity and shouldn't count).

Related: #22, follow-up to the upload-quota item in the PR #22 review.

### Why \`POST /api/v1/uploads\` (#22) is authenticated but has no per-token quota or rate limit. A compromised bearer token, or a buggy client retrying with re-encoded bytes (fresh hash each time → idempotency doesn't help), can fill the daemon's disk: - 10 MiB per request × N requests/sec → linear disk growth. - Garbage collection of unreferenced uploads is also out of scope today (filed separately), so the floor never moves down. ### Options 1. **Per-token byte quota** (preferred). Track \`SUM(file size)\` of uploads owned by a token; reject \`POST /uploads\` with 413 once the threshold is exceeded. Needs a token-id column on uploads and a quota config knob. Most informative for clients. 2. **Per-token request rate limit.** Reuse the \`golang.org/x/time/rate\` token bucket pattern already used by \`api.IPRateLimiter\`. Cheaper to implement but doesn't bound total bytes — N requests under the limit still equals N × 10 MiB. 3. **Per-IP rate limit** on the upload path, layered with auth. Cheap, but punishes shared-IP devices. Recommend (1) for production, (2) as a stopgap if it lands sooner. ### Acceptance - A token that has uploaded more than the configured quota gets a 413 with \`error: \"quota-exceeded\"\` (or similar stable code). - Integration test covers the threshold-crossing case. - The quota is bypassable for the avatar PUT (avatars are bounded to one file per identity and shouldn't count). Related: #22, follow-up to the upload-quota item in the PR #22 review.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
posta/server#24
No description provided.