Cyphrme / Cyphrme/Cyphr

Per-principal storage-byte quota was never implemented

Open
#129 0 comments 0 reactions 0 assignees View on GitHub
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.