docs: cut the padding from the readme

Drop the "This structure allows" bullets justifying a two-crate workspace,
collapse four one-line "To run X:" stubs into a single block, and lose the
adverbs, the emoji heading, and the bolded emphasis.

Project Goals no longer claims a hard no-external-crates rule for the
library, which stopped being the stance; it says lean instead. Also fixes
"Headless mode daemon mode".
This commit is contained in:
Michel de Graaf
2026-07-26 11:45:33 +02:00
parent 45b83546ae
commit 9869b11f6d
+48 -79
View File
@@ -11,15 +11,14 @@ there.**
Search the network, share your files, browse someone's collection, join a room. Search the network, share your files, browse someone's collection, join a room.
It runs over ssh on the machine where your music already lives. It runs over ssh on the machine where your music already lives.
Soulseek is a closed-source P2P file-sharing network from the 2000s, still used Soulseek is a closed-source P2P network from the 2000s, still used by music
by music enthusiasts around the world to share niche music. This repository is enthusiasts to share niche music. This repository is that client plus
that client plus [`soulseek-rs-lib`](./soulseek-rs-lib), the protocol library it [`soulseek-rs-lib`](./soulseek-rs-lib), the protocol library under it.
is built on.
**[re-invention.nl/soulseek-rs](https://re-invention.nl/soulseek-rs/)**: what it **[re-invention.nl/soulseek-rs](https://re-invention.nl/soulseek-rs/)**: what it
does, how to install it, and every `config.toml` setting. does, how to install it, and every `config.toml` setting.
## 🎥 Demo ## Demo
[![soulseek-rs TUI demo](fixtures/demo.svg)](https://re-invention.nl/soulseek-rs/) [![soulseek-rs TUI demo](fixtures/demo.svg)](https://re-invention.nl/soulseek-rs/)
@@ -36,41 +35,31 @@ does, how to install it, and every `config.toml` setting.
connections when a peer can't be reached directly connections when a peer can't be reached directly
- **Automatic port mapping**: opens your listen port via UPnP-IGD and - **Automatic port mapping**: opens your listen port via UPnP-IGD and
NAT-PMP, with a `portmap` subcommand to test your router NAT-PMP, with a `portmap` subcommand to test your router
- **TUI and CLI**: a full terminal interface, plus a one-shot command surface - **TUI and CLI**: a full terminal interface, plus one-shot commands for
built for scripts and agents, where every feature is reachable without a scripts and agents: every feature reachable without a terminal, stdout
terminal, stdout carries records only, and the exit code says what happened carrying records, the exit code saying what happened
## Project Goals ## Project Goals
This project is a learning exercise to explore Rust. I've been using Soulseek A learning exercise in Rust. I've been on Soulseek since the early 2000s, and
since the early 2000s, so it's close to my heart, and the Soulseek protocol is its closed-source protocol is a good way to learn asynchronous network
a closed-source network protocol that provides a great opportunity to learn programming and reverse engineering.
about asynchronous and concurrent network programming and reverse engineering
Since it's a learning project, I have a self-imposed restriction not to use The library stays lean on dependencies and has none today; the client takes
external dependencies in the library. This means I can't use any external them freely.
crates that are not part of the Rust standard library. This is a good challenge
to learn how to build complex systems with only the standard library.
In the client crate, external dependencies are allowed for building a rich
experience. For me, this is a good balance between learning and practicality.
## Planned Features ## Planned Features
- [ ] Headless mode daemon mode with remote control - [ ] Headless daemon mode with remote control
## Project Structure ## Project Structure
This project is organized as a Cargo workspace with two crates: A Cargo workspace with two crates:
- **soulseek-rs-lib** - The core library implementing the Soulseek protocol - **soulseek-rs-lib** - the protocol implementation, for anyone building their
- **soulseek-rs** - A CLI client built on top of the library own client
- **soulseek-rs** - the client built on it, installable with Homebrew or
This structure allows: `cargo install`
- Other developers to build custom Soulseek clients using `soulseek-rs-lib`
- Users to install the ready-made client with Homebrew or `cargo install`
- Clean separation of concerns between protocol implementation and user interface
## Installation ## Installation
@@ -98,7 +87,7 @@ cd soulseek-rs
cargo build --release cargo build --release
``` ```
The binary will be available at `target/release/soulseek-rs`. The binary lands at `target/release/soulseek-rs`.
### For Library Users ### For Library Users
@@ -168,10 +157,9 @@ first: the config commands need no account at all, and `whoami` answers
Eleven of these need no credentials, because they never touch the network: Eleven of these need no credentials, because they never touch the network:
`config path|list|get|set`, `shares list|add|remove`, `portmap` and `config path|list|get|set`, `shares list|add|remove`, `portmap` and
`skills install|uninstall|list`. Note that `skills install|uninstall|list`. `shares status` and `shares reindex` are not
`shares status` and `shares reindex` are not among them, because they report among them: they report what the network will see, which means logging in and
what the network will actually see, which means logging in and letting the scanning the folders.
library scan the folders.
Every command emits records except the four that perform an action. Every command emits records except the four that perform an action.
`room say`, `message send`, `shares add` and `shares remove` say nothing on `room say`, `message send`, `shares add` and `shares remove` say nothing on
@@ -207,10 +195,9 @@ the message field empty. `serve`'s `status` is `uploading`, `completed`,
`cancelled` or `failed`. `user`'s `status` is `online`, `away` or `offline`, `cancelled` or `failed`. `user`'s `status` is `online`, `away` or `offline`,
and any field the server did not answer prints as `-`. and any field the server did not answer prints as `-`.
`--json` carries more than text does, because an object has room for fields a `--json` carries more than text does: `search` adds `duration`, `slots`,
column layout does not: `search` adds `duration`, `slots`, `speed` and `speed` and `free_slot`; `browse` adds `directory`; `download` adds the `user`,
`free_slot`; `browse` adds `directory`; `download` adds the `user`, remote remote `path` and `size` alongside the local `file`; `whoami` adds `listening`,
`path` and `size` alongside the local `file`; `whoami` adds `listening`,
`listen_port` and `download_dir`; `user` adds `privileged` and `listen_port` and `download_dir`; `user` adds `privileged` and
`shared_folders`; `shares status` adds the `directories` array; `serve` adds `shared_folders`; `shares status` adds the `directories` array; `serve` adds
`bytes_sent`, `size`, `speed` and a `reason` on failure; `portmap` adds `bytes_sent`, `size`, `speed` and a `reason` on failure; `portmap` adds
@@ -260,10 +247,10 @@ soulseek-rs user someuser --json | jq -e '.status != "offline"'
[![an agent loading the soulseek-rs skill and downloading tracks](website/public/agent-demo.svg)](https://re-invention.nl/soulseek-rs/#agents) [![an agent loading the soulseek-rs skill and downloading tracks](website/public/agent-demo.svg)](https://re-invention.nl/soulseek-rs/#agents)
The scriptable surface above is meant to be driven by coding agents as much as Coding agents drive the surface above as readily as shell scripts do, but
by shell scripts, but `--help` cannot tell an agent which JSON keys a record `--help` cannot tell an agent which JSON keys a record carries or what exit 4
carries or what exit 4 means. That lives in a skill file shipped inside the means. That lives in a skill file shipped inside the binary, so `cargo install`
binary, so a `cargo install` is all you need to hand it to your agent: is all it takes to hand it over:
```bash ```bash
soulseek-rs skills install soulseek-rs skills install
@@ -301,16 +288,16 @@ seconds (an hour by default), or runs until interrupted with `--follow`.
```bash ```bash
soulseek-rs shares add ~/Music # remembered in config.toml soulseek-rs shares add ~/Music # remembered in config.toml
soulseek-rs shares status # what the network will actually see soulseek-rs shares status # what the network will see
soulseek-rs shares reindex # after adding files on disk soulseek-rs shares reindex # after adding files on disk
soulseek-rs serve --follow soulseek-rs serve --follow
``` ```
Uploads are only visible inside a running `serve`, because every other command Uploads only show up inside a running `serve`; every other command is
is a short-lived process with nothing to serve, so there is no separate short-lived with nothing to serve, so there is no `uploads list`. Managing
`uploads list`. Managing transfers across commands (pause, resume, a queue transfers across commands (pause, resume, a queue that outlives one
that outlives one invocation) needs the resident daemon that `serve` would invocation) needs the resident daemon `serve` would grow into, which is not
grow into; it is not there yet. there yet.
Downloads run under a deadline (`--timeout`, 300s by default) and Downloads run under a deadline (`--timeout`, 300s by default) and
`--max-concurrent-downloads` at a time, so an unattended run always ends. `--max-concurrent-downloads` at a time, so an unattended run always ends.
@@ -387,7 +374,7 @@ In the interactive TUI:
- press `m` to compose, then type `<recipient> <message>` and `Enter` to send; - press `m` to compose, then type `<recipient> <message>` and `Enter` to send;
- press `i` to open the inbox popup listing sent and received messages - press `i` to open the inbox popup listing sent and received messages
(incoming messages arrive automatically while the TUI is open). The `i` (the TUI receives incoming messages while it is open). The `i`
shortcut shows an unread counter, e.g. `i inbox (3)`. shortcut shows an unread counter, e.g. `i inbox (3)`.
### Chat rooms ### Chat rooms
@@ -416,20 +403,20 @@ In the interactive TUI, press `c` to open the chat-rooms popup:
Browsing and downloading are peer-to-peer, so at least one side must accept an Browsing and downloading are peer-to-peer, so at least one side must accept an
incoming connection. When the listener is enabled (the default), the client incoming connection. When the listener is enabled (the default), the client
**automatically tries to open its listen port** on your router via **UPnP-IGD** tries to open its listen port on your router via **UPnP-IGD** and **NAT-PMP**,
and **NAT-PMP**, so firewalled peers and the server can connect back to you. so firewalled peers and the server can connect back. This is best-effort: if
This is best-effort: if your router has UPnP/NAT-PMP disabled it's a no-op and your router has UPnP/NAT-PMP disabled it's a no-op, and a log line tells you to
you'll see a log line suggesting you forward the port manually. forward the port yourself.
- The mapped/forwarded port is your `--listener-port` (env - The mapped/forwarded port is your `--listener-port` (env
`SOULSEEK_LISTENER_PORT`, default `2234`); it is renewed automatically and `SOULSEEK_LISTENER_PORT`, default `2234`); the client renews it while it runs
removed on exit. and removes it on exit.
- If auto-mapping can't get that exact port, forward **TCP 2234** (or whatever - If auto-mapping can't get that port, forward **TCP 2234** (or whatever
`--listener-port` you chose) to this machine on your router. `--listener-port` you chose) to this machine on your router.
- Pass `--no-listener` to turn the listener (and port mapping) off, or - Pass `--no-listener` to turn the listener (and port mapping) off, or
`--listener` to force it on when the config file disables it. `--listener` to force it on when the config file disables it.
Check whether it works on **your** network without launching the whole client: Check whether it works on your network without launching the whole client:
```bash ```bash
soulseek-rs portmap # exits 0 when mapping works, 4 when it does not soulseek-rs portmap # exits 0 when mapping works, 4 when it does not
@@ -447,27 +434,10 @@ bug.
## Development ## Development
To run the project in development mode with debug output and trace output:
```bash
RUST_LOG=trace cargo run
```
To run the tests:
```bash ```bash
RUST_LOG=trace cargo run # run with debug and trace output
cargo test cargo test
```
To run the linter:
```bash
cargo clippy cargo clippy
```
To run the formatter:
```bash
cargo fmt cargo fmt
``` ```
@@ -501,8 +471,8 @@ SOULFIND_BIN=/path/to/soulfind/bin/soulfind \
> Homebrew's SQLite instead; no patching needed. > Homebrew's SQLite instead; no patching needed.
Set `SOULSEEK_E2E_REQUIRED=1` to turn a missing server into a hard failure Set `SOULSEEK_E2E_REQUIRED=1` to turn a missing server into a hard failure
instead of a skip. Continuous integration sets it so the e2e suite genuinely instead of a skip. CI sets it, so the e2e suite runs against a freshly built
runs against a freshly built soulfind rather than silently skipping. soulfind instead of skipping.
### Continuous integration ### Continuous integration
@@ -517,5 +487,4 @@ runs against a freshly built soulfind rather than silently skipping.
## License ## License
This project is licensed under the MIT License. See the [LICENSE](./LICENSE) MIT. See [LICENSE](./LICENSE).
file for details.