docs: publish archive control design set
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
# 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 supported 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 | Advertise exact versions and select adapter |
|
||||
| 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.
|
||||
|
||||
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.
|
||||
Reference in New Issue
Block a user