# Audio publishing reference

`soundfish audio` publishes albums of audio tracks on Sound.fish from the
command line. Each album belongs to one signed-in Hraness account, starts as
a draft with a private manage page, and becomes public at
`https://sound.fish/@<handle>/<slug>` when published. Each track keeps ordered
revisions (v1, v2, …) and plays the promoted one. This is the only part of the
CLI that talks to a server: loop and song commands stay local.

## Sign in

Tokens are created by a signed-in account at
<https://sound.fish/settings/tokens> and shown once. Never pass a token as an
argument; it would land in shell history and process lists.

```sh
soundfish audio login                    # terminal: prompts with echo off
printf '%s' "$TOKEN" | soundfish audio login --token-stdin   # agents and scripts
soundfish audio whoami                   # account, handle, profile, usage, limits
soundfish audio logout
```

`login` checks the token against the API before saving it under
`$XDG_STATE_HOME/hraness/soundfish/credentials.json` (default
`~/.local/state/hraness/soundfish/credentials.json`), keyed by API origin, in
a directory only the current user can read. `SOUNDFISH_TOKEN` in the
environment overrides the stored token for one run and is sent to whatever
origin `--api-url` (or `SOUNDFISH_API_URL`) resolves to; that origin must be
`https:` unless the host is loopback. A `whoami` line `handle - stale` means
another account claimed the handle: sign in at <https://sound.fish> to refresh
it. Album and track ids keep working meanwhile.

## Exit codes and output

0 success; 1 network failure or a 5xx response; 2 usage; 3 invalid local
input (a bad ref, an unsupported file, a malformed token); 4 conflict or plan
limit (an existing slug with a different title, a track that already carries
different audio, the one-album or storage limit); 5 rejected by the API (any
other 4xx, including a missing sign-in). Standard output carries only results,
as tab-separated lines or, with `--format json`, the API view. Progress,
hints, and the manage page link go to standard error. Every mutating verb
accepts `--dry-run`, which needs a token, resolves album and track refs with
GET requests, hashes any files, and prints the writes it would send as JSON
without sending them.

## Albums and tracks

Album refs are `alb_<id>`, `@handle/<slug>`, or one of your own slugs; track
refs are `trk_<id>` or the track's slug within that album.

```sh
soundfish audio albums create --title 'Night drive' --slug night-drive --description-file notes.txt
soundfish audio albums list
soundfish audio albums show night-drive
soundfish audio albums update night-drive --metadata label=self-released --unset-metadata draft
soundfish audio tracks add night-drive --title 'Take off'   # empty track; upload fills it
soundfish audio tracks reorder night-drive take-off intro outro
soundfish audio art set night-drive cover.png
soundfish audio albums publish night-drive
soundfish audio albums stats night-drive                   # plays, hearts, comments
soundfish audio embed night-drive --html                   # <iframe> for any site
```

Album pages, track pages, and embeds accept `?track=<slug>` and `t=83` or
`t=1m23s` to open on a track at a moment without autoplaying; the player's
share control copies such links.

`create` returns the existing album unchanged when the slug and title match,
and exits 4 when the slug exists with another title. Descriptions are plain
text split into paragraphs on blank lines (`-` reads standard input).
`--metadata key=value` repeats; keys are lowercase letters, digits, `.`, `_`,
and `-`. `delete` and `tracks remove` are soft deletes that free the slug.

## Upload

```sh
soundfish audio upload night-drive 'Take Off.wav' intro.mp3 outro.flac
soundfish audio upload night-drive 'take-off-v2.wav' --track take-off --note 'louder bass'
soundfish audio upload night-drive mix.wav --track take-off --no-promote --created-at 2026-09-14T12:00:00Z
soundfish audio revisions list night-drive take-off
soundfish audio revisions promote night-drive take-off 1
soundfish audio status ast_<id>
```

MP3, WAV (16, 24, and 32-bit PCM or 32-bit float), and FLAC are accepted, at
most 200 MiB per file, 1 second to 20 minutes, mono or stereo. For each file
the CLI hashes it, asks the API for an upload, streams the bytes to storage,
waits while the server inspects them, then attaches the result:

- Without `--track`, the track is chosen by slug: `--slug`, the slug derived
  from `--title`, or the file name without its extension. A missing track is
  created with the file as v1. A track that already carries this exact file
  is reported `unchanged`. A track that carries different audio makes the
  command exit 4 with the hint to use `--track <slug>`, so a second file never
  silently replaces a track.
- With `--track`, the file becomes the next revision of that track and is
  promoted unless `--no-promote` is given.

`--track`, `--title`, `--slug`, and `--note` require exactly one file.
`--created-at` dates the revision (not in the future) so an imported catalogue
keeps its version dates. A file the account already stored is never uploaded
twice. For a draft album each successful upload prints
`Listen before publishing: <manage URL>` on standard error.

## Limits

Free accounts publish one album with up to 10 tracks, 25 revisions per track,
200 MiB per file, and 2 GiB of storage in total. Cover images are PNG, JPEG,
or WebP, at most 12 MiB, between 200 and 8000 pixels per edge.

## Example: migrating an album with dated versions

```sh
soundfish audio albums create --title valhalla --slug valhalla
for t in valhalla takeoff pdoom breach ascent; do
  soundfish audio tracks add valhalla --title "$t" --slug "$t"
done
soundfish audio tracks update valhalla pdoom --title 'p(doom)'
soundfish audio tracks update valhalla ascent --title takeover   # renamed; the slug stays
soundfish audio upload valhalla v1/valhalla.mp3 --track valhalla --created-at 2026-09-14T12:00:00Z
soundfish audio upload valhalla v2/valhalla.mp3 --track valhalla --created-at 2026-09-15T12:00:00Z
soundfish audio upload valhalla v3/valhalla.mp3 --track valhalla --created-at 2026-09-16T12:00:00Z
soundfish audio albums publish valhalla
curl -sI "https://sound.fish/embed/@$(soundfish audio whoami --format json | jq -r .username)/valhalla"
```

Upload v1 files first so ordinals match the dates. Re-running any line is
safe: existing albums, tracks, and revisions are reported `unchanged`.

## Data boundary

Only the audio verbs send data to Sound.fish, and only the files, titles,
descriptions, and metadata you name, over the account whose token is stored.
Loop URLs, compose text, and MIDI never leave the machine. Revoke a token at
<https://sound.fish/settings/tokens> if it may have leaked.
