Per-user quotas: storage.quota.perUserBytes (deferred from v0.2.5) #41

Merged
Cordy merged 3 commits from feat/per-user-quota into main 2026-07-26 15:16:17 +00:00
Owner

The per-user half of quota enforcement, deferred in v0.2.5 "until the per-user home model exists" — it exists since v0.2.6, so:

  • storage.quota.perUserBytes (+ CAIRN_QUOTA_PER_USER_BYTES): caps each /home/<user>/ tree. The quota decorator sits below the scope overlay, so it sees actual backend paths and needs no user context — the path prefix is the account.
  • Usage accounting without a DB: tree walk of the user's home, cached 30s; successful writes credit the cache immediately. Conservative approximations (overwrites charged at full size, deletes not decremented until re-walk) self-correct at the next walk. Missing home = zero. Walk depth capped at 32.
  • Stream enforcement: the effective cap per write is min(maxFileBytes, per-user remainder), enforced mid-stream via the existing limitedReaderErrQuotaExceeded → 507/JSON as before. Exhausted quota rejects before any bytes flow.
  • Writes outside /home/<user>/ (group spaces, flat mode, admin root writes) are not subject to the per-user cap — global caps still apply.

Tests (in-memory tree backend): cap enforced across cached credit, exhausted quota, per-user isolation, outside-home exemption, fresh-user zero usage, per-user remainder tighter than maxFileBytes.

The per-user half of quota enforcement, deferred in v0.2.5 "until the per-user home model exists" — it exists since v0.2.6, so: - **`storage.quota.perUserBytes`** (+ `CAIRN_QUOTA_PER_USER_BYTES`): caps each `/home/<user>/` tree. The quota decorator sits below the scope overlay, so it sees actual backend paths and needs no user context — the path prefix *is* the account. - **Usage accounting without a DB**: tree walk of the user's home, cached 30s; successful writes credit the cache immediately. Conservative approximations (overwrites charged at full size, deletes not decremented until re-walk) self-correct at the next walk. Missing home = zero. Walk depth capped at 32. - **Stream enforcement**: the effective cap per write is min(`maxFileBytes`, per-user remainder), enforced mid-stream via the existing `limitedReader` → `ErrQuotaExceeded` → 507/JSON as before. Exhausted quota rejects before any bytes flow. - Writes outside `/home/<user>/` (group spaces, flat mode, admin root writes) are not subject to the per-user cap — global caps still apply. Tests (in-memory tree backend): cap enforced across cached credit, exhausted quota, per-user isolation, outside-home exemption, fresh-user zero usage, per-user remainder tighter than maxFileBytes.
Cordy merged commit 0b7574a863 into main 2026-07-26 15:16:17 +00:00
Sign in to join this conversation.
No reviewers
No labels
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: Cordy/Cairn#41
No description provided.