Away from home

An install has up to three addresses: the computer's own LAN address, a secure home name with a real certificate that works on the home network, and - only if the owner turns it on - a second name that works from anywhere. For anybody who would rather not be on the internet at all, Tailscale is the other way out. And because a home's upload is often slow, the server paces what it sends to a phone away from home.

Three addresses

AddressWhere it worksCertificate
http://192.168.1.50:8099The home networkNone (plain http)
https://<id>.home.soundstorm.dev:8099The home networkLet's Encrypt, trusted everywhere
https://<id>.net.soundstorm.dev:8099Anywhere, once remote access is onThe same certificate, carrying both names

The id is random and per install. All three are served on the same port: SoundStorm's listener tells a TLS connection from a plain HTTP request by its first byte (servetls.Listener), so the address people already have keeps working while the secure one is added.

The home name and its certificate

A trusted certificate needs a name on the internet, and a home server has neither a domain nor a route an ACME challenge can reach. So SoundStorm runs one small central service, the names service, which gives each install <id>.home.soundstorm.dev, points it at the install's private LAN address, and publishes the DNS-01 challenge that lets Let's Encrypt issue a certificate for a server nothing on the internet can reach. It is the same idea as Plex's plex.direct, without the account.

The page moves to the secure name only after checking it works: it fetches the name's /healthz in no-cors mode, which succeeds only if the name resolved, the connection opened and the certificate verified. Plenty of routers refuse to resolve a public name that points at a private address (DNS rebinding protection), and on those the page simply stays on http. Details are on Certificates and names.

Remote access

Turned on under Settings (or with -Remote / --remote at install), remote access gives the install the second, public name. It is off by default, with a plain warning, because once it is on the sign-in page faces the internet. The security reviews that had to come first - the setup code, per-account sign-in throttling, request timeouts, cross-origin checks, the reader fixes - were done before it shipped.

Turning it on does three things:

  1. Open the port. internal/portmap asks the router to forward it, trying PCP, then NAT-PMP, then UPnP. PCP is best where it works, but it carries a client address that a strict router compares with the packet's source - and behind Docker's NAT those differ - so NAT-PMP is the fallback. UPnP's discovery is multicast, which does not cross Docker's bridge, so the installer discovers the router on the host and passes it in, along with the router's address (SOUNDSTORM_GATEWAY): inside the container, the "gateway" is Docker's bridge, which does not answer. The mapping is refreshed on a timer and dropped when remote access is turned off.
  2. Prove it. The names service probes the install at the address the request came from - never an address the install supplies, so it cannot be aimed at anybody else - and expects an answer only that install can compute from its own token. Only then does it publish the public record. "Your port is not open yet" is actionable in a way a silently dead record is not.
  3. Get the certificate. One certificate carries both names, so the public name does not double each install's draw on the domain's shared Let's Encrypt allowance.
Two names rather than one, because many routers cannot "hairpin" - loop a device on the home network back in through the public address. With a single public name, home access would break on those routers. The page already checks which name it can reach before moving, so it prefers whichever works.

When it cannot work: CGNAT and double NAT

The router's own WAN address says a lot, and portmap asks for it even when the port mapping succeeded - a router behind carrier-grade NAT opens the port without complaint, on an address the internet cannot reach. ClassifyWAN judges it:

Over IPv6 there is nothing to forward: the public name also carries an AAAA record, published from a call the install makes over IPv6, which proves it has working IPv6. That path stays dormant until the container actually has IPv6, which Docker does not give it by default.

A restart once forgot the remote name. Keeping it through a names-service outage relied on memory, which a restart empties, so when the DNS provider answered with an error just as the server came back, the certificate was reissued without it and phones away from home were refused. The remote name is now read back from the certificate on disk, and an unanswered check retries in five minutes.

Tailscale

Tailscale connects devices privately with nothing exposed to the internet and nothing to forward, on any connection - including behind carrier-grade NAT. docker compose --profile tailscale up -d runs a Tailscale sidecar that puts SoundStorm on a tailnet at https://<hostname>.<tailnet>.ts.net. On Windows a Start menu shortcut, Set up Tailscale, walks through it: a window with the steps, a button to Tailscale's key page, and a box that accepts only something shaped like a Tailscale auth key. On Mac and Linux it is --tailscale --auth-key ....

It is opt-in for the same reason Plex was rejected as a backend: it needs an account, an auth key and an app on every device, none of which can be automated. SoundStorm works fully without it, and nobody is walked through a sign-up they did not ask for. Making it the default would make the "no accounts, no keys" claim untrue.

Three things were learned by running it:

Pacing and slow links

A home's upload is often the narrowest pipe in the whole path. The report that started this work: songs sat at 0:00 for a minute or more on a phone away from home, over a link measured at about 0.3 Mbps. Several causes were found, one after another:

Streaming quality can be set per device under Settings → Playback on this device. Always original is a promise: no probe and no fallback. Films have their own setting; the default, smart, sends the original at home and caps at 20 Mbit from an away-from-home name.