Per-principal storage-byte quota was never implemented
- Dominant language
- Rust
- Stars
- 3
- Forks
- 0
- Avg merge
- 1d 42m
- Merged PRs (30d)
- 11
Description
The server enforces a per-principal **count** quota but has no per-principal **storage-byte** quota. The production-readiness roadmap called for both.
## What exists today
Three limit mechanisms are implemented:
1. **Per-IP rate limit.**
2. **Per-operation rate limit** (read / push / login / revoke), keyed by IP — deliberately not per-principal.
3. **Per-principal commit-count quota** (`count_quota`). Enforced in `rs/cyphr-server/src/rate_limit.rs` as tower middleware: on `/push`, after the body is buffered, it reads the principal's commit count and returns `402` before the request reaches the handler.
There is also a global per-request body-size cap (`max_body_bytes`), but that bounds a single request rather than a principal's total footprint.
`LimitsConfig` in `rs/cyphr-server/src/config.rs` has seven fields; none is a per-principal size quota.
## What is missing
No mechanism bounds the total bytes one principal may store. A principal staying under the commit-count cap can consume unbounded storage by pushing large commits, so the count quota does not bound the resource that actually runs out.
## Why this is filed separately
The roadmap item covering admission control scoped "per-PG storage/count quotas." The count half landed; the storage half did not. That item otherwise reads as complete, so the gap is invisible from the roadmap.
## Constraint on any implementation
**A storage quota must enforce by refusing admission, never by pruning.**
The specification states that services are optional, principals may self-host or use multiple services, and full verification is always possible from transaction history. A server that prunes accepted entries to stay under a quota can no longer serve the history that guarantee depends on — the guarantee becomes false for that server. Refusing a write leaves the record intact and verifiable; pruning destroys it.
If a design appears to require pruning, that is a specification-level question about the history guarantee, not an implementation decision. Stop and escalate rather than pruning.
This constraint is being recorded alongside the server's other standing invariants so it is visible to whoever implements the quota, independent of this issue.
## Current state is safe
An audit of the merged server tree found no deletion path reachable from quota enforcement, no background or scheduled maintenance task, and no deletion capability at all in the durable storage layers — no delete, prune, truncate, evict, compact, or vacuum function exists in the storage, index, or blob crates. Pruning is not merely unreachable today; it cannot be added without first introducing the capability.
(`principal/delete` is unrelated: a lifecycle transaction a principal signs on itself, which sets a flag and never erases the commit chain.)
## Suggested scope
- A per-principal storage-byte quota, configurable alongside the existing count quota.
- Enforcement at admission, symmetric with the count quota's `402` refusal.
- A test asserting that exceeding the quota refuses the write and leaves stored history byte-identical.
## Priority
Not urgent — no correctness defect and no current path to data loss. It is a resource-exhaustion exposure and an incomplete roadmap item. Worth handling before any deployment that accepts writes from principals outside the operator's control.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with LimitsConfig in rs/cyphr-server/src/config.rs and the count-quota middleware in rs/cyphr-server/src/rate_limit.rs, tracing the /push admission path after body buffering. Run the existing quota tests, then define how stored bytes are measured and where the refusal occurs. Done means an over-quota write returns 402 and leaves the stored history byte-identical, without pruning.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend, databases
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100