Ask a music app where a song lives and the honest answer is usually a database row: an ID that means something to that service and nothing anywhere else. Soundfish takes a stranger route. A composition’s identity is its own encoding. The URL fragment that carries the piece is the piece, hashed twice for two different jobs, and the title you gave it is not part of the identity at all.
This is content addressing applied to media in the tab. The benefits are the ones content addressing always promises (integrity, deduplication, portability, no server required to name a thing) paid for with a strict size contract and a canonical encoding. The free half of this lesson covers the shape of the address and the bounds that make it feasible. The gated half goes into the two digests, the verification order, and how the local library keys storage by bytes rather than by name.
the url is the document
A Soundfish composition travels as a URL fragment:
#s1.<music-cid>.<codec>.<payload>.<transport-sha256>
Each field does one job. s1 is LOOP_URL_ENVELOPE_VERSION, the envelope version. The MusicCID is the semantic identity, a CIDv1 raw SHA-256 over the canonical encoding, rendered as lowercase base32 and recognizable by its leading b. The codec is one character: c for canonical CBOR, d for raw DEFLATE, and compression is chosen only when it is strictly shorter, so the encoding is deterministic rather than negotiated. The payload is the encoded document. The trailing digest is a 43-character base64url SHA-256 over the transport bytes.
The fragment is doing security work by accident of the platform: browsers do not send URL fragments in ordinary HTTP requests, so the document contents never hit a log, a referrer header, or an analytics payload. Sharing the link shares the document, and the server that serves the page never sees which document you opened. A composition that fits in a fragment needs no upload step, no account, and no permission: the address is the content, self-certifying and offline by default.
small by contract
Content addressing only works as a URL scheme if the document is small, so the model is small on purpose. A loop document is at most 16 bars of 16 steps each (MAX_BARS, BEATS_PER_BAR, STEPS_PER_BEAT), at most 16 tracks of which 15 are melodic because channel 10 is reserved for General MIDI drums (MAX_TRACKS, MAX_NOTES_TRACKS), at most 512 events per track and 832 per document, a BPM between 40 and 240, a title under 80 UTF-8 bytes, and automation lanes expressed in basis points with their own ceilings. Every bound is a named constant in the protocol model, not a convention.
Those bounds compose into a hard outer limit: MAX_CANONICAL_CBOR_BYTES is 24,493, the maximum a canonical document can occupy. MAX_LOOP_FRAGMENT_CHARS is 32 KiB and MAX_LOOP_URL_CHARS is 64 KiB, so a document that fits the model provably fits the envelope, and a link that fits the envelope fits anywhere a URL can go, including a chat message or a QR code. The decoder enforces the same order: bound the fragment characters, then bound the payload bytes, before any allocation or decompression happens. Smallness is not a limitation here; it is the price of admission that buys portability.