# qBittorrent and Syncthing integration Adapters are version-aware boundaries. Core orchestration consumes normalized resource, route, and progress models and never branches on raw service response shapes. ## qBittorrent Web API The implementation targets qBittorrent 4.5–4.6 and 5.x releases and detects the application and Web API versions at startup. The authoritative references are the official [5.0 WebUI API](https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-%28qBittorrent-5.0%29) and [4.1-compatible WebUI API](https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-%28qBittorrent-4.1%29). ### Adapter capabilities | Need | Web API family | Archive Control rule | | --- | --- | --- | | Authenticate | `auth/login`, cookie session | Log no credentials/cookies; reauthenticate once on expiry | | Detect compatibility | `app/version`, `app/webapiVersion`, build info | Require qBittorrent 4.5.0+ because exact metainfo export is mandatory | | List/lookup | `torrents/info`, properties | On-demand and hash-scoped where possible | | Read file state | `torrents/files` | Normalize indices, paths, selected/skipped, size, progress | | Export metainfo | `torrents/export` | Required before staging; fail if exact metainfo unavailable | | Add target | `torrents/add` | Explicit save path, stopped/paused, manual management | | Set selection | `torrents/filePrio` / selection endpoint | Encode only selected vs skipped, never preserve priority levels | | Recheck | `torrents/recheck` plus state polling | Keep stopped; any download activity fails the job | | Remove entry | `torrents/delete` | Eviction uses `deleteFiles=false` only | The adapter accounts for terminology/behavior changes such as paused versus stopped states. It does not mutate qBittorrent preferences, categories, tags, limits, queueing defaults, or global save-path behavior. ### Inventory normalization For each torrent the client derives canonical v1/v2 identity from reliable API and exported-metainfo data, keeps the qB-local hash separately, and calculates: - total and selected-complete logical bytes; - normalized selected and selected-complete file-index sets; - torrent runtime state; - canonical path flag and content revision; - a save-path fingerprint based on validated path mapping, never a leaked absolute host path. qBittorrent 5 may expose a pure-v2 or hybrid torrent under the first 20 bytes of its v2 hash while separately advertising full `infohash_v1` and `infohash_v2` values. Lookup matches all advertised identities, rejects ambiguous matches, validates the canonical hashes from exported metainfo, and uses only qBittorrent's returned local hash for later mutation calls. Complete content trees are built only for a chosen resource or job preflight. All entries retain qBittorrent's stable torrent file index. Renamed/noncanonical content paths are rejected in v1 because they cannot be transported and merged without ambiguity. ### Verification guard The client captures transfer counters and state before recheck, issues recheck, and watches until success, timeout/stall, or failure. Success requires every selected file complete and no missing/error pieces. Any downloading state or increase in received payload bytes causes immediate stop and a failed job with the affected path/piece detail available from the adapter when possible. ## Syncthing REST and events APIs Clients authenticate using the configured API key header and discover exact service version/capabilities. Primary references are Syncthing's official [REST conventions](https://docs.syncthing.net/dev/rest.html), [configuration model](https://docs.syncthing.net/users/config.html), and [DownloadProgress event](https://docs.syncthing.net/events/downloadprogress.html). ### Discovery and route management The adapter reads system status/version and configuration, then selects folders that: - are `sendreceive`; - map beneath the configured API-visible and local sync roots; - contain exactly the local device and one peer device; - pair one registered cache with one registered archive client; - have a safe relative filesystem path. The folder's native ID becomes `route_id`. Multi-device, send-only, receive-only, out-of-root, or ambiguous folders are reported as unsupported and left unchanged. Syncthing may serialize a folder path relative to its home as `~/...`. The client normalizes that notation beneath the configured API-visible sync root before applying the API-to-local root mapping. Deployments must mirror Syncthing's nested bind mounts into the client so the normalized API path and the client filesystem path refer to the same bytes. Provisioning uses idempotent device and folder configuration updates. Each client receives its peer device ID and optional advertised addresses (`dynamic` by default). It changes only the specific device/folder objects owned by the provisioning command. Read-back verification must agree before nonce exchange begins. ### Transfer progress and completion `DownloadProgress` events are filtered to the job's exact staging prefix and used for live block-level byte estimates. Their percentage is advisory. The adapter falls back to coarser polling if events are unavailable. Final completion combines: - job-prefix files present and matching manifest logical sizes; - Syncthing folder/local-global completion from the official [`db/completion`](https://docs.syncthing.net/rest/db-completion-get.html) response; - per-file state from [`db/file`](https://docs.syncthing.net/v2.0.0/rest/db-file-get.html) where supported; - no relevant outstanding entries from [`db/need`](https://docs.syncthing.net/v1.19.2/rest/db-need-get.html); - valid manifest digest and ready marker. The client can request a scoped rescan after publishing/cleanup. It does not declare transfer success from a `100%` progress event alone. ## Error normalization Each adapter maps transport errors, authentication failure, incompatible API, missing torrent/folder, stale external state, permission failures, and service health into stable protocol error codes. Responses and exception text are redacted before logging because service products may echo URLs, headers, or filesystem paths. Authentication failures are permanent until configuration changes; transient service absence moves an active job to waiting. ## Bot-free control adapter The loopback-only test adapter calls the same `ArchiveControlService` used by Telegram: - `GET /test/v1/resources?operation=archive|unarchive|evict_cache` performs a complete, paged, on-demand summary scan and reconciles placements. - `GET /test/v1/resources/{info_hash}/tree?client_id={client}` fetches one complete revisioned content tree. - `POST /test/v1/jobs/preview` fetches fresh source and target summaries and full trees, expands optional `selected_paths` or `selected_file_indices`, and returns an immutable definition plus its `preview_revision`. - `POST /test/v1/jobs` consumes `{preview_revision, definition}` and rejects a deterministic protobuf SHA-256 mismatch. - Job list/detail/cancel/hide/clear endpoints expose the shared ownerless job state, while `GET /test/v1/placements` exposes reconciled generations. Lower-level inventory-query, route, scheduler, and backup endpoints remain for contract and failure-injection tests. This adapter must never bind outside loopback.