Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 57 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,14 @@ docker/ # Docker image of `ketch server` (Dockerfile, entrypoint, compose
which, as the server, MCP and `ketch --list-files` word it),
`ActiveConnections`, `ActiveConnection`, `ConnectionDirection`, `ConnectionRoute`, `PeerDetails`,
`CountryDatabase` (its `Key` is a `ControlKey`) and `CountryDatabaseStatus`,
`SpeedModes` (its `Key` is a `ControlKey`: an instance's Full speed, Slow lane and Auto rules),
`SpeedModeState`, `SpeedModeChange`, `SpeedSettings` (`[speed]`), `SpeedRule`,
`SpeedLimitMode`, `Weekday`, `SpeedScheduler` and `SpeedWindow` (the rules read in a time zone),
`ControlKey` (`KetchApi.controller(key)` looks up optional typed controllers that other modules
define; `null` by default), `Interest` (what `KetchApi.subscribe(interest, interval)`, abstract,
follows: `Interest.Segments(taskIds)`, `Interest.Stats` (`KetchStats`) and
`Interest.Connections(limit)`; other modules define their own, all serializable, each naming the
follows: `Interest.Segments(taskIds)`, `Interest.Stats` (`KetchStats`),
`Interest.Connections(limit)` and `Interest.SpeedMode` (`SpeedModeState`); other modules define
their own, all serializable, each naming the
`valueSerializer` servers encode what it reports with; a flow ends when what it follows is gone,
and an instance fails those it does not know with `UnsupportedOperationException` when
collected), `Checksum`, `ChecksumAlgorithm`, and
Expand Down Expand Up @@ -161,6 +165,9 @@ docker/ # Docker image of `ketch server` (Dockerfile, entrypoint, compose
- `com.linroid.ketch.core.geo` -- `CountryLookup` (a `CountryDatabase` `Ketch(countries = ...)`
names `ActiveConnection.country` with), internal `CountryTable`, and `DbIpCountryDatabase`
(jvmAndAndroidMain)
- `com.linroid.ketch.core.speed` -- `SpeedModeOptions` (what `Ketch(speedModes = ...)` starts
from and saves through) and `ObservedPeak`; internal `SpeedModeKeeper` (the `SpeedModes`
`Ketch` runs)
- `com.linroid.ketch.core.mirror` -- `MirrorDownloadHelper` (one file of known size from several
HTTP(S) mirrors at once, with piece checks and repair), `Mirror`, `PieceHashes`,
`mirrorHeaders()`; internal `MirrorPool` (least busy mirror, backoff, dropping)
Expand Down Expand Up @@ -313,17 +320,17 @@ docker/ # Docker image of `ketch server` (Dockerfile, entrypoint, compose
`AiSettings`, `LlmSettings`, `LlmProvider`, `LlmApi`, `LlmProviderGroup`, `SearchSettings`,
`SearchProvider`,
`PageAccessSettings`, `PageAccessMode`, `SiteNames`, `TorrentSettings`, `AppearanceConfig`,
`AccentColor`, `ThemeMode`, `SpeedSettings`, `SpeedRule`, `UiPreferences`, `DesktopSettings`,
`AccentColor`, `ThemeMode`, `UiPreferences`, `DesktopSettings`,
`NotificationSettings`, `IntegrationSettings`, `PowerSettings`, `HookConfig`, `HookEvent`

### `app:shared` (`com.linroid.ketch.app`)
- `App` (root composable), `state` (`AppController`, `AppState`, `TaskListModel`, `PulseModel`,
`ConnectionGridModel`, `IntakeState`, `SpeedModeController`, `PendingOps`, `ForegroundPolicy`,
`KeepAwake`, `SleepInhibitor`, `FileSelectionModel`, `FileOrder`, `AiDiscoverController`,
`DiscoverSession`, `DiscoverHistoryStore`, `FileDiscoverHistoryStore` on JVM/Android), `instance`
(`InstanceManager`, `DevicePresence`, `DeviceScope`, `PairingRequests`, `LocalServerContext`),
`theme` (`KetchTheme` tokens), `components` (the Ketch controls), `icons` (`KetchIcon`),
`input` (`KetchCommands`,
(`InstanceManager`, `DevicePresence`, `DeviceScope`, `PairingRequests`, `LocalServerContext`,
and `embeddedSpeedModes()` in nonWebMain), `theme` (`KetchTheme` tokens), `components` (the
Ketch controls), `icons` (`KetchIcon`), `input` (`KetchCommands`,
`CommandScope`, `ShortcutMatcher`), `feedback` (`MessageCenter`, `ActivityMonitor`,
`UnreadableFiles`) and `util`
- `ui` -- `AppShell` and `shell` (layout, navigation and its badges, device switcher, drop
Expand Down Expand Up @@ -754,6 +761,23 @@ docker/ # Docker image of `ketch server` (Dockerfile, entrypoint, compose
run, such as a lent torrent seeder, pay `DownloadContext.globalThrottle`, the global limit
alone, which holds nothing of the run. Seeders `TorrentControls` restores have no context and
pay no Ketch limit
- Speed modes (`Ketch(speedModes = SpeedModeOptions(...))`, `controller(SpeedModes)`, listed as
`KetchFeatures.SPEED_MODES`, `speed.modes`): the engine switches its own global limit between
the standing cap (`SpeedSettings.standard`), Full speed, and the slow lane, by hand
(`SpeedLimitMode.SlowLane`) or while a weekly `SpeedRule` holds in `Auto`, so the schedule runs
on the device doing the downloads, also while no client is connected. `SpeedModeKeeper`
evaluates the rules (`SpeedScheduler`, wall-clock times in the system time zone) whenever one
starts or ends and at least every minute, and records the highest download speed it sees in a
week outside the slow lane (`SpeedModeState.observedPeak`), whose `SLOW_LANE_PERCENT` (30 %)
is the slow lane's suggested speed when none is set (at least 256 KB/s, 1 MB/s without a
peak). The slow lane never runs faster than the cap. `DownloadConfig.speedLimit` stays the
limit in effect; the cap is always current: at full speed every limit set is the cap, and
outside it `updateConfig` takes a `speedLimit` other than the one in effect as the new cap,
while one that writes back the limit in effect changes nothing. The engine starts from
`config.speedLimit` as the cap; `SpeedModeOptions.saveSettings` saves each change before it
returns (hosts write `[speed]` and the cap as `[download] speedLimit`), `savePeak` the peak once
it held 10 s (`[ui] observedPeak`). `Interest.SpeedMode` follows the state. Tests:
`SpeedModeKeeperTest` (virtual time), `KetchSpeedModesTest`, `RemoteSpeedModesTest` (server)
- Token-bucket algorithm (`TokenBucket`) with delegating wrapper. Waiters queue by owner
(`SpeedLimiter.acquire(bytes, owner)`; the global limiter's owner is the task ID) and are served
in turns of at most 16 KiB by start-time fair queuing, so tasks share the global limit equally
Expand Down Expand Up @@ -1665,8 +1689,9 @@ docker/ # Docker image of `ketch server` (Dockerfile, entrypoint, compose
through `setEncryption`, `setUtpEnabled`, `setPortMapping` and `setLocalDiscovery` (the engine's
`TorrentPeerEncryption.policy`, a uTP endpoint that refuses new connections while off, a port
mapper started or stopped, and the LAN discovery group joined or left)
- `SpeedSettings`: the embedded device's speed mode (Full speed, Slow lane, Auto with weekly
`SpeedRule`s), applied by the apps' `SpeedModeController`; `UiPreferences` (`[ui]`): view
- `[speed]` (`SpeedSettings`, from `library:api`): the speed mode (Full speed, Slow lane, Auto
with weekly `SpeedRule`s) of the engine that runs this directory's downloads, which reads and
saves it (the apps' embedded engine, or `ketch server`); `UiPreferences` (`[ui]`): view
state such as table columns, sort, sidebar, inspector, density, per-device add sheet defaults,
Discover's docked history (`discoverHistory`) and onboarding; `DesktopSettings`: close action,
open at login, Dock badge, the speed in the macOS menu bar (`menuBarSpeed`), daily update
Expand Down Expand Up @@ -1852,6 +1877,17 @@ docker/ # Docker image of `ketch server` (Dockerfile, entrypoint, compose
task only meant to seed, are in the inspector only). Auto connections and queue positions only
show for devices whose `KetchStatus.features` list them (`AppState.featuresOf`; the embedded
engine has all, `EmbeddedFeatures`)
- Speed modes on every device: the engine of the device doing the downloads runs them
(`SpeedModes`), so a phone can switch a NAS to the Slow lane or Auto and the NAS follows its
rules while the phone is away. `SpeedModeController` (`state/`) is the app's view of one
device's speed modes, following `Interest.SpeedMode`; `InstanceManager` makes one for the
embedded engine (`embeddedSpeedMode`, the hosts pass `embeddedSpeedModes(config, save)` to its
`Ketch`) and one for each connected remote client, and `speedModes` lists those that reported
one (a remote device running an older Ketch never does). `AppState.speedModeFor(entry)` is the
device's: the Speed page shows Mode, Limits and Rules for any device that has one (a remote one
listing `speed.modes` loads until it reports, an older one keeps the plain speed limit and its
note), and the Pulse pill and popover, device rows and menus, the palette and the desktop menu
bar switch the shown device's; `DevicePresence.speedMode` is each device's own
- Remote devices send what is shown: every task's state at once and progress every second; the
segments of the rows on screen every second on desktop and the web (`WatchShownTasks`; phones
leave them out) and of the inspected tasks every 500 ms (`AppState.followSegments`); while the
Expand Down Expand Up @@ -2028,6 +2064,11 @@ docker/ # Docker image of `ketch server` (Dockerfile, entrypoint, compose
`selection` and `seeding` commands guarded by a revision and an idempotency key, and an SSE
`events` stream of snapshots (deprecated: the event stream carries them). Command failures
answer with the `TorrentCommandError` wire name
- Speed modes (`SpeedRoutes`, with `controller(SpeedModes)`, else not installed: `404`):
`GET /api/speed` answers the `SpeedModeState`, `PUT /api/speed` applies a `SpeedModeChange`
(fields left out unchanged; `slowLane` `unlimited` follows the suggestion) and answers the
state; the event stream's `speedMode` interest follows them. `RemoteKetch.controller(SpeedModes)`
fails closed with `UnsupportedOperationException` against servers without `speed.modes`
- Live connections (`ConnectionRoutes`): `GET /api/connections?limit=1..1024` answers the first
`ActiveConnections` snapshot and `/api/connections/events` (deprecated: the event stream
carries them) streams `snapshot` events (id: a per-stream count), repeating the last one every
Expand Down Expand Up @@ -2158,6 +2199,13 @@ docker/ # Docker image of `ketch server` (Dockerfile, entrypoint, compose
(`pinnedDownloadFields`) are saved too, with a warning, as those replace them at the next start.
A file that cannot be read or written fails the save, untouched, and the next change saves the
failed fields again
- `ketch server` runs the speed modes of `[speed]` (`serverSpeedModes`), which clients switch and
it follows on its own, and saves them through the same `ServerConfigFile` (`update`, under the
lock its saves take): `[speed]` as they change, the cap as `[download] speedLimit` only once it
differs from the one it started with (with the same warning when a flag or `KETCH_SPEED_LIMIT`
sets it), and `[ui] observedPeak`. `onConfigChanged` gets the standing cap as `speedLimit`, as
the limit in effect is the slow lane's while it holds. Its banner names a mode other than full
speed
- `ketch server` and `ketch mcp --standalone` save the torrent settings their clients change
(`PUT /api/torrents/settings`) to `[torrent]` of their config file (`--config`, else the
default; `TorrentSettingsSaver`): each change is written over the file as it is then, only the
Expand Down Expand Up @@ -2527,7 +2575,7 @@ docker/ # Docker image of `ketch server` (Dockerfile, entrypoint, compose
"FtpClient", "WebDavSource", "TorrentSource", "TorrentEngine", "TorrentSession",
"TorrentSwarm", "TorrentTracker", "TorrentWebSeed", "TorrentLsd", "TrackerList",
"TorrentController", "TorrentControls", "Connections", "RemoteKetch", "RemoteTask",
"RemotePairing", "RemoteTorrents", "TokenBucket", "SqliteStore",
"RemotePairing", "RemoteTorrents", "TokenBucket", "SpeedModes", "SqliteStore",
"SqliteDriver", "ConfigStore", "KetchServer", "ServerRoutes", "DownloadRoutes", "TorrentRoutes",
"ConnectionRoutes", "CountryDatabase", "EventRoutes", "StreamRoutes", "EventHub", "Pairing", "McpStdio",
"GitHubReleases", "Hooks", "Mirrors", "MetalinkSource", "ServerConfig";
Expand Down
3 changes: 2 additions & 1 deletion README.de.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@ Versionen: die Desktop-App über Einstellungen → Über, die Kommandozeile mit
### Deine Bandbreite im Griff

- **Geschwindigkeitsmodi** — Volle Geschwindigkeit, **Drosselung** für mehr Spielraum bei
Videoanrufen oder **Automatisch**, das nach einem Wochenplan zwischen beiden wechselt.
Videoanrufen oder **Automatisch**, das nach einem Wochenplan zwischen beiden wechselt. Jedes
Gerät führt seine eigenen aus, sodass ein NAS seinen Plan auch einhält, wenn das Handy fehlt.
- **Jeden Download einzeln steuern** — Ändere Tempolimits, Verbindungszahlen und Prioritäten im
laufenden Betrieb. **Dringend** pausiert einen weniger wichtigen Download und startet sofort.
- **Eine flexible Warteschlange** — Begrenze gleichzeitige Downloads insgesamt und pro Website,
Expand Down
3 changes: 2 additions & 1 deletion README.es.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ publicadas aquí: la aplicación desde Ajustes → Acerca de y la línea de coma
### Controla tu ancho de banda

- **Modos de velocidad** — Velocidad máxima, **Modo lento** para dejar espacio a una videollamada o
**Automático**, que alterna entre ambos según un horario semanal.
**Automático**, que alterna entre ambos según un horario semanal. Cada dispositivo aplica los
suyos, así que un NAS sigue su horario aunque el teléfono que lo configuró no esté.
- **Control por descarga** — Cambia límites de velocidad, conexiones y prioridades durante la
descarga. **Urgente** pausa una descarga menos importante para empezar de inmediato.
- **Una cola a tu medida** — Limita las descargas simultáneas y por sitio, o programa una descarga
Expand Down
3 changes: 2 additions & 1 deletion README.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ publiées ici : l'application via Réglages → À propos, la ligne de commande
### Maîtrisez votre bande passante

- **Modes de vitesse** — Pleine vitesse, **Vitesse réduite** pour laisser de la place à un appel
vidéo, ou **Automatique**, qui alterne selon un programme hebdomadaire.
vidéo, ou **Automatique**, qui alterne selon un programme hebdomadaire. Chaque appareil applique
les siens : un NAS suit son programme même quand le téléphone qui l'a réglé est absent.
- **Réglages par téléchargement** — Modifiez les limites de vitesse, le nombre de connexions et
les priorités pendant le téléchargement. **Urgente** met en pause un téléchargement moins
important pour démarrer immédiatement.
Expand Down
3 changes: 2 additions & 1 deletion README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ Maven Central で公開しています。[Ketch の組み込みを始める →]
### 帯域幅をコントロール

- **速度モード** — 「フルスピード」、ビデオ通話用に帯域を残す「低速モード」、
週間スケジュールに従って両者を切り替える「自動」を選べます。
週間スケジュールに従って両者を切り替える「自動」を選べます。各デバイスが自身で実行するため、
設定したスマートフォンが離れていても NAS はスケジュールどおりに動きます。
- **ダウンロードごとの設定** — 実行中に速度制限、接続数、優先度を変更できます。
「緊急」は優先度の低いダウンロードを一時停止して、すぐに開始します。
- **柔軟なキュー** — 全体やサイトごとの同時ダウンロード数を制限し、開始時刻も指定できます。
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ the command line with `ketch update` ([how updates work](docs/updates.md)).
### In control of your bandwidth

- **Speed modes** — Full speed, **Slow lane** to leave room for a video call, or **Auto**, which
switches between them on a weekly schedule.
switches between them on a weekly schedule. Each device runs its own, so a NAS keeps to its
schedule while the phone that set it is away.
- **Per-download control** — Speed limits, connection counts and priorities you can change while
a download runs. **Urgent** pauses a less important download to start right away.
- **A queue that behaves** — Limit how many downloads run at once and per site, and start a
Expand Down
3 changes: 2 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ Ketch 将每个下载任务拆分为多个并行连接,并实时显示各连
### 掌控带宽

- **速度模式** — 使用「全速」,用「低速模式」为视频通话留出带宽,
或使用「自动」按每周计划在两者之间切换。
或使用「自动」按每周计划在两者之间切换。每台设备自行执行,
即使设置它的手机不在,NAS 也会按计划运行。
- **单个下载任务控制** — 下载过程中可调整限速、连接数和优先级。
「紧急」会暂停优先级较低的任务,让当前任务立即开始。
- **灵活的队列** — 限制同时运行的下载任务数及每个网站的并发数,也可指定稍后的开始时间。
Expand Down
Loading
Loading