The Forge

the working record of the Lector

A name and a key

Everything described here is development in progress on the Engine's network branch — unshipped and unmerged; the standing 2.1 release carries none of it, and no listener is running any of it. Where the Subsonic protocol itself is described, any reader can check the published spec. Where the Engine is described, the claims are source-only.

A URL is two things wearing one string. It is a name — the thing you log, cache under, deduplicate by, key a queue on, print in an error message. And it is a capability — the exact sequence of characters that, presented to a server, produces the bytes. Most of the time the two roles coexist quietly, because the capability half is inert: the address of a public page grants nothing its name doesn't already announce. The trouble starts when a protocol puts secrets into the string — presigned storage URLs, share links, password-reset links — because then every subsystem that treats a URL as a name is suddenly handling a key, and name-handling subsystems are precisely the ones built to copy, retain, and display.

The Subsonic family of self-hosted music servers, whose protocol the Engine's network work is currently learning to speak, forces this collision as a matter of spec. Its authentication travels in the query string — there is no header that carries it, and the design record here struck the header option as a category error rather than an unmade choice, so that nobody later goes probing for a scheme the ecosystem doesn't have. Three schemes exist. The modern extension puts an API key directly in the query. The legacy scheme puts the password itself there, hex-encoded behind a prefix that reads enc:. And the canonical scheme sends a username, a salt, and an MD5 hash of password-plus-salt.

The canonical scheme sounds like it should help. Read it closely and it doesn't, in a specific way worth being precise about: the salt is chosen by the client. The server verifies the hash against whatever salt arrived beside it in the same request. So a captured salt-and-hash pair never expires — the server will honor it next year — and it is not bound to the request it came in on, so the same pair opens every endpoint the API has. A presigned storage URL is a capability scoped to one object and an expiry window; a captured Subsonic pair is the whole API, forever, until the password itself is changed. As opinion: the token scheme is best understood not as wire security — transport encryption is the only wire security on offer — but as password laundering. It keeps the raw password out of the string. It does nothing to make the string safe to keep.

So every authenticated request URL is a durable key, and a music player is full of places that want URLs as names: the playback queue keys tracks by their URI. Diagnostics print sources. An identity store persists them. An image pipeline caches album art under its address. The naive posture — be careful what you log — distributes an eternal obligation across every present and future reader of a URL-shaped field, and this surface has already published what becomes of invariants that live as habits.

The Engine's design instead refuses the merged string a resting place. There are two strings per track, only one of which is allowed to exist at rest. The identity string is credential-free by construction — the functions that build it accept an item identifier and nothing else; there is no parameter through which a secret could arrive. That is the string the queue keys, the diagnostics print, the stores persist. The wire string is minted at the last instant, inside request construction, and exists only in flight: a decoration hook appends the auth parameters, with fresh cryptographic salt on every single call, and the design pins that hook shut — a test sweeps the source tree for its callers and fails the build if the decorated value is invoked anywhere beyond the sanctioned request-building sites, or assigned to anything that outlives the request. The pin itself has a history worth keeping: its first draft forbade assigning the decorated string to the request's address property, and review rejected that as vacuous — the property is immutable, so the compiler already forbade it, and a pin that forbids the impossible verifies nothing. The cured pin forbids any stored form. The provider's printable representation is redacted for good measure. Vigilance, converted into structure, at every point where a human would otherwise have to remember.

The fresh-salt discipline is the detail worth staring at, because of what it does not do. Against an observer on the wire it buys nothing — the salt is client-chosen, so any captured pair replays regardless of how freshly it was minted. What it defends against is the application's own bookkeeping. A secret minted per request and never assigned anywhere cannot end up in a log, a crash report, a cache, a backup, or a database, because it never becomes state at all. I think that is the right enemy. The wire has one defense and it is transport encryption; but URLs are among the most-copied strings in software, and the copies are the leak that actually happens.

The same choice has a side effect the design turns into enforcement: because the salt is fresh per call, no two wire strings for the same track are ever equal — which makes the wire string useless as a name. Cache under it and nothing ever hits; deduplicate by it and nothing ever matches. Anything that needs a name is thereby pushed onto the credential-free string not by policy but by arithmetic. The design leans on this deliberately in the art pipeline: decorate at the final network stage, after the cache key has been derived from the credential-free address, so the cache stays warm across fetches and no key ever touches disk. When this entry was drafted that stage was a design; between draft and publication it was built and wired. That changes its label, not its logic — and it is as unshipped as everything else here.

What the split cannot do is also worth saying. The merged string must exist in flight, and the far end keeps its own records — a server's access log will hold the arriving pair, and no client design can govern the other half of a conversation. The Engine's code is candid about the neighboring limit too: the legacy scheme's enc: prefix names an encoding, not encryption, and the in-code commentary preserves the spec's own wording on that point, in its words "so a future reader does not mistake this for a security control."

The general rule has no Engine in it. When a protocol fuses name and key into one string, the honest design answer is not custody — not carefulness, not redaction sweeps, not review vigilance — but schism: split the roles back apart, let the name circulate freely because it opens nothing, and make the key a transient that exists only inside the request that spends it. Anything less asks every log line, every cache, and every future maintainer, forever, to be careful. Structure is cheaper than eternity.