# crabidy A client/server music player. A headless gRPC server owns the library, the play queue, and audio output; a terminal UI connects to it over localhost (or the network). Media comes from pluggable **providers**, each mounted as a subtree of one library: ```text / ├── tidal Tidal streaming (see tidaldy/README.md) ├── youtube YouTube search & playlists (see ytdy/README.md) ├── fs a local music folder (see fsdy/README.md) └── crabidy your saves: queues, bookmarks (`w`), and captures (`W`), managed by the server (see architecture/crabidy-store.md) ``` ## Binaries - `crabidy-server` — the server: providers, queue, playback, gRPC on `0.0.0.0:50051`. Also serves the web client at that address (see below). - `cbd-tui` — the terminal client. Press `?` inside for all key bindings. - `cbd` — both in one process: starts the server, waits until it accepts connections, then runs the TUI. Adopts an already-running server instead of failing on an occupied port. - `cbd-web` — the browser client (Leptos/WASM), with the same functionality as the TUI. Not run directly: it is built to a bundle and embedded into `crabidy-server` (see [cbd-web/README.md](cbd-web/README.md)). ## Quick start The toolchain is managed by [devenv](https://devenv.sh): ```sh devenv shell # provides rust, yt-dlp, and friends cargo run -p cbd # server + TUI in one process ``` Or run the halves separately: `cargo run -p crabidy-server` and, in another terminal, `cargo run -p cbd-tui`. ## Configuration All configuration lives in `~/.config/crabidy/` (the platform config directory). Every file is optional; missing providers simply do not mount. Files are created/rewritten on first start with their defaults filled in. | File | Component | Documentation | | -------------------- | ---------- | -------------------------------------- | | `tidaly.toml` | Tidal | [tidaldy/README.md](tidaldy/README.md) | | `ytdy.toml` | YouTube | [ytdy/README.md](ytdy/README.md) | | `fsdy.toml` | local fs | [fsdy/README.md](fsdy/README.md) | | `cbd-tui.toml` | `cbd-tui` | below | | `cbd.toml` | `cbd` | below (same options as `cbd-tui.toml`) | | `crabidy-server.toml`| server | below (never auto-created) | The server-managed `crabidy` provider does not live under `~/.config`. Its track-file tree (saved queues, bookmarks, and captures) lives in `~/.local/state/crabidy/` (the platform *state* directory) and the audio it captures lives in a single content-addressed store under `~/.local/share/crabidy/` (the *data* directory), shared and de-duplicated across saves. Neither needs configuration; see [architecture/crabidy-store.md](architecture/crabidy-store.md). ### `cbd-tui.toml` and `cbd.toml` Client configuration. `cbd-tui` (the standalone terminal client) reads `cbd-tui.toml`; `cbd` (server + TUI in one process) reads its own `cbd.toml`. They are **separate files with the same options** so the two can run side by side on one machine — a common setup is `cbd` playing locally against its in-process server while `cbd-tui` points at a remote server (e.g. a Raspberry Pi). A shared file would force one to follow the other's `address`. ```toml [server] # Where to find the server. Default (both files): localhost, which is # what cbd's own in-process server listens on. Point cbd-tui.toml at a # remote server to use it as a remote control. address = "http://127.0.0.1:50051" # Credentials, when the server has [auth] configured (see below). # `user` is the role name; leave both empty against an open server. # The password is stored in plaintext — keep this file private. user = "" password = "" # Show the frequency-spectrum bars under the track progress. Default true. spectrum = true ``` Every option is also available as a command-line flag before the subcommand (`cbd-tui --address ... --user owner`, `cbd --spectrum false`); a flag overrides the file value. To write the credentials into the config once, use the `auth` subcommand (see below) instead of editing the file by hand: ```sh cbd-tui auth owner 'my-password' # sets user + password cbd-tui auth queue-owner 'pw' --address http://pi:50051 ``` ### `crabidy-server.toml` — roles and rights By default the server is open: everyone who can reach the port has full control. Adding an `[auth]` section turns on HTTP basic auth for every RPC and hands out *roles* (see `architecture/roles-auth.md`): - **owner** — everything (the normal user). - **queue-owner** — anything on the queue and playback, but no library writes: no bookmarks (`w`), captures (`W`), queue saving, renames or deletes. - **queue-appender** — may browse/search and append tracks to the queue; nothing else. ```toml [auth] # One PHC hash per role; omit a role to disable it. Generate and store # a hash with: crabidy-server guard (see the CLI section). owner = "$argon2id$v=19$m=19456,t=2,p=1$..." queue_owner = "$argon2id$..." queue_appender = "$argon2id$..." ``` Clients authenticate with the role name as the basic-auth user (see `cbd-tui.toml` above). A malformed `crabidy-server.toml` aborts server startup rather than silently running open. Note that the transport is plain HTTP/2: fine on a trusted home network, but anything exposed further needs TLS termination (reverse proxy, VPN) in front. ## Command line Every binary is a clap CLI: run it with `--help` (and any subcommand with `--help`) for the full surface. Running a binary with **no subcommand** behaves as it always has — `crabidy-server` runs the server, `cbd-tui` runs the TUI, `cbd` runs the in-process server + TUI. The `library`, `queue`, and `global` subcommands are available on all three binaries and act as a remote control over gRPC (they connect to a running server, honouring the same `[auth]` credentials as the TUI): ```sh crabidy-server library list /tidal # browse a node cbd-tui --address http://pi:50051 queue append /fs/album cbd global play # toggle play/pause cbd global volume -- -0.1 # lower the volume ``` Connection flags (`--address/--user/--password`) go **before** the subcommand; omitted, they fall back to the client config file. Server-only subcommands (`crabidy-server`, and `cbd`): - `guard [password]` — hash a role password (argon2id), print the PHC string, and (unless `--no-config`) write it into `crabidy-server.toml`'s `[auth]`. Roles: `owner`, `queue-owner`, `queue-appender`. - `scan [--capture|--move]` — walk a folder and drop a `.cbd-track.toml` beside every audio file so it browses under `/fs`. `--capture` copies each file into the content store (the toml points there); `--move` moves it instead of copying. Client-only subcommand (`cbd-tui`, and `cbd`): - `auth [password] [--address ADDR]` — write the role name and cleartext password (and address) into the client config. **Password caveat.** A password given as a command-line argument is visible in the process list (e.g. `ps`). Omit it and `guard` reads the password from stdin instead, which keeps it out of argv and is pipe-friendly: ```sh printf '%s' 'my-password' | crabidy-server guard owner ``` The client config stores the password in plaintext, so keep the file private. ### Completions and man pages ```sh cbd-tui completions bash # print a completion script devenv shell -- gen-cli-assets # write dist/completions + dist/man ``` `gen-cli-assets` builds the binaries with `CBD_ASSET_DIR=$PWD/dist`, so `dist/completions/**` (bash/zsh/fish) and `dist/man/*.1` are produced for all three binaries. Every ordinary build also emits them into the crate's `OUT_DIR`. ## Using the library Navigation is vim-style: `j`/`k` select, `l` enters the selected folder, `h` goes to the parent, `Tab` switches between library and queue, `Enter` replaces the queue with the selection. `%` creates a node where the pane title shows `% to add` (e.g. a search term), `e` renames, `d` deletes. `/` filters the current pane (library or queue) live as you type — `Enter` keeps the filter, `Esc` clears it. - `w` saves the selection (a library subtree, or in the queue pane the queue) as a new folder under `/crabidy/` of **link** files — needs the source provider to replay. On a name that already exists the save is refused with a warning; delete the old folder and save again. - `W` **captures** the selection into `/crabidy/`: same as `w`, but every track's audio is fetched into the shared content store under `~/.local/share/crabidy/` and the saved tomls link to it — fully local playback afterwards. Works on a library subtree and on the queue (no need to save it first). Audio is **de-duplicated**: capturing the same track again (from a playlist, a search, another save) reuses the stored file instead of downloading it twice, matched first by provider id and then by content hash. Tracks already local (from `/fs`) are copied into the store rather than re-downloaded. A source that genuinely cannot be captured is recorded as *skipped* (red in the UI, skipped by playback). Download captures can take long; progress is shown in the library pane. - Captured rows are marked with a leading `|` — visible even while browsing another provider, so you can see what you already have. - Inside `/crabidy`, `d` deletes a folder or track immediately (no confirmation): it removes only the metadata toml, never the shared store audio, which other saves may reference. Press `?` for the full binding table. A row of frequency-spectrum bars is drawn under the track progress while audio plays (the server taps its own output, runs the FFT, and streams the bars, so it works whether the server is local or remote). Turn it off with `spectrum = false` in the client config. ## Web client `crabidy-server` serves a browser client with the same functionality as the TUI at its own address (`http://:50051/`) — same navigation, same keys (`j`/`k`/`h`/`l`, `%`, `e`, `d`, `w`, `W`, queue and playback controls, `?` for help), plus clickable equivalents and a light/dark theme toggle. It talks gRPC-web to the same service the TUI uses, so it honors the same `[auth]` roles (it shows a login form when the server requires credentials). It is compiled to a WASM bundle and embedded into the server binary, behind the default-on `web-ui` cargo feature. A plain `cargo build` needs no WASM toolchain — it embeds a "not built" placeholder page until you build the bundle: ```sh devenv shell -- build-web # writes cbd-web/dist cargo build -p crabidy-server # embeds it ``` Build the server with `--no-default-features` for a headless, gRPC-only binary. See [cbd-web/README.md](cbd-web/README.md) for the dev loop and details. ## Logs `cbd` and `cbd-tui` log to `~/.local/state/crabidy/` (daily files); `crabidy-server` logs to stderr. Stream URLs and credentials are redacted from logs by design. ## Development Design documents live in `architecture/`, per-feature quality gates in `quality/`, and implementation plans in `plan/`. See `CLAUDE.md` / `AGENTS.md` for the development workflow and coding rules.