Bandwidth & scheduling
Rendered from spec at76d46f3 (2026-10-25) · its source · Edit this page
Status: Accepted · Audience: Operator · Area: D — Content & household
Purpose
Stop the stack from ruining the household’s internet.
A media stack saturates whatever connection it’s given. Unconstrained, a large grab consumes the entire uplink and downlink, and the effect is felt immediately by everyone else in the house — video calls stutter, games lag, streaming buffers.
This is a social failure more than a technical one. The operator gets blamed, and the usual resolution is to stop running the stack during the day, or to stop running it at all. Meanwhile seeding obligations on private trackers push in the opposite direction.
There is also a hard constraint for many: metered connections with monthly data caps, where uncontrolled seeding is genuinely expensive.
Behaviour
Limits are expressed in shares, not just numbers
Most operators don’t know their connection’s actual throughput in the units the download clients expect. Limits are therefore offered as a proportion of measured capacity as well as absolute figures, with the measured figure shown.
Schedules follow household rhythms
The useful shape is not arbitrary cron-like rules but the pattern every household has: constrained while people are awake and using the connection, unconstrained overnight.
| Period | Typical |
|---|---|
| Active hours | Limited, so the connection stays usable |
| Quiet hours | Unlimited |
Defined once and applied to both download clients, rather than configured separately in SABnzbd and qBittorrent with different semantics.
Upload is limited separately, and defaults lower
Home connections are asymmetric — uplink is typically a fraction of downlink, and saturating it degrades everything, including downloads, because acknowledgements can’t get out.
Uploads are the more damaging direction and get a conservative default. But seeding is an obligation on private trackers, so throttling is preferred to stopping, and the ratio consequence of any limit is stated.
Data caps are tracked where declared
An operator who declares a monthly cap gets consumption tracked against it, with a warning before exhaustion — and seeding is the usual culprit, since it continues indefinitely without producing anything visible.
Limits apply to the stack, not the machine
lemonfiber limits its own services. It does not attempt system-wide traffic shaping, which would require privileges it shouldn’t hold and would affect applications it has no business touching.
Temporary override
A one-off “unrestricted for the next hour” for when the operator wants something now and knows nobody else is affected. Time-boxed, so it can’t be forgotten and left on.
Pausing every download client at once
Sometimes the line is wanted now: a call is about to start, or a guest is about to play something. The operator pauses every download client the stack runs, qBittorrent and SABnzbd alike, in one request, and resumes them the same way, at the command line or over the web API. The outcome names each client with what it read back afterwards, so a client that did not take the request shows up by name rather than being assumed paused. A rehearsal says what each client is doing now and what the request would change, and changes nothing.
A pause holds until somebody resumes it. A schedule boundary, a temporary override, a new month or a lifted cap leaves a paused client paused, because a pause is the operator’s decision and none of those are.
On the companion
A share is a share of something, and N10-R4 requires the app to say whether the
line’s capacity was declared by the operator or observed by the stack
(N10). Half the uplink measured
against a figure somebody typed and half the uplink measured against a figure
something observed are different promises, and only one of them quietly becomes
wrong when the connection changes.
Data caps are tracked where declared needs two distinctions the contract
already carries. N10-R7 requires a cap with no figure set told apart from a cap
of zero — on a dashboard those render identically and mean opposite things — and
N10-R6 requires reaching one to say which of pause, throttle or continue the
stack does, since cap reached without that is not something an operator can
plan the rest of the month around. Where the contract carries whether traffic
passes through the tunnel, N10-R5 requires it shown.
The app raises no alerts of its own here; what is configured is the core’s
(N10-R11, N4-R11). A rehearsed alert is labelled as a rehearsal and is never
presented as one that was sent (N10-R9), and connections, capacity or alerts
that could not be read are told apart from there being none (N10-R12).
States
| State | Meaning |
|---|---|
unlimited |
No limits configured |
limited |
Limits active |
scheduled-active |
Within active hours; limits applied |
scheduled-quiet |
Within quiet hours; limits relaxed |
overridden |
Temporary override in force, with expiry |
cap-warning |
Approaching a declared data cap |
cap-exceeded |
Cap reached; configured behaviour applied |
paused |
Every download client paused by the operator, until resumed |
Edge cases
| Situation | Behaviour |
|---|---|
| Operator doesn’t know their connection speed | Offer to measure, and express limits as a proportion of the result. |
| Measured speed varies | Re-measure periodically; don’t pin limits to a single stale reading. |
| Schedule boundary crossed mid-transfer | Apply the new limit to the running transfer; don’t wait for it to finish. |
| Clock change or daylight saving | Schedules follow local time and handle the transition without a gap or double application. |
| Seeding throttled below tracker requirements | State the consequence for ratio; never silently jeopardise standing on a private tracker. |
| Data cap declared but usage unmeasurable | Track what the stack itself transfers; state that other household usage isn’t counted. |
| Cap exceeded | Apply the configured behaviour — pause, throttle heavily, or continue — as chosen in advance, not decided in the moment. |
| Override left running | Time-boxed by design; expires automatically and reports that it did. |
| Download client ignores a limit | Verify the limit was accepted; report if actual throughput exceeds it. |
| VPN adds overhead | Note that measured throughput through the tunnel is lower than raw connection speed. |
| Household member streaming from Jellyfin | Local traffic isn’t limited. Only external transfers are shaped. |
| Very slow connection | Warn if limits would make transfers impractically slow. |
| One client cannot be reached during a pause or resume | The others are paused or resumed anyway, and the outcome names the unreachable one as not reached. |
| Resumed while a spent cap holds the clients stopped | Resumed as asked. The outcome says the cap is spent, so its configured behaviour applies again at the next check. |
| Paused, then the month turns over or the cap is lifted | Stays paused until resumed. |
Acceptance criteria
| ID | Requirement |
|---|---|
| D10-R1 | Limits MUST be expressible as a proportion of measured capacity as well as absolute values. |
| D10-R2 | Measured connection capacity MUST be shown alongside any proportional limit. |
| D10-R3 | Schedules MUST support active and quiet periods, defined once and applied to all download clients. |
| D10-R4 | Upload limits MUST be configurable separately and MUST default more conservatively than download. |
| D10-R5 | Any limit affecting seeding MUST state the ratio consequence. |
| D10-R6 | Schedule boundaries MUST apply to in-progress transfers immediately. |
| D10-R7 | Schedules MUST follow local time and MUST handle daylight-saving transitions without gaps or double application. |
| D10-R8 | Declared data caps MUST be tracked against measured stack usage, with a warning before exhaustion. |
| D10-R9 | Cap-exceeded behaviour MUST be configured in advance, not prompted at the time. |
| D10-R10 | Data-cap tracking MUST state that non-stack household usage is not counted. |
| D10-R11 | Temporary overrides MUST be time-boxed and MUST report on expiry. |
| D10-R12 | lemonfiber MUST limit only its own services and MUST NOT perform system-wide traffic shaping. |
| D10-R13 | Applied limits MUST be verified as accepted, and actual throughput exceeding a limit MUST be reported. |
| D10-R14 | Local playback traffic MUST NOT be subject to bandwidth limits. |
| D10-R15 | Every download client the stack runs MUST be pausable and resumable in one request, at the command line and over the web API. The outcome MUST name each client with what it read back afterwards, and a rehearsal MUST change nothing. |
| D10-R16 | A pause MUST hold until it is resumed. A schedule boundary, a temporary override, a new month or a lifted cap MUST NOT resume a paused client. |
Related
- D5 Disk space — the other finite resource
- C7 Queue health — distinguishing throttled from stalled
- C2 VPN verification — tunnel throughput overhead
- B5 Notifications