Overview
Soulseek uses a custom binary protocol over persistent TCP connections. There are two distinct connection roles:- Server connection — one long-lived TCP connection to the central Soulseek server (
server.slsknet.org:2242) used for authentication, search dispatch, peer address lookups, room lists, and the distributed network handshake. - Peer connections — direct TCP connections between clients, established on demand for file browsing, transfers, and search result delivery.
SeeleSeek’s primary protocol reference is Nicotine+, the open-source Python client that has the most complete community-maintained documentation of the Soulseek protocol.
Message Framing
Every message — to the server or to a peer — uses the same envelope:UInt32 byte count followed by UTF-8 bytes.
In SeeleSeek, MessageBuilder serializes outgoing messages and MessageParser reassembles incoming byte streams into complete frames.
Connection Flow
After a TCP connection to the server is established, the client performs this sequence:Login (code 1)
Send username, password, client version (
169), an MD5 hash of username + password, and a minor version. The server replies with a success flag, a greeting string, and the client’s external IP address.Set listen port (code 2)
Tell the server which TCP port the client is listening on for incoming peer connections. SeeleSeek reports both a standard port and an obfuscated port.
Report shared files (code 35)
Send the count of shared folders and files so the server can display the client’s share stats to other users.
Join the distributed network
Send
HaveNoParent (code 71) to indicate the client needs a distributed search parent, then BranchLevel (code 126) and BranchRoot (code 127) once a parent is assigned. Send AcceptChildren (code 100) to volunteer as a relay node.Server Message Codes
TheServerMessageCode enum in MessageCode.swift defines every recognized server message. Key codes:
Authentication and session
Authentication and session
| Code | Name | Direction |
|---|---|---|
| 1 | Login | C→S / S→C |
| 2 | SetListenPort | C→S |
| 28 | SetOnlineStatus | C→S |
| 32 | Ping | C→S |
| 34 | SendDownloadSpeed | C→S |
| 35 | SharedFoldersFiles | C→S |
| 41 | Relogged | S→C (another login detected) |
| 92 | CheckPrivileges | C→S / S→C |
Peer discovery
Peer discovery
| Code | Name | Description |
|---|---|---|
| 3 | GetPeerAddress | Request IP/port for a username |
| 5 | WatchUser | Subscribe to status updates for a user |
| 7 | GetUserStatus | One-shot status query |
| 18 | ConnectToPeer | Server relays a connection request token |
| 36 | GetUserStats | Request speed and share stats |
| 1001 | CantConnectToPeer | Server could not relay the request |
Search
Search
| Code | Name | Description |
|---|---|---|
| 26 | FileSearch | Broadcast a search query |
| 42 | UserSearch | Search a specific user’s shares |
| 103 | WishlistSearch | Wishlist background search |
| 120 | RoomSearch | Search within a chat room |
| 160 | ExcludedSearchPhrases | Phrases the server blocks from search |
Chat rooms
Chat rooms
| Code | Name | Description |
|---|---|---|
| 13 | SayInChatRoom | Send a room message |
| 14 | JoinRoom | Join a public or private room |
| 15 | LeaveRoom | Leave a room |
| 16 | UserJoinedRoom | Another user joined |
| 17 | UserLeftRoom | Another user left |
| 64 | RoomList | Full list of rooms and user counts |
| 22 | PrivateMessages | Send/receive private messages |
| 23 | AcknowledgePrivateMessage | Mark message as read |
Distributed network
Distributed network
| Code | Name | Description |
|---|---|---|
| 71 | HaveNoParent | Client needs a parent node |
| 93 | EmbeddedMessage | Server forwards a distributed message |
| 100 | AcceptChildren | Client volunteers as relay |
| 102 | PossibleParents | Server sends candidate parent nodes |
| 126 | BranchLevel | Client reports its depth in the tree |
| 127 | BranchRoot | Client reports its tree root username |
| 130 | ResetDistributed | Server resets the distributed state |
Peer Connections
Connection Types
Each peer connection is opened with a declared type:| Type string | ConnectionType enum | Purpose |
|---|---|---|
"P" | .peer | General peer messages (browse, search replies, user info) |
"F" | .file | Raw file transfer data |
"D" | .distributed | Distributed search relay |
Peer Message Codes
Peer messages use the same length+code framing. Key codes fromPeerMessageCode:
| Code | Name | Description |
|---|---|---|
| 0 | PierceFirewall | Indirect connection response (NAT traversal) |
| 1 | PeerInit | Handshake — declare username, connection type, token |
| 4 | SharesRequest | Request the peer’s full share list |
| 5 | SharesReply | Zlib-compressed share list response |
| 8 | SearchRequest | Distributed search query (forwarded by parent) |
| 9 | SearchReply | Search results from the peer |
| 15 | UserInfoRequest | Request profile (description + avatar) |
| 16 | UserInfoReply | Profile response |
| 36 | FolderContentsRequest | Request files in a specific folder |
| 37 | FolderContentsReply | Folder file listing |
| 40 | TransferRequest | Initiate a file transfer |
| 41 | TransferReply | Accept or deny a transfer |
| 43 | QueueUpload | Queue a download request (called QueueDownload internally) |
| 44 | PlaceInQueueReply | Report queue position |
| 46 | UploadFailed | Upload could not complete |
| 50 | UploadDenied | Upload was refused with a reason string |
| 51 | PlaceInQueueRequest | Ask for current queue position |
SharesReply Encoding
The shares reply payload is zlib-compressed. The uncompressed structure is:Direct vs Indirect Connections (NAT Traversal)
Soulseek clients are often behind NAT. The protocol handles this with two connection paths:Direct connection
Client A sends a
ConnectToPeer (code 18) to the server with a random token and the target username. The server relays the request. Client B opens a TCP connection directly to A’s listen port and sends PeerInit with the token to identify itself.Indirect connection (PierceFirewall)
If a direct connection fails, client A tries to open a connection to B’s listen port instead, and sends
PierceFirewall (code 0) with the token. B recognizes the token and treats this incoming connection as the expected peer link. This is the fallback for clients where inbound connections are blocked.PeerConnectionPool.onPierceFirewall handles incoming indirect connections and routes them to the correct pending request — either a browse operation or a download managed by DownloadManager.
Distributed Search Network
The distributed network is a tree overlay that propagates search queries without flooding the central server.- After login, send
HaveNoParentto request a parent assignment. - The server replies with
PossibleParents(code 102) — a list of candidate usernames. - Connect to one as a
D-type peer connection and exchangeBranchLevel/BranchRootdistributed messages. - Report the resulting level and root back to the server with codes 126 and 127.
- Send
AcceptChildren = true(code 100) to become a relay node.
D connection, SeeleSeek checks its own shares and sends a SearchReply directly to the querying peer, then forwards the DistributedSearchRequest to any connected children.
Distributed message codes (carried inside EmbeddedMessage or directly over D connections):
| Code | Name |
|---|---|
| 0 | DistributedPing |
| 3 | DistributedSearchRequest |
| 4 | BranchLevel |
| 5 | BranchRoot |
| 7 | ChildDepth |
| 93 | EmbeddedMessage |
SeeleSeek Protocol Extensions
SeeleSeek defines private peer message codes in the10000+ range, under SeeleSeekPeerCode. These are silently ignored by other clients.
| Code | Name | Payload |
|---|---|---|
| 10000 | Handshake | UInt8 version — sent after PeerInit to identify SeeleSeek peers |
| 10001 | ArtworkRequest | UInt32 token + String filePath — request embedded album art |
| 10002 | ArtworkReply | UInt32 token + raw image bytes (may be empty if none found) |
Handshake, PeerConnection sets isSeeleSeekPeer = true, enabling artwork requests between SeeleSeek clients.
References
- Nicotine+ Protocol Documentation — community-maintained reverse-engineered specification
- Soulseek Network — official client and network home page
seeleseek/Core/Network/Protocol/MessageCode.swift— canonical enum of all codes used in SeeleSeekseeleseek/Core/Network/Protocol/MessageBuilder.swift— serialization for every outgoing message type