Expand description
Per-user quotas (D37): how much of the node one credential may hold.
D33 gave the node a record of who did what (crate::limits::Access)
and a bound on how often anyone did it
(crate::limits::RateLimiter). Neither bounds how large one
request may be or how much durable state one user may accumulate, and
a rate limit does not imply either: one push a minute is still an
unbounded pack, and one workspace a minute is still unbounded disk.
Two ceilings, sharing only the identity they key on — the
authenticated username string, exactly as
crate::limits::RateLimiter::check keys on it. Nothing here reads
the auth file or asks any user store whether a name exists: the string
arrives from the request’s credentials and is used as an opaque key.
§Where the push ceiling is enforced, and why it is there
read_bounded runs on the request thread before git http-backend is spawned, which is the whole design rather than an
implementation detail. Git applies no ref until the pre-receive hook
exits zero, so a size check inside that hook would already have
submitted ops for the refs it did reach — ops for refs git will never
create — and would have to drive the compensating retraction pass
Node::create_repo documents. Refusing before the CGI starts means no
hook ran, no op was submitted, and the retraction path is not entered
at all. The bound is on the transfer; the sequencer never learns the
push was attempted.
The cost is stated rather than hidden: an over-limit body is drained
to a sink before the refusal is written, so the client always reads a
413 instead of a broken connection, and so tiny_http’s own reader
does not try to swallow the remainder in one allocation on drop. The
bytes still cross the network. What the ceiling buys is that they
never reach memory beyond the ceiling, never reach git, and never
reach the log.
§Where the workspace ceiling gets its count, and why that survives a
restart
WorkspaceTally is not a new persisted file. It is a projection
folded out of the op log during the replay the platform already
performs at startup, keyed on the attribution channel each
workspace-creating entry already carries in its signature-covered
channel field. the build log left this item out of D33 calling a
surviving tally “a persisted-state question”; it is one, and the
answer is that the persisted state already exists and needed a reader
rather than a writer. A restart rebuilds the tally from the same log
that rebuilds the view, with no new format, no new file and no second
durability barrier.
§Examples
use choir_node::quota::{channel_for, Quotas};
// The tally's key is derived from the authenticated username and
// nothing else.
assert_eq!(channel_for("alice"), "git/alice");
// Both ceilings are off unless the operator sets them.
let none = Quotas::default();
assert!(none.push_bytes.is_none() && none.workspaces.is_none());
assert!(!none.is_active());Structs§
- Quotas
- The operator’s per-user ceilings. Either may be left unset, and unset means unlimited — the same shape as the D33 rate-limit flags.
- Workspace
Tally - Which channel holds which workspace, folded from the op log.
Enums§
- Body
- What
read_boundedfound in a request body.
Functions§
- channel_
for - The attribution channel a request-authenticated user’s operations are submitted under.
- read_
bounded - Reads a request body, stopping at
limitbytes.