feat(config): remember which daemon to use, and document the download box

A remote daemon could only be named with --daemon and --daemon-token on every
invocation, which made the one workflow daemon mode is most useful for — a
headless download box driven from a laptop — a matter of retyping a 64-character
token. Both are now config keys, so it is two 'config set' commands once and
plain commands thereafter.

Fixes a bug that setup exposed: the up-front daemon status probe read the token
from the command line only, so with the token in the config file it failed
silently and every command that names a path or a server fell back to this
machine's defaults — whoami claiming downloads land in the laptop's home
directory when they land on the box. The regression test was checked by
reintroducing the bug.

Documents the download-box workflow in plain terms across the README, the
website and the agent skill, and says plainly that the schema is open: OpenRPC,
served by the daemon itself, so an Android, iOS or web remote is something
anyone can build without asking.
This commit is contained in:
Michel de Graaf
2026-07-27 14:11:51 +02:00
parent 3f914c4425
commit 2a906df1b5
6 changed files with 279 additions and 29 deletions
+64 -11
View File
@@ -165,23 +165,76 @@ file *and* update a running daemon, so a folder is served straight away rather
than at its next start. Files land on the *daemon's* filesystem — `daemon
status` reports where.
For a client on another machine, bind a TCP port as well and give it the
token:
#### A download box you drive from your laptop
This is what daemon mode is really for. Put soulseek-rs on the machine that
should be doing the downloading — a home server, a NAS, a Raspberry Pi, a
seedbox — and drive it from wherever you happen to be.
**On that machine**, run the daemon and let it accept connections from your
network:
```bash
soulseek-rs daemon --bind 0.0.0.0:5030
soulseek-rs daemon token # the secret that connection needs
soulseek-rs daemon token # copy this, you need it once
```
There is no transport encryption, so put SSH or a TLS proxy in front of
anything non-loopback.
It needs no terminal and no one logged in; hand it to systemd or launchd and
forget about it.
Third-party clients are first-class: the daemon speaks newline-delimited
JSON-RPC 2.0 described by an [OpenRPC](https://open-rpc.org) document
([`docs/openrpc.json`](docs/openrpc.json), also served by `rpc.discover`), so
a generator produces a client in any language.
[`docs/daemon-protocol.md`](docs/daemon-protocol.md) covers the framing, the
handshake, and how pushed events behave.
**On your laptop**, say once where it lives:
```bash
soulseek-rs config set daemon nas.local:5030
soulseek-rs config set daemon_token <the token you copied>
```
That is the whole setup. Every command from now on is the normal command —
your laptop needs no Soulseek account of its own, because it is using the
server's:
```bash
soulseek-rs search 'aphex twin' --json
soulseek-rs get 'selected ambient works'
soulseek-rs daemon status # what it's doing right now
```
Then close the lid. **The downloads keep going**, because they belong to the
daemon, not to the command that asked for them or to the laptop that ran it.
Come back tomorrow, run `soulseek-rs daemon status` again, and pick up where
you left off. The files are on the download box, which is where you wanted
them.
When you are finished with it:
```bash
soulseek-rs daemon stop # shuts down the remote daemon
```
One caution: there is no encryption on that TCP port. On a home network that
is usually fine. Over the internet, don't expose it — reach it through SSH
instead, either with a tunnel (`ssh -L 5030:localhost:5030 nas`) or by running
the commands over `ssh` on the box itself.
#### Build your own remote
The daemon's interface is open and documented, so nothing about it is specific
to this CLI. It speaks newline-delimited JSON-RPC 2.0 over a socket, described
by an [OpenRPC](https://open-rpc.org) document —
[`docs/openrpc.json`](docs/openrpc.json), which the daemon also serves itself
via `rpc.discover`, so a client can ask a running daemon what it can do.
OpenRPC is the JSON-RPC counterpart to OpenAPI: point a client generator at
that document and you get typed bindings in whatever language you are working
in. If you want an Android or iOS app that queues downloads on your home
server from the sofa, or a small web page that shows what is transferring, you
have everything you need and no permission to ask for. Searches, transfers,
rooms, private messages and shares are all on the same interface this CLI
uses — there is no private back channel it keeps for itself, and live updates
are pushed to every connected client rather than polled for.
[`docs/daemon-protocol.md`](docs/daemon-protocol.md) covers the parts a schema
cannot state: the framing, the handshake, and how pushed events behave.
### Scripting