# Roles and authorization By default the server is **open**: it listens on the network and anyone who can reach the port has full control — not only playback, but library writes such as renaming or deleting your saves. This is the right default on a trusted home network with no config to write. Adding password hashes to the `[auth]` section of `crabidy-server.toml` locks the server down **from the top**. Each password you set lowers what a caller with **no credentials** may do; a caller *with* a matching password is elevated to that role. You lock the powerful roles first and leave the weaker ones open for anyone on your network — see [What anonymous callers get](#what-anonymous-callers-get) below. ## The three roles Roles are ordered by privilege; each includes the rights of the ones below it (owner ⊃ queue-owner ⊃ queue-appender): - **owner** — the normal user: everything, including all library writes. - **queue-owner** — anything on the queue and playback (append, remove, reorder, clear, shuffle, repeat, play/stop, next/prev, volume, mute, …), but **no library writes**: no bookmarks (`w`), no captures (`W`), no saving, renaming, or deleting. - **queue-appender** — may browse and search the library and **append** tracks to the queue; nothing else. (Searching is allowed because appending something first means finding it.) ## Turning it on Each role is credentialed by one argon2id password hash in PHC format. Generate a hash and store it in the config in one step with: ```sh crabidy-server guard ``` where `` is `owner`, `queue-owner`, or `queue-appender`. This reads a password, hashes it (argon2id), and writes the resulting PHC string into `crabidy-server.toml`'s `[auth]` table. The file ends up like this: ```toml [auth] # One PHC hash per role. Guard from the top down. owner = "$argon2id$v=19$m=19456,t=2,p=1$..." queue_owner = "$argon2id$v=19$..." queue_appender = "$argon2id$v=19$..." ``` A role's PHC hash — not its password — is what lives in the file. Omit a role's key to leave that role **open**: no password is needed to act as it, and no one can log in as it. ## What anonymous callers get A request with no credentials is not rejected — it is granted the **most powerful role you did *not* guard**. Setting a password walks that floor down one step: | Guarded roles | A no-credential caller is… | | --- | --- | | *(none — no `[auth]`)* | **owner** — the open default | | `owner` | **queue-owner** | | `owner` + `queue_owner` | **queue-appender** | | all three | *nothing* — credentials required | So to run a server where guests may append but a password is needed to run the queue, guard `owner` **and** `queue_owner` and leave `queue_appender` open. Because the anonymous caller always gets the highest open role, guarding a weaker role while a stronger one is still open would be pointless — the anonymous role would simply outrank it. Such a config is treated as a mistake: setting `queue_owner` without `owner`, or `queue_appender` without `queue_owner`, **aborts startup**, and `crabidy-server guard` refuses to write it. Guard the roles from the top down. ## How enforcement behaves - **Fail-closed.** Authorization is enforced in one place, in front of the RPC handlers, and it is default-deny: an unknown or future method requires the owner role until it is explicitly mapped to a lower one. No handler ever sees an unauthorized request. - **Startup is strict.** A `crabidy-server.toml` that exists but does not parse — or that guards the roles out of order — aborts server startup rather than silently running open: a broken auth config never downgrades to a weaker posture than intended. - **Clients send the role as the username.** A client authenticates by sending the role name (`owner`, `queue-owner`, `queue-appender`) as the basic-auth user and the role's password as the basic-auth password. Set these with the client `auth` subcommand or in the client config's `[server]` table (see [Configuration](./config.md)). With no credentials configured the client sends no header and is treated as the anonymous role — which keeps the zero-config local setup working against an open server. - A caller below a method's required role — whether anonymous or logged in — gets gRPC `PERMISSION_DENIED`. A wrong password (and an anonymous request to a fully-locked server) gets `UNAUTHENTICATED`, with every authentication failure answering identically so a caller cannot probe which part was wrong. A wrong password is never quietly downgraded to the anonymous role. ```admonish warning The transport is plain HTTP/2 — basic auth travels in the clear. This is fine on a trusted LAN. Put TLS in front of anything exposed beyond it (a reverse proxy or a VPN); crabidy does not terminate TLS itself. ``` Secrets — client passwords, the `authorization` header, and stream tokens — are never written to logs, traces, or error messages. ## See also - [Introduction](./intro.md) — the trust model in one paragraph. - [Configuration](./config.md) — the client `[server]` table and file locations. - [Command line](./clients/cli.md) — the `guard` and `auth` subcommands.