feat: pos network download restart + smart retry + systemd healer — outage-resilient downloads
restart <gid>: re-queue from history — torrents via rebuilt magnet
(urn:btih: + &tr= trackers), HTTP via original URIs keeping dir/out;
--continue=true resumes partials, complete files verify instantly.
retry <gid|all>: waits out internet outages (NET_PROBE seam,
--interval/--max-wait), re-queues and re-verifies; aria2 error 3 = real
problem → diagnosed + marked permanent (url:/bt: ids in download.retry,
skipped by retry all, manual restart overrides); --once/--quiet for the
healer timer.
Healer: pos-aria2-retry.{service,timer} user units — arms on download
start (add/torrent/metalink/restart), disables when nothing left.
watch <gid> auto-restarts after an outage.
Fixes from stub-suite review: ensure_healer missing from submit paths;
RESTART_NAME lost across do_restart subshell (download_name helper);
restart exited 1 (tmux test as last statement).
Stub harness (/tmp/opencode/dl-test) 119/119 green; make gen && make check green.
Docs: POS.md rows, howto/network.md outage recipe, SYSTEMD.md user units.
This commit is contained in:
@@ -591,7 +591,7 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
|
||||
| `bin/pos-media-mp3` | 80 | Download audio as MP3 (yt-dlp) |
|
||||
| `bin/pos-media-mp4` | 126 | Download video as MP4 (smart/interactive format select) |
|
||||
| `bin/pos-network-checkport` | 496 | Check TCP/UDP port reachability (nmap, or bash/nc fallback) + local interface view |
|
||||
| `bin/pos-network-download` | 558 | aria2 RPC daemon + queue control (add/torrent/metalink, watch, limits) |
|
||||
| `bin/pos-network-download` | 874 | aria2 RPC daemon + queue control (add/torrent/metalink, watch, limits) |
|
||||
| `bin/pos-network-hotspot` | 93 | Wi-Fi hotspot via create_ap + wihotspot-gui |
|
||||
| `bin/pos-network-ip` | 69 | Show interfaces, routes, public IP + location |
|
||||
| `bin/pos-network-scan` | 271 | Parallel ping sweep of CIDR |
|
||||
|
||||
@@ -129,9 +129,13 @@ Runs a persistent `aria2c` JSON-RPC daemon (`localhost:6800`) as a **systemd use
|
||||
| `pos network download limit [gid] <speed>` | Speed limit, global or per-download (`--upload` = upload speed; `0` = unlimited); accepts `2M`/`512K` |
|
||||
| `pos network download set <k=v>...` | Set global aria2 options (`--gid <gid>` = per-download) |
|
||||
| `pos network download watch [gid]` | Live table, 2 s refresh; with a GID it exits when that download completes |
|
||||
| `pos network download restart <gid>` | Re-queue a finished/errored download from history: torrents re-add via magnet (info-hash + trackers), HTTP via their original URLs — `--continue=true` resumes partial files, a complete file re-verifies instantly. Options `--dir`, `--seed`, `--split`, `--tmux` |
|
||||
| `pos network download retry <gid\|all>` | Smart retry of errored downloads: waits out internet outages (poll `--interval`, give up after `--max-wait`), then re-queues and re-verifies. Sources failing with aria2 error 3 are marked permanent in `~/.config/linux_post_install/download.retry` (id = `url:<uri>` / `bt:<infohash>`) and skipped by `retry all` — manual `restart` overrides. `--once` (healer timer mode) skips the wait and exits 0 even on failure; `--quiet` silences output. Options `--dir`, `--seed`, `--split`, `--tmux` |
|
||||
|
||||
**`--tmux`:** after enqueueing, `add`/`torrent`/`metalink` open a detached tmux session `dl-<name>` running `watch <gid>` (name from `--out` or the URL basename, sanitized and truncated to 40 chars; `-2` suffix on collision). The session closes itself when the download finishes — attach with `tmux attach -t dl-<name>`.
|
||||
|
||||
**Outage resilience:** `watch <gid>` auto-restarts its download when the network comes back (it polls `NET_PROBE`, default `timeout 3 bash -c '</dev/tcp/8.8.8.8/53'`). For unattended machines the **retry healer** timer (`pos-aria2-retry.timer`, systemd **user** scope) runs `retry all --once --quiet` every 2 min; it arms automatically whenever a download starts (`add`/`torrent`/`metalink`/`restart`) and disables itself when no active, waiting, or errored downloads remain. Both are dry-run aware.
|
||||
|
||||
### docker
|
||||
|
||||
| Command | File | Purpose | Configuration |
|
||||
|
||||
@@ -6,6 +6,7 @@ The units installed and enabled by `postinstall.sh`, plus the `pos` bash complet
|
||||
- [`autostart.service`](#autostartservice)
|
||||
- [`ssh-agent.service`](#ssh-agentservice)
|
||||
- [`pos-health.service`](#pos-healthservice)
|
||||
- [Per-user units (`pos network download`)](#per-user-units-pos-network-download)
|
||||
- [Feature-flag gating](#feature-flag-gating)
|
||||
- [Bash completion](#bash-completion)
|
||||
|
||||
@@ -88,6 +89,31 @@ The service is `Type=oneshot` and is driven **only** by its companion `pos-healt
|
||||
|
||||
---
|
||||
|
||||
## Per-user units (`pos network download`)
|
||||
|
||||
The aria2 download tool installs **user-scope** units (not by postinstall):
|
||||
written to `~/.config/systemd/user/` and enabled with `systemctl --user` on
|
||||
first use, so they need no sudo.
|
||||
|
||||
- `pos-aria2.service` — the daemon (`Type=simple`, `Restart=on-failure`,
|
||||
`WantedBy=default.target`): runs `aria2c --enable-rpc
|
||||
--rpc-listen-port=6800 --rpc-secret=… --dir=$HOME/Downloads --continue=true
|
||||
--max-connection-per-server=16 --split=16 --seed-time=0`. `pos network
|
||||
download start` generates the `RPC_SECRET` into
|
||||
`~/.config/linux_post_install/download.env` (chmod 600).
|
||||
- `pos-aria2-retry.service` — `Type=oneshot`; `ExecStart=<pos> network download
|
||||
retry all --once --quiet` (runner = `/usr/local/bin/pos-network-download`,
|
||||
repo-path fallback with a warning). Driven only by its companion timer.
|
||||
- `pos-aria2-retry.timer` — `OnUnitActiveSec=2min` + `OnBootSec=2min`,
|
||||
`AccuracySec=30s`, `Persistent=true`, `WantedBy=timers.target`. Arms itself
|
||||
when a download starts (`add`/`torrent`/`metalink`/`restart`) and disables
|
||||
itself when no active, waiting, or errored downloads remain.
|
||||
|
||||
On headless boxes `pos network download start` prints a `loginctl
|
||||
enable-linger` warning so the user units survive logout.
|
||||
|
||||
---
|
||||
|
||||
## Feature-flag gating
|
||||
|
||||
The systemd loop in `postinstall.sh` special-cases two units:
|
||||
|
||||
+21
-2
@@ -1,7 +1,8 @@
|
||||
# How-To: `pos network`
|
||||
|
||||
Networking day-to-day: IP/diagnostic info, Wi-Fi hotspots, host discovery, and
|
||||
port checks. Tools: `ip`, `hotspot`, `scan`, `checkport`.
|
||||
Networking day-to-day: IP/diagnostic info, Wi-Fi hotspots, host discovery, port
|
||||
checks, and the aria2 download daemon. Tools: `ip`, `hotspot`, `scan`,
|
||||
`checkport`, `download`.
|
||||
|
||||
| Tool | What it does |
|
||||
|------|--------------|
|
||||
@@ -9,6 +10,7 @@ port checks. Tools: `ip`, `hotspot`, `scan`, `checkport`.
|
||||
| `pos network hotspot` | Wi-Fi AP via `create_ap` / `wihotspot-gui` |
|
||||
| `pos network scan` | Two-phase host discovery with `nmap` |
|
||||
| `pos network checkport` | Is a TCP port open on a host? |
|
||||
| `pos network download` | aria2 download daemon + queue control (add/torrent/metalink, watch, limits) |
|
||||
|
||||
---
|
||||
|
||||
@@ -154,6 +156,23 @@ download status`; if the unit failed, check the journal with
|
||||
`journalctl --user -u pos-aria2 -n 50`. Seed/limit knobs live on `add`/`torrent`
|
||||
(`--seed`, `--split`) and on `limit`/`set` for the running queue.
|
||||
|
||||
**Recipe: survive a long internet outage.** A download that fails while the
|
||||
internet is down is stopped with `error` status — the retry machinery brings it
|
||||
back without you:
|
||||
|
||||
```bash
|
||||
pos network download add https://example.com/big.iso # arms the retry healer
|
||||
# ...router dies for 3 hours; big.iso sits in STOPPED/error...
|
||||
pos network download retry all # waits for connectivity, re-queues, verifies
|
||||
pos network download watch 2e9dffc4 # or: watch a live one — auto-restarts when back
|
||||
```
|
||||
|
||||
The healer timer (`pos-aria2-retry.timer`) already runs `retry all` every 2 min
|
||||
on its own once a download started, so no shell is needed on headless boxes.
|
||||
`retry <gid>` for one download; sources that fail with a *real* 404/410 are
|
||||
remembered in `~/.config/linux_post_install/download.retry` and skipped by
|
||||
`retry all` — `pos network download restart <gid>` re-queues them by hand.
|
||||
|
||||
---
|
||||
|
||||
## Related
|
||||
|
||||
Reference in New Issue
Block a user