The global limits in Settings → Connection apply to the engine as a whole. Per-torrent limits, set in the inspector’s Overview tab, cap a single torrent’s download or upload speed regardless of how much headroom the global limit has left.

The most common use case: cap upload on a specific seedbox torrent so it doesn’t saturate your link, without having to lower the global upload cap (which would affect every other torrent too).

Where to find it

Inspector → Overview tab → the two rate-limit rows below the totals:

  • ↓ Speed limit — KB/s, or empty for unlimited.
  • ↑ Speed limit — KB/s, or empty for unlimited.

Values are entered as KB/s, persisted to the torrents table (down_rate_limit, up_rate_limit columns in bytes/sec), and re-applied on startup. Clearing the input (or entering 0) removes the cap.

How it works (implementation note)

anacrolix v1.61 has a per-Client rate limiter (which is what powers the global cap) but no per-torrent limiter API. Mosaic implements per-torrent caps with a background duty-cycle goroutine: every 500 ms it samples the torrent’s byte counters and toggles DisallowDataDownload / AllowDataDownload (and the upload equivalents) to throttle when the previous interval exceeded budget.

Consequences:

  • Coarse, not byte-perfect. Actual throughput hovers around the configured cap ± one tick’s worth of in-flight data (~one chunk, ~16 KB by default). For typical caps (tens of KB/s or more), the visible behavior matches the configured value within a few percent. Below ~16 KB/s, the duty cycle gets bursty.
  • One goroutine per limited torrent. The goroutine starts the first time a limit is set and exits when both axes drop back to 0. Setting limits on hundreds of torrents simultaneously creates hundreds of goroutines — fine in practice (they’re cheap and mostly sleeping), but worth knowing if you’re scripting bulk operations.
  • Pause path is independent. The duty-cycle limiter and Pause() both use DisallowDataDownload/Upload under the hood, but the engine tracks which call last set the state so a manual pause-then-resume doesn’t accidentally drop your limit, and a limit hitting 100% block doesn’t trick the UI into showing the torrent as paused.

Interaction with the global cap

Per-torrent and global caps compose; the effective speed is the minimum of all applicable caps. Examples:

  • Global down = 1000 KB/s, torrent down = 200 KB/s → this torrent gets at most 200 KB/s.
  • Global down = 100 KB/s, torrent down = 200 KB/s → this torrent gets at most 100 KB/s (the global is tighter).
  • Global down = 0 (unlimited), torrent down = 200 KB/s → this torrent gets at most 200 KB/s; other torrents are unlimited.

The scheduled-bandwidth rules from Scheduling only mutate the global limit — your per-torrent caps stay in force across schedule windows.

API

GET  /api/torrents/{id}/rate_limits        → { "down_kbps": 0, "up_kbps": 200 }
PUT  /api/torrents/{id}/rate_limits        body: { "down_kbps": 0, "up_kbps": 200 }

0 means unlimited on that axis. Values are in KB/s; the engine stores bytes/sec internally and reapplies on startup. Requires editor-or-owner access on the torrent.

See also