feat(daemon): say what to do next on startup, and prove multiple clients agree

Starting the daemon printed 'ready' and nothing else, so the token and the
next step were both somewhere else. It now prints what other commands can do
from here, and when --bind is given, the token itself with the two config
commands a remote client needs. All on stderr, so stdout stays empty and
daemon still composes in a pipeline.

Adds the guarantee daemon mode is for as a test: eight clients connected at
once are each answered correctly, none has its replies crossed with another's,
and the daemon still counts all eight. Plus 250 connect-and-drop cycles, well
past the connection cap, to catch a per-connection resource that is not
released — the shape a daemon that works for a while and then refuses
everybody would take.

A connection the daemon drops before it can be served now logs why at warning
level. A client only ever sees 'the connection closed', so that line is the
only account of the reason.
This commit is contained in:
Michel de Graaf
2026-07-27 14:39:31 +02:00
parent 2a906df1b5
commit 01152d91f6
8 changed files with 226 additions and 66 deletions
+14 -14
View File
@@ -228,7 +228,7 @@ const Daemon = () => (
<Section>
<SectionHead eyebrow="5 · the daemon" title="One login, however many commands.">
Soulseek allows one session per account. Run a daemon and everything else borrows
it &mdash; no second login, nothing to configure.
it. No second login, nothing to configure.
</SectionHead>
<Cols start>
<Terminal
@@ -237,7 +237,7 @@ const Daemon = () => (
{ t: 'cmd', text: 'soulseek-rs daemon &' },
{ t: 'cm', text: '# once; it holds the session' },
{ t: 'cmd', text: 'soulseek-rs search "netlabel 2004" --json' },
{ t: 'cm', text: '# no login, no flags — it finds the daemon' },
{ t: 'cm', text: '# no login, no flags; it finds the daemon' },
{ t: 'cmd', text: 'soulseek-rs' },
{ t: 'cm', text: '# the TUI attaches to the same session' },
{ t: 'cmd', text: 'soulseek-rs daemon status' },
@@ -251,10 +251,10 @@ const Daemon = () => (
running, everything behaves exactly as it did before.
</p>
<p>
Why bother: the server permits a single session per account, so two one-off
commands at once either cut each other off or push you into a throwaway
username per run &mdash; account churn on a network other people maintain. A
daemon is one login for as long as you work, one share index instead of a
Why bother: the server permits a single session per account. Two one-off
commands at once either cut each other off, or push you into a throwaway
username per run, which is account churn on a network other people maintain.
A daemon is one login for as long as you work, one share index instead of a
rescan every invocation, and downloads that outlive the command that queued
them. If you drive this from a script or an agent, start one.
</p>
@@ -293,16 +293,16 @@ const DownloadBox = () => (
/>
<Prose>
<p>
Your laptop needs no Soulseek account of its own &mdash; it is using the box&rsquo;s.
Queue what you want, then close the lid: the downloads keep going, because they
Your laptop needs no Soulseek account of its own; it uses the box&rsquo;s. Queue
what you want, then close the lid. The downloads keep going, because they
belong to the daemon rather than to the command that asked for them. Come back
tomorrow, run <Code>daemon status</Code>, and pick up where you left off. The
files are on the box, which is where you wanted them.
</p>
<p>
There is no encryption on that port. On a home network that is usually fine;
over the internet, reach it through SSH instead &mdash;{' '}
<Code>ssh -L 5030:localhost:5030 nas</Code>, or just run the commands on the box.
There is no encryption on that port. On a home network that is usually fine.
Over the internet, reach it through SSH instead:{' '}
<Code>ssh -L 5030:localhost:5030 nas</Code>, or run the commands on the box.
</p>
</Prose>
</Cols>
@@ -325,9 +325,9 @@ const OpenSchema = () => (
</p>
<p>
OpenRPC is the JSON-RPC counterpart to OpenAPI: point a generator at it and you
get typed bindings in whatever language you are working in. An Android or iOS app
that queues downloads on your home server from the sofa, a small web page showing
what is transferring &mdash; you have everything you need and nobody to ask.
get typed bindings in whatever language you are working in. Want an Android or
iOS app that queues downloads on your home server from the sofa, or a web page
showing what is transferring? You have what you need and nobody to ask.
</p>
<p>
Searches, transfers, rooms, private messages and shares are all on the same