webui: replace nautctl web server with a loadable plugin
Drop the web UI that was compiled into nautctl and serve the torrent-ui front end (../torrent-ui/public) from a native plugin (plugins/webui) loaded via `nautd --plugin`. The plugin talks to the engine only through the host call_rpc ABI and adapts the daemon's RPC surface to the qBittorrent-style contract the UI expects (snapshot/SSE, torrent detail tabs, add/delete, cookie auth). Also folds in the daemon refactor that owns per-torrent worker threads and the swarm engine (naut_swarm) used by the plugin's data source. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
50a357968a
commit
8dde48c05a
27 changed files with 2706 additions and 403 deletions
113
README.md
113
README.md
|
|
@ -25,6 +25,8 @@ src/peer/ wire protocol, MSE/RC4, BEP-10, ut_metadata, and PEX
|
|||
apps/echo/ Phase 1 gate: io_uring echo server on the buffer pool
|
||||
apps/leech/ Phase 3 gate: verified single-peer download
|
||||
apps/swarm/ tracker/DHT discovery, magnets, and concurrent peers
|
||||
apps/nautctl/ thin CLI frontend over daemon RPC
|
||||
plugins/webui/ daemon plugin that serves ../torrent-ui as the web panel
|
||||
tests/unit/ unit + concurrency tests
|
||||
```
|
||||
|
||||
|
|
@ -35,6 +37,13 @@ cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
|
|||
ninja -C build
|
||||
ctest --test-dir build --output-on-failure
|
||||
|
||||
# portable daemon/client build with Jansson and Lua embedded
|
||||
cmake -S . -B build-standalone -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release -DNAUT_STANDALONE=ON
|
||||
ninja -C build-standalone nautd nautctl
|
||||
ldd build-standalone/nautd
|
||||
ldd build-standalone/nautctl
|
||||
|
||||
# sanitizer build (address|thread|undefined)
|
||||
cmake -S . -B build-tsan -G Ninja -DCMAKE_BUILD_TYPE=Debug -DNAUT_SAN=thread
|
||||
ninja -C build-tsan && ./build-tsan/test_buf
|
||||
|
|
@ -42,13 +51,16 @@ ninja -C build-tsan && ./build-tsan/test_buf
|
|||
# run the Phase 1 echo gate
|
||||
./build/naut_echo 9000
|
||||
|
||||
# download from explicit peers, or omit them to use the torrent's trackers
|
||||
./build/naut_swarm file.torrent output/ 192.0.2.10:6881 192.0.2.11:6881
|
||||
./build/naut_swarm file.torrent output/
|
||||
# start the engine, then add and inspect downloads through its RPC frontend
|
||||
./build/nautd
|
||||
./build/nautctl add file.torrent output/
|
||||
./build/nautctl list
|
||||
./build/nautctl show 1
|
||||
./build/nautctl events
|
||||
|
||||
# trackerless magnet start through DHT (override bootstraps when needed)
|
||||
./build/naut_swarm 'magnet:?xt=urn:btih:...' output/
|
||||
NAUT_DHT_BOOTSTRAP=127.0.0.1:6881 ./build/naut_swarm 'magnet:?xt=urn:btih:...' output/
|
||||
# explicit peers and trackerless magnets use the same daemon workflow
|
||||
./build/nautctl add file.torrent output/ 192.0.2.10:6881
|
||||
./build/nautctl add 'magnet:?xt=urn:btih:...' output/
|
||||
|
||||
# force an encrypted single-peer MSE/RC4 connection
|
||||
./build/naut_leech --mse file.torrent output/ 192.0.2.10 6881
|
||||
|
|
@ -59,41 +71,86 @@ NAUT_DHT_BOOTSTRAP=127.0.0.1:6881 ./build/naut_swarm 'magnet:?xt=urn:btih:...' o
|
|||
bash tests/integration/run_echo_scale.sh ./build/naut_echo
|
||||
|
||||
# optional data-path tuning
|
||||
NAUT_DIRECT_IO=1 NAUT_WORKERS=8 ./build/naut_swarm file.torrent output/
|
||||
NAUT_DIRECT_IO=1 NAUT_WORKERS=8 ./build/nautd
|
||||
NAUT_CPU=2 NAUT_SQPOLL=1 NAUT_HUGEPAGES=1 NAUT_NUMA_NODE=0 ./build/naut_echo 9000
|
||||
```
|
||||
|
||||
Requirements: Linux ≥ 6.0, `liburing` (≥ 2.x), OpenSSL `libcrypto`, Jansson,
|
||||
Lua, CMake ≥ 3.20, gcc/clang, Ninja.
|
||||
|
||||
`NAUT_STANDALONE=ON` downloads hash-pinned Jansson 2.14.1 and Lua 5.4.8
|
||||
sources at configure time and statically embeds them in `nautd` and `nautctl`.
|
||||
The resulting executables still use the host's glibc/ELF loader intentionally:
|
||||
fully static glibc breaks normal DNS/NSS behavior and native `.so` plugins.
|
||||
Release builds target a portable CPU baseline. Use `-DNAUT_NATIVE=ON` only for
|
||||
a local build that will run on the same CPU family as the build machine.
|
||||
|
||||
## Daemon, RPC, plugins, and scripts
|
||||
|
||||
Phase 7 adds a headless control process and thin CLI over a versioned,
|
||||
length-prefixed JSON protocol on a Unix socket:
|
||||
`nautd` is the application engine: it owns torrent workers, storage, scripts,
|
||||
plugins, progress, and lifecycle. `nautctl` is one thin frontend over a
|
||||
versioned, length-prefixed JSON protocol on a Unix socket; a desktop or web
|
||||
panel can use the same RPC surface.
|
||||
|
||||
```sh
|
||||
./build/nautd \
|
||||
--socket /tmp/nautd.sock \
|
||||
--plugin ./build/naut_example.so \
|
||||
--script ./tests/fixtures/phase7.lua
|
||||
--plugin ./build/naut_example.so
|
||||
|
||||
./build/nautctl ping
|
||||
./build/nautctl plugins
|
||||
./build/nautctl status
|
||||
./build/nautctl script ./examples/anime_sort.lua
|
||||
./build/nautctl add show.torrent /downloads/show
|
||||
./build/nautctl list
|
||||
./build/nautctl events
|
||||
```
|
||||
|
||||
`nautctl` accepts an optional JSON value after the method:
|
||||
The convenience commands cover normal operation:
|
||||
|
||||
```sh
|
||||
# register a torrent's storage so a move command can resolve + relocate its files
|
||||
./build/nautctl add_torrent \
|
||||
'{"torrent_id":7,"torrent":"file.torrent","root":"output/"}'
|
||||
./build/nautctl emit \
|
||||
'{"type":"torrent_finished","torrent_id":7}'
|
||||
./build/nautctl add file.torrent output/ [IP:PORT ...]
|
||||
./build/nautctl list
|
||||
./build/nautctl show 1
|
||||
./build/nautctl remove 1
|
||||
./build/nautctl script rules.lua
|
||||
./build/nautctl unscript
|
||||
./build/nautctl shutdown
|
||||
```
|
||||
|
||||
For tooling and plugin methods, the generic form remains
|
||||
`nautctl METHOD [PARAMS_JSON]`.
|
||||
|
||||
### Web panel
|
||||
|
||||
The web panel is a daemon plugin, not part of `nautctl`. It serves the static
|
||||
frontend from `../torrent-ui/public` by default and adapts that UI's `/api/*`
|
||||
contract to Naut's daemon RPC surface:
|
||||
|
||||
```sh
|
||||
NAUT_WEBUI_ROOT=../torrent-ui/public \
|
||||
NAUT_AUTH_PASSWORD='change-me' \
|
||||
./build/nautd --socket /tmp/nautd.sock --plugin ./build/naut_webui.so
|
||||
# open http://127.0.0.1:8080
|
||||
```
|
||||
|
||||
Configuration:
|
||||
|
||||
```sh
|
||||
NAUT_WEBUI_HOST=127.0.0.1 # default
|
||||
NAUT_WEBUI_PORT=8080 # default
|
||||
NAUT_WEBUI_ROOT=../torrent-ui/public
|
||||
NAUT_AUTH_USER=admin # default
|
||||
NAUT_AUTH_PASSWORD=change-me # generated and logged if omitted
|
||||
NAUT_WEBUI_SAVE_PATH=/downloads # default add-torrent destination
|
||||
```
|
||||
|
||||
The plugin implements the stable `torrent-ui` API surface: cookie login,
|
||||
`/api/snapshot`, `/api/stream` Server-Sent Events, `/api/meta`, torrent detail
|
||||
tabs, add/remove, and `/api/plugins` loading ES modules from
|
||||
`public/plugins/plugins.json`. Some advanced qBittorrent-style controls in the
|
||||
UI are accepted as no-ops until Naut grows matching daemon RPC methods.
|
||||
|
||||
The native ABI is declared in `include/naut/naut_plugin.h`. Plugins export
|
||||
`naut_plugin_register()`, receive the versioned host API, and may register RPC
|
||||
methods, storage backends, and event handlers. `plugins/example/example.c`
|
||||
|
|
@ -105,12 +162,11 @@ hooks are `on_torrent_added`, `on_piece_complete`, `on_file_complete`,
|
|||
filesystem, process, package-loading, debug, and raw chunk-loading globals
|
||||
(`os`, `io`, `package`/`require`, `debug`, `dofile`/`loadfile`, and
|
||||
`load`/`loadstring` — the bytecode loaders are denied so a crafted binary chunk
|
||||
can't escape the VM). `naut.move_file()` submits a bounded command from the
|
||||
script thread to the daemon owner thread; the owner resolves it through the
|
||||
torrent registry (`naut_session`) and performs the relocate with
|
||||
`naut_storage_relocate()`. Register a torrent's storage first with the
|
||||
`add_torrent` RPC so the id resolves. `phase7_extensibility` drives this
|
||||
end to end and asserts the file actually moves on disk.
|
||||
can't escape the VM). `naut.move_file()` submits a bounded command to the
|
||||
worker that owns the torrent. That worker performs
|
||||
`naut_storage_relocate()` and keeps tracking the file at its new path.
|
||||
`phase7_extensibility` drives a real daemon-owned download end to end and
|
||||
asserts the moved file byte-for-byte.
|
||||
|
||||
The full script-visible surface — every event hook, the `event` object's
|
||||
fields, and the `naut` API table — is documented in
|
||||
|
|
@ -211,9 +267,8 @@ embedded Anitomy-style filename parser ([`examples/`](examples/)).
|
|||
piece verifies — before the torrent finishes — and `naut_storage_relocate()`
|
||||
moves that file out safely (even mid-download, while other files' pieces are
|
||||
still arriving). `test_filemove` proves a file is relocated mid-download with
|
||||
no corruption. The scripting layer (Phase 7) forwards the event to an
|
||||
`on_file_complete` hook and exposes `move_file`; the daemon resolves the
|
||||
command through the `naut_session` torrent registry (`src/session/session.c`)
|
||||
and calls `naut_storage_relocate()` on its owner thread. `phase7_extensibility`
|
||||
exercises the whole chain — script thread → bounded queue → owner thread →
|
||||
storage — and asserts the file moves on disk.
|
||||
no corruption. The scripting layer forwards the event to an
|
||||
`on_file_complete` hook and exposes `move_file`; the daemon queues the command
|
||||
back to the worker that owns the torrent's storage.
|
||||
`phase7_extensibility` exercises the whole chain — download worker → script
|
||||
thread → bounded command queue → download worker — and checks the moved bytes.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue