{#if hue !== null} {/if}
{#if key} {:else} {/if}
{profile?.display_name || (key ? "You" : "You, on Soulseek")} {#if key}SoulStream{/if}
{#if key} · {/if} {#if me}{me} on Soulseek{:else}not signed in to Soulseek{/if} {online ? "online" : "offline"} {#if ownLinkCurrent}linked{/if}
{#if profile} {profile.from_ledger ? "profile published" : `profile synced as of ${fmtClock(community.profileAt)}`} · rev {profile.sequence} {:else if key} nothing published yet {:else if freenetOn} {status.text} {:else} SoulStream identity off {/if} {friendCount()} friends · {slsk.buddies.length} contacts · {joinedNames.length} rooms · {convos.length} conversations
{#if hubOpen} {:else if community.section === "inbox"} {#if pendingIn().length}
{#each pendingIn() as f (f.id)}
{f.username ? display(f.username) : community.remoteProfiles[f.persona_key]?.display_name || `SoulStream ID ${fpShort(f.persona_key)}`} wants to be friends
{f.username ? `${f.username} · ` : ""}SoulStream ID {fpShort(f.persona_key)} · asked {fmtWhen(f.updated_at * 1000)}
SoulStream{f.carry === "inbox" ? "sealed · via your encrypted inbox · signed by their key" : "signed request · their ID proven over Soulseek"}·open until {fmtWhen(f.expires_at * 1000)}
friends are two signed answers — yours goes back to them over Soulseek
{/each}
{/if} {#if community.shares.length}
{#each community.shares as c (c.id)} {@const pv = community.sharePreviews[c.id]}
{c.name}
{c.entry_count} {c.entry_count === 1 ? "track" : "tracks"} · playlist · shared by {#if c.from_username}{:else}SoulStream ID {fpShort(c.from_key)}{/if} {fmtWhen(c.received_at * 1000)}
SoulStream signed by {fpShort(c.owner_key)} {#if c.carry === "inbox"}· sealed · via your encrypted inbox{/if} {#if pv && !pv.pending && !pv.error && !pv.withdrawn}·verified · revision {pv.revision_seq}{/if} {#if pv?.withdrawn}·withdrawn by the owner{/if} {#if pv?.error}·{pv.error}{/if}
tracks resolve against your library first; the rest become Soulseek streams
{/each}
{/if} {#if !convos.length && !community.shares.length && !pendingIn().length}
No conversations yet
Message someone from People, a room roster, or a search result. Conversations are kept on this device only — Soulseek stores nothing for you.
{:else}
{#if requests.length} {#if requestsOpen} {#each requests as c (c.u)} {/each} {/if} {/if}
{#each threads as c (c.u)} {/each}
Retained on this device only. Nothing here is a receipt — Soulseek has no delivery or read reports.
{#if slsk.activeChat && activeChat} {@const u = slsk.activeChat}
{display(u)} {#if metaOf(u).favorite}{/if} {#if metaOf(u).trusted}{/if}
{#if community.contacts[u]?.petname}{u}·{/if} Soulseek {presenceOf(u) ? presenceWord[presenceOf(u)] : isContact(u) ? "status unknown" : "not a contact — no status"}
{#if !isContact(u)} {/if}
{#each groupedMessages(activeChat.messages) as m, i (m.divider ? "d" + m.divider + i : i)} {#if m.divider}
{m.divider}
{:else}
{#if !m.me}{/if}
{m.text}
{fmtClock(m.ts)}{#if m.me} · submitted{/if}
{/if} {/each} {#if dmState.failed}
{dmState.failed}
failed ·
{/if}
{ e.preventDefault(); void sendCurrent(); }}>
{dmState.sending ? "queued locally" : "Soulseek PM · plain text"}
{/if}
{/if} {:else if community.section === "rooms"}
{#if joinedNames.length}
Joined
{#each joinedNames as n (n)} {/each} {/if} {#if favRooms.length}
Favorites
{#each favRooms as n (n)} {/each} {/if}
Directory · {fmtCount(slsk.rooms.length)} rooms
{#each directory as r (r.name)}
{fmtCount(r.users)}
{/each} {#if !slsk.rooms.length}
{online ? "The room list arrives after login — refresh if it stays empty." : "Sign in to Soulseek to see the room directory."}
{/if}
Counts are the server's. A room streams only after you join it; the global firehose stays off.
{#if slsk.activeRoom && activeRoom} {@const rn = slsk.activeRoom}
#{rn}
Soulseek Room {fmtCount(activeRoom.users.length)} here · history on this device since {fmtClock(roomSince[rn] ?? Date.now())}
{fmtClock(roomSince[rn] ?? Date.now())}You joined #{rn}. History on this device starts here.
{#each activeRoom.messages as m, i (i)}
{fmtClock(m.ts)} {m.message}
{/each}
{ e.preventDefault(); void sayCurrent(); }}>
public · everyone in the room
{#if roster.contacts.length}
Contacts here {roster.contacts.length}
{#each roster.contacts as u (u.username)} {/each} {/if}
Everyone {roster.others.length}
{#each roster.others.slice(0, 80) as u (u.username)} {/each} {#if roster.others.length > 80}
+{roster.others.length - 80} more
{/if}
{:else}
No room open
Pick one from the directory. Joined rooms keep their roster and messages here for the session.
{/if}
{:else if community.section === "people"}
{#each groups as g (g)} {/each}
{ e.preventDefault(); const k = addKey.trim(); if (k) { void friendRequestByKey(k); addKey = ""; } }}>
{ e.preventDefault(); void addContact(); }}>
{#if !slsk.buddies.length}
No contacts yet
Add someone by username, or from a room roster and a conversation. Contacts are watched for status while you are signed in.
{:else}
{#snippet personRow(p, seenCol)}
{#if p.meta.petname}{p.u}{/if} {#if p.meta.favorite}{/if} {#if p.meta.trusted}{/if} {#if friendshipFor(p.u)?.state === "friends"}{/if}
Soulseek {#if linkFor(p.u)}SoulStream{/if} {#if p.meta.group}·{p.meta.group}{/if} {#if p.st.file_count != null}·{fmtCount(p.st.file_count)} files{/if} {#if p.meta.note}·{p.meta.note}{/if}
{#if p.presence === "online"}onlineSoulseek, now {:else if p.presence === "away"}awaySoulseek, now {:else if p.st.exists === false}unknown userSoulseek has no such name {:else if fmtSeen(community.lastSeen[p.u])}{fmtSeen(community.lastSeen[p.u])}last seen by this device {:else}offlinenot seen by this device yet{/if}
{/snippet} {#if peopleOnline.length}
Online {peopleOnline.length}
{#each peopleOnline as p (p.u)}{@render personRow(p, false)}{/each} {/if} {#if peopleAway.length}
Away {peopleAway.length}
{#each peopleAway as p (p.u)}{@render personRow(p, false)}{/each} {/if} {#if peopleOff.length}
Offline or unknown {peopleOff.length}
{#each peopleOff as p (p.u)}{@render personRow(p, true)}{/each} {/if}
Contacts are yours and stay on this device. "Trusted" is a permission you granted, not a verified identity. Petnames, groups and notes live in the profile drawer — click a name.
{/if} {:else if community.section === "discover"}

{slsk.activeRoom ? `In #${slsk.activeRoom} right now` : "In a room right now"}

from the room roster{#if activeRoom} · {fmtCount(activeRoom.users.length)} here{/if}
{#if roomPeople.length}
{#each roomPeople as u (u.username)} {/each}
{:else}
{activeRoom ? "Everyone in this room is already a contact." : "Join a room and the people in it show up here, with the room as their provenance."}
{#if !activeRoom}{/if}
{/if}

Recent peers

from your transfers this session
{#if recentPeers.length}
{#each recentPeers as p (p.u)} {/each}
{:else}
Nobody yet. People who send you files, or take files from you, appear here for the session.
{/if}

Similar Soulseek interests

the server's match on declared likes · not follows
{#if community.similar?.users?.length}
{#each community.similar.users.slice(0, 24) as su (su.username)} {/each}
{:else if community.busy.similar}
Asking the server…
{:else}
{community.similar?.error ? "The server did not answer." : online ? "The server matches people on the likes you declare on Soulseek — SoulStream has none set for you yet, so this shelf is empty." : "Sign in to Soulseek to ask the server."}
{#if online}{/if}
{/if}

Library overlap

computed on this device, after an explicit browse
Nothing computed yet. Browse a person's shares and SoulStream compares them with your library locally — nothing is uploaded to do it. The comparison itself is the next piece of this shelf.
There is no global directory here and no "people you may know". Every shelf says where its people came from. Freenet discovery indexes are opt-in, curator-run, and currently off.
{:else if community.section === "spaces"}
No Spaces yet
A Space is a persistent SoulStream community on Freenet with its own member keys — invitation only, encrypted when private, and never a Soulseek Room in disguise. The research behind it is done; the Space contract itself is the next piece of the Freenet track, so nothing here is live yet.
beta · not live
{:else if community.section === "you"}
{#if key}

Your public profile SoulStream

Anyone who has your SoulStream ID can fetch this. It is signed by your persona key and replicated — publishing is not something that can be fully undone.

{#if avatarUrl}{/if}
{#if community.busy.avatar}

Preparing your photo and publishing it — a few seconds normally; the first publication can take longer.

{:else if profile?.avatar_note && profile.avatar_note !== "none published"}

{profile.avatar_note}

{/if}

Display names are not unique. People you meet may give you a private petname; you cannot see it.

{#if community.busy.publish} pending publication {:else if community.busy.profile} reading from the network — up to a minute {:else if profile} {profile.from_ledger ? "published from this device" : `synced as of ${fmtClock(community.profileAt)}`} · revision {profile.sequence} {:else} nothing published yet {/if} {#if community.profileError}{community.profileError}{/if}
{:else}

Your SoulStream identity SoulStream

{#if !freenetOn}

A SoulStream identity is a key pair held by your local Freenet node. Turn the Freenet layer on (right) and the node will hold it; nothing is published until you say so.

{:else if !nodeUp}

Waiting for the node — {status.text}. Identity needs a connected node.

{:else}

Create a fresh key here, or bring one back from a recovery file. Creating one publishes nothing; your profile is a separate button, later.

{/if} {#if community.error}

{community.error}

{/if}
{/if}
{#if key}

Your public playlists {community.published.filter((p) => !p.withdrawn).length}

{#if !community.published.length}

Nothing published yet. Open a playlist and choose Share on SoulStream: a signed manifest goes to the network under your ID — titles, artists, albums, durations, your notes; never a file, never a path.

{:else} {#each community.published as p (p.playlist_id)}
{p.name}
{p.entry_count} tracks · revision {p.revision_seq} · {p.withdrawn ? "withdrawn" : "published"} {fmtWhen(p.published_at * 1000)}
{#if p.withdrawn} {:else} {/if}
{/each}

Withdrawing stops your pointer; copies others already fetched stay with them.

{/if}

Your SoulStream ID

The fingerprint of your persona key. It is the stable you — names change, this does not. Share it as text; nothing else identifies you across networks.

{#each fpGroups(key) as g, i (i)}{#if i}·{/if}{g}{/each}
Key version
{community.persona?.persona?.key_version ?? 1}{#if (community.persona?.persona?.key_version ?? 1) > 1} · rotated{/if}
Signs
profile today; playlists, friend links, Soulseek link and Space links as those land
Delegate
{fpShort(community.persona?.delegate_key)}

Recovery

Your identity lives in the node's key custody on this machine. Export a recovery file — encrypted with a passphrase you choose — and keep it somewhere that is not this disk.

{#if exportDone}

Written: {exportDone.path} ({exportDone.bytes} bytes). Test it on another machine before you need it.

{/if}
{#if confirmRotate} Rotating signs a transition with the old key; friends re-verify on their next fetch. {:else if confirmRemove} This deletes the key from this node. Without a recovery file, this identity ends. {:else} {/if}
{#if community.error}

{community.error}

{/if}
{/if}

Networks

Soulseek {online ? "online" : "offline"}
{#if me}Signed in as {me} · {fmtCount(network.shareFiles)} files shared.{:else}Not signed in — see Settings.{/if}
{#if !key} Create a SoulStream identity to link this account to it. {:else if !me} Sign in to Soulseek to link this account to your identity. {:else if ownLinkCurrent} Linked to this identity — proof verified {new Date(ownLink.verified_at * 1000).toLocaleString()} through a Soulseek message to yourself. Other SoulStream users who ask over Soulseek now get your signed ID; nothing is published. {:else} Not linked to your identity. Linking sends one message to yourself over Soulseek and verifies the signed answer — nothing is published, and from then on other SoulStream users who ask get your signed ID. {/if}
{#if key && me}
{#if ownLinkCurrent} {:else} {#if !nodeUp}needs the node connected{/if} {/if}
{/if}
Freenet experimental
{#if freenetOn} Local node on 127.0.0.1:{community.freenet?.ws_port ?? 7509} · {status.text} {#if statusKind(community.freenet?.status) === "node-absent"}
{#if community.nodeInfo?.binary} runs the installed peer{#if community.nodeInfo.version} ({community.nodeInfo.version}){/if} in the background · telemetry pinned off · data in the app folder {:else} No Freenet peer found on this machine — install it (cargo install freenet) and check again. {/if}
{:else if community.nodeInfo?.started_by_app}
started by SoulStream · keeps running when the app closes; this button stops it
{/if} {:else} Off. Carries your identity and profile; later playlists, friend links and Spaces. Turning it on publishes nothing. Turning it off is instant and touches nothing else in the app. {/if}
Before you rely on it — seven things that are true
  • A desktop background peer must run; it uses network, disk, memory and CPU.
  • The network is alpha. Things you publish are replicated and cannot be guaranteed erased.
  • Freenet is not an anonymity service.
  • The node's own diagnostics are a node setting SoulStream pins off; verify after upgrades.
  • Nothing is published when you enable this. Publishing is always a button.
  • Nothing here ever says "online" — a successful fetch is not presence.
  • Phones and TVs cannot run the same peer today.
{#if freenetOn} {#if key}
{#if community.inbox?.published} encrypted inbox · key {community.inbox.box_key_id} {community.inbox.last_check ? `checked ${fmtWhen(community.inbox.last_check * 1000)} · ` : ""}friend requests and shares arrive sealed to a key kept on this device; a persona restored elsewhere publishes a new one {:else} lets other SoulStream users send you friend requests and shares sealed, without Soulseek in between {/if}
{/if} {/if}

Devices later

A paired device gets its own key and a signed grant — it never receives your root key or Soulseek password. Comes after the profile work settles.

{/if}
{#if drawerUser}
{/if}