7.0 KiB
Telegram UX
Archive Control adds a separate handler/module to the existing bot. Existing commands and conversations retain their current behavior.
Authorization and shared state
The handler accepts private-chat users through the existing @username
allowlist for compatibility. A configured numeric Telegram user ID may pin an
entry; when pinned, both the ID and current allowlist rule must match. This
mitigates username reassignment while permitting a staged migration.
Jobs, placements, events, queues, and routes are shared administrative state and store no initiating-user identity. UI session state records only what is needed to resume the conversation/view and expires after 24 hours by default.
Entry point and navigation
The existing “Archive/Unarchive Resources” entry opens one message:
What do you want to do?
[ Archive ] [ Unarchive ]
[ Evict Cache ] [ Job Status ]
All subsequent pages edit this message. Cancel closes the active selection
flow. Back returns one level while retaining validated filters/selections.
Callback payloads contain opaque session/action IDs, not resource names or
paths, and are validated against persisted session revision and expiry.
Archive and unarchive flow
1. Select source resource
Control queries relevant online nodes on demand. It groups identical resource and selection replicas into one display choice while showing distinct selections separately. The list is stable-sorted and paged:
Which cached resource do you want to archive? (page 1/3)
filters: `documentary`, `2025`
1. `10.0 GB` Resource A — cache-1
2. `840 MB` Resource B — cache-2
[ 1 ] [ 2 ]
[ Prev ] [ Next ]
[ Remove filters ] [ Cancel ]
Size is selected-complete logical content, not total torrent size. New messages add literal, case-insensitive filters and narrow the current results; Remove filters resets the chain. If no item remains, the message explains whether no source is online, no selected-complete data exists, or filters eliminated all results.
2. Select source replica and target
If multiple identical-selection replicas exist, control may choose a healthy source automatically using deterministic availability/route/free-space rules; otherwise it lists the source replicas. One eligible target is selected automatically; multiple targets are paged. Offline targets are not offered.
A resource already on a target is still eligible when the requested selection has an unverified delta. The UI says that it will extend an existing placement.
3. Confirm or choose one entry
Before showing confirmation, control obtains fresh full trees from source and target and calculates the transfer delta.
Archive Resource A from cache-1 to archive-2?
Requested: 7 files, 10.0 GB
Already verified there: 2 files, 3.1 GB
Will transfer: 5 files, 6.9 GB
[ Confirm ]
[ Choose one entry… ] [ Cancel ]
Choose one entry opens a paged/filterable tree. Only source-selected-complete files/directories are enabled. Selecting a directory expands it to descendant file indices, returns to a freshly recomputed confirmation, and clearly states the chosen subtree. The protocol supports multiple entries from v1, but the initial UI exposes one.
Confirmation uses a persisted, single-use nonce bound to session revision, operation, resource, source, target, selection, fingerprints, and placement generation. Double taps or stale callbacks cannot create a second job.
Evict Cache flow
The first page lists current cache placements. Selecting one triggers fresh queries to the cache and relevant online archive nodes. Each row/confirmation shows coverage:
- enabled when the union of verified archives covers every selected cache file;
- disabled with missing file count/size and unavailable archive explanation otherwise.
There is no override button. Confirmation creates a durable three-step eviction job; it never clears an archive placement or a transfer history row.
Job Status
Status pages show live and retained jobs with operation, resource, endpoints, state, current-step and overall percentages, bytes/s where meaningful, and failure reason. Routine edits are coalesced to one per five seconds by default. Step changes, stall transitions, failures, commit, cleanup-required, and terminal states edit immediately subject to Telegram rate-limit backoff.
State symbols:
| Symbol | Meaning |
|---|---|
| 🟡 | queued/not started |
| 🔵 | active, waiting, or stalled |
| 🟠 | committed but cleanup required |
| 🟢 | succeeded |
| 🔴 | failed or cancelled |
Transfer jobs display five numbered steps: Source Stage/HardLink, Syncthing, Target Merge/Copy, QB Verification, and Staging Cleanup. Route setup and preflight are named, unnumbered pre-steps. Eviction displays Verify Archive Coverage, QB Remove Entry, and Safe File Unlink.
A stalled job keeps its last overall percentage:
🔵 Archive: Resource A — cache-1 → archive-2
[2/5 Syncthing: 63.42%, overall 32.68%, stalled, last progress 38m ago]
Waiting indefinitely; retry occurs when progress/service connectivity returns.
Failure messages include stable phase and clear reason, while hiding secrets and host-only paths.
Clear and remove behavior
Clear Finished hides only green successful jobs and leaves a 30-day default
tombstone. Orange Cleanup Required is not clearable as finished.
Remove One first replaces buttons with current-page job numbers and Cancel.
A successful green row hides immediately. A queued job confirmation deletes
only its queue record/reservation. An active, failed-with-residue, or cancelling
job requires a second confirmation and begins/continues compensation; its row
remains until safe cleanup is known. A committed job can only retry staging
cleanup. No history action deletes a cache placement; that is exclusively Evict
Cache.
Restart and rate-limit behavior
Sessions, pages, filters, chosen entries, confirmation bindings, and live status views are persisted. On bot restart, active status messages resume updates when the stored chat/message remains editable; otherwise users can reopen Job Status without losing job state. “message is not modified” is treated as success. Telegram retry-after values pause/coalesce edits without affecting orchestration.
Interactive test entry
An opt-in Archive Control (Test) entry reuses this same handler with an
independent callback namespace and SQLite database. Its scripted backend
provides comprehensive, empty, and slow-route scenarios without contacting
control/client daemons, qBittorrent, or Syncthing. The production entry,
callback namespace, database, jobs, placements, and other bot features are
unchanged.
The test entry exposes administrator-only /actest controls to reset a
scenario, move a synthetic job through displayed states, fail the next selected
backend call, complete a slow route, inspect recent trace events, or export the
complete JSONL trace. The trace contains callback and filter inputs, session
state transitions, exact message/keyboard renders, backend results/errors, and
job transitions, so a UI report can be reproduced without live storage nodes.