# Quality gates — roles and rights LLM-verified gates for `architecture/roles-auth.md`. Automatic tests live in `crabidy-server/src/auth.rs`, `crabidy-server/src/main.rs` (hash helper) and `cbd-tui/src/rpc.rs`. ## Security posture - [x] Fail-closed everywhere: an unknown/future gRPC method requires owner; a present-but-malformed `crabidy-server.toml` aborts server startup instead of silently running open; a missing file (or empty `[auth]`) is the documented open mode. - [x] No panics on request input: malformed `authorization` headers (bad base64, missing colon, non-UTF-8, wrong scheme), unknown role names, and oversized values all produce `UNAUTHENTICATED`, never a panic. - [x] Indistinguishable failures: wrong user and wrong password both answer plain `UNAUTHENTICATED` with the same message. - [x] Secrets redacted: passwords, authorization header values, and PHC hashes never appear in logs, traces, or error messages (including the `hash-password` helper and TUI logs). - [x] Authorization is enforced in exactly one place (the tower layer), before any handler runs; RPC handlers did not change. ## Semantics - [x] Rights matrix implemented as specified: appender = reads + `Append` + `CreateLibraryNode`; queue-owner adds every queue and playback verb; owner-only = `CaptureLibraryNode`, `SaveQueue`, `RenameLibraryNode`, `DeleteLibraryNode`. Higher roles include lower ones. A test pins the full method list of the proto service so an unmapped new RPC fails the suite. - [x] Valid credentials with an insufficient role get `PERMISSION_DENIED` (not `UNAUTHENTICATED`). - [x] No `[auth]` hashes ⇒ exactly today's behavior: no header required, all methods allowed. - [x] A role without a configured hash cannot authenticate. ## Performance - [x] Argon2 verification runs once per credential: successful verifications are cached (header value → role) and the cache is only fed by successes, keeping it bounded by the number of valid credentials. ## Client - [x] Without configured credentials the TUI sends no header (zero-config local use unchanged); with credentials, every request — including update-stream reconnects — carries the same `authorization: Basic` header. - [x] The client config documents that `password` is plaintext and the file must be kept private. ## Tooling - [x] `crabidy-server hash-password` reads the password from stdin (nothing echoed by the tool itself), prints only the PHC string, and its output verifies against the same server code path.