5.1 KiB
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 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:
crabidy-server guard <role>
where <role> 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:
[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.tomlthat 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 clientauthsubcommand or in the client config's[server]table (see Configuration). 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) getsUNAUTHENTICATED, 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.
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 — the trust model in one paragraph.
- Configuration — the client
[server]table and file locations. - Command line — the
guardandauthsubcommands.