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:
@@ -1,6 +1,7 @@
|
||||
import { Link } from 'react-router'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { ExtLink } from '@/components/ui/ext-link'
|
||||
import { Code } from '@/components/ui/inline-code'
|
||||
import {
|
||||
Cols,
|
||||
@@ -257,18 +258,99 @@ const Daemon = () => (
|
||||
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>
|
||||
</Prose>
|
||||
</Cols>
|
||||
</Section>
|
||||
)
|
||||
|
||||
const DownloadBox = () => (
|
||||
<Section>
|
||||
<SectionHead
|
||||
eyebrow="6 · a download box"
|
||||
title="Put it on the machine that should be downloading."
|
||||
>
|
||||
A home server, a NAS, a Raspberry Pi. It needs no terminal and nobody logged in.
|
||||
You drive it from your laptop, and it keeps working after you close the lid.
|
||||
</SectionHead>
|
||||
<Cols start>
|
||||
<Terminal
|
||||
label="on the box"
|
||||
lines={[
|
||||
{ t: 'cmd', text: 'soulseek-rs daemon --bind 0.0.0.0:5030' },
|
||||
{ t: 'cmd', text: 'soulseek-rs daemon token' },
|
||||
{ t: 'cm', text: '# copy this once' },
|
||||
{ t: 'cm', text: '' },
|
||||
{ t: 'cm', text: '# on your laptop, one-time setup:' },
|
||||
{ t: 'cmd', text: 'soulseek-rs config set daemon nas.local:5030' },
|
||||
{ t: 'cmd', text: 'soulseek-rs config set daemon_token <token>' },
|
||||
{ t: 'cm', text: '' },
|
||||
{ t: 'cm', text: '# then just use it, no flags:' },
|
||||
{ t: 'cmd', text: 'soulseek-rs get "selected ambient works"' },
|
||||
{ t: 'cmd', text: 'soulseek-rs daemon status' },
|
||||
{ t: 'cmd', text: 'soulseek-rs daemon stop' },
|
||||
{ t: 'cm', text: '# shuts down the remote one when you are done' },
|
||||
]}
|
||||
/>
|
||||
<Prose>
|
||||
<p>
|
||||
It speaks newline-delimited JSON-RPC 2.0 described by an{' '}
|
||||
<Code>openrpc.json</Code> the daemon serves itself, so a client generator gives
|
||||
you one in any language. <Code>daemon --bind</Code> adds a TCP port for other
|
||||
machines; those need the token from <Code>daemon token</Code>, while the local
|
||||
socket never does.
|
||||
Your laptop needs no Soulseek account of its own — it is using the box’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 —{' '}
|
||||
<Code>ssh -L 5030:localhost:5030 nas</Code>, or just run the commands on the box.
|
||||
</p>
|
||||
</Prose>
|
||||
</Cols>
|
||||
</Section>
|
||||
)
|
||||
|
||||
const OpenSchema = () => (
|
||||
<Section>
|
||||
<SectionHead eyebrow="7 · build your own" title="The interface is open.">
|
||||
Nothing about the daemon is specific to this CLI. Point a client generator at its
|
||||
schema and write your own remote.
|
||||
</SectionHead>
|
||||
<Cols start>
|
||||
<Prose>
|
||||
<p>
|
||||
It speaks newline-delimited JSON-RPC 2.0, described by an{' '}
|
||||
<ExtLink href="https://open-rpc.org">OpenRPC</ExtLink> document. The daemon
|
||||
serves that document itself through <Code>rpc.discover</Code>, so a client can
|
||||
ask a running daemon what it can do.
|
||||
</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 — you have everything you need and nobody to ask.
|
||||
</p>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</Prose>
|
||||
<Terminal
|
||||
label="any language"
|
||||
lines={[
|
||||
{ t: 'cmd', text: 'soulseek-rs daemon --bind 0.0.0.0:5030' },
|
||||
{ t: 'cm', text: '# then, from your own client:' },
|
||||
{ t: 'out', text: '{"jsonrpc":"2.0","id":1,"method":"auth",' },
|
||||
{ t: 'out', text: ' "params":{"protocol":1,"token":"..."}}' },
|
||||
{ t: 'out', text: '{"jsonrpc":"2.0","id":2,"method":"search.start",' },
|
||||
{ t: 'out', text: ' "params":{"query":"aphex twin"}}' },
|
||||
{ t: 'cm', text: '# events arrive unasked: transfers, chat, uploads' },
|
||||
]}
|
||||
/>
|
||||
</Cols>
|
||||
</Section>
|
||||
)
|
||||
|
||||
const TheRest = () => (
|
||||
<Section band>
|
||||
<Panel title="the rest" className="bg-raised [&>span]:bg-panel">
|
||||
@@ -305,6 +387,8 @@ export const Docs = () => (
|
||||
<Keys />
|
||||
<FromTheShell />
|
||||
<Daemon />
|
||||
<DownloadBox />
|
||||
<OpenSchema />
|
||||
<TheRest />
|
||||
</>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user