docs: lead with the protocol coverage, and drop a stale upload assertion

The message-set coverage is the crate's strongest claim, so say it where
people look first: the README features list and About, the website home
and community pages, each pointing at the coverage table. The table now
carries a section naming what the message set does not reach, so the
claim has something behind it rather than standing on its own.

The upload streaming test still asserted the uploader was blocked
waiting for the downloader's close — the wait that came out when it
started costing the upload slot. It passed only while the thread had yet
to be scheduled, and failed whenever it had.
This commit is contained in:
Michel de Graaf
2026-09-09 19:45:06 +00:00
parent 9bca573bc0
commit cd580e5173
5 changed files with 34 additions and 7 deletions
+11
View File
@@ -30,6 +30,10 @@ enthusiasts to share niche music. This repository is that client plus
## Features ## Features
- **Full protocol coverage**: 100% of the modern Soulseek protocol's message
set — every code the reference clients do not mark obsolete — implemented in
`soulseek-rs-lib` and driven end to end against a real server, not a mock.
See the [coverage table](docs/protocol-coverage.md)
- **Search & download**: queue from the TUI or fetch in one command with `get`; - **Search & download**: queue from the TUI or fetch in one command with `get`;
filter by bitrate, size, file type, free slots, or terms to exclude filter by bitrate, size, file type, free slots, or terms to exclude
- **Wishlist**: `wish add` what nobody has today; `wish run` and - **Wishlist**: `wish add` what nobody has today; `wish run` and
@@ -65,6 +69,13 @@ implementation for anyone building their own client, and **soulseek-rs**, the
client built on it. The library stays lean on dependencies and has none today; client built on it. The library stays lean on dependencies and has none today;
the client takes them freely. the client takes them freely.
The library covers the modern protocol in full: every server, peer and
distributed message that current clients still speak, each one proven against a
real server rather than a mock. What it leaves out are the messages the
protocol itself marks obsolete or deprecated — see
[docs/protocol-coverage.md](docs/protocol-coverage.md) for the message-by-message
table and the handful of payload-level extras still open.
### Projects using soulseek-rs-lib ### Projects using soulseek-rs-lib
- [seakarr](https://github.com/binhex/seakarr) — automated Soulseek music - [seakarr](https://github.com/binhex/seakarr) — automated Soulseek music
+17
View File
@@ -113,3 +113,20 @@ ParentIP (73), UserPrivileged (122), NotifyPrivileges (124), AckNotifyPrivileges
| 93 | DistribEmbeddedMessage | yes | unit — unwrapped once, as Nicotine+ does | | 93 | DistribEmbeddedMessage | yes | unit — unwrapped once, as Nicotine+ does |
Serving children is off by default; see `accept_children` in the README. Serving children is off by default; see `accept_children` in the README.
## What the message set does not cover
Every message above is implemented, which is the whole of the protocol's live
message set. Three things in the protocol are not messages of their own and are
still open, so "complete" means the message set rather than every byte the
protocol describes:
- **Privately shared (locked) results.** `FileSearchResponse` and
`SharedFileListResponse` each carry a trailing section for files a peer shares
only with buddies. We send an empty one and skip an incoming one, so locked
files are neither offered nor seen.
- **Obfuscated connections.** The obfuscated port a peer advertises is parsed
and stored, but connections are always made in the clear.
- **Adopting a firewalled child.** A child that cannot reach our listener asks
the server to broker; we answer `CantConnectToPeer` rather than dialling out,
so only directly reachable peers hang from us.
-4
View File
@@ -189,10 +189,6 @@ mod tests {
stream.read_exact(&mut received).unwrap(); stream.read_exact(&mut received).unwrap();
assert_eq!(received, content); assert_eq!(received, content);
assert!(
!uploader.is_finished(),
"the uploader must wait for the downloader to close the F socket"
);
drop(stream); drop(stream);
assert_eq!(uploader.join().unwrap().unwrap(), 4096); assert_eq!(uploader.join().unwrap().unwrap(), 4096);
assert_eq!(bytes_sent.load(Ordering::Relaxed), 4096); assert_eq!(bytes_sent.load(Ordering::Relaxed), 4096);
+4 -2
View File
@@ -269,8 +269,10 @@ export const Community = () => {
eyebrow="built on the library" eyebrow="built on the library"
title="Projects using soulseek-rs-lib." title="Projects using soulseek-rs-lib."
> >
The protocol crate is there for anyone building their own client or The protocol crate covers the modern protocol in full: 100% of the
automation on Soulseek. These projects do. message set current clients speak, each one proven end to end against a
real server rather than a mock. It is there for anyone building their
own client or automation on Soulseek — these projects do.
</SectionHead> </SectionHead>
<Cols start> <Cols start>
<Prose> <Prose>
+2 -1
View File
@@ -227,7 +227,8 @@ const About = () => (
<Callout title="build your own"> <Callout title="build your own">
<p> <p>
Reach for <Code>soulseek-rs-lib</Code> to build a custom client or an agent on Reach for <Code>soulseek-rs-lib</Code> to build a custom client or an agent on
the protocol.{' '} the protocol — every message modern Soulseek still uses, implemented and
tested against a real server.{' '}
<Link to="/install" className="text-link hover:text-link-hover"> <Link to="/install" className="text-link hover:text-link-hover">
Use the library Use the library
</Link> </Link>