Repository navigation
feat(settings): OPDS sharing pane on the two-mode model - #165
Conversation
libcalibre Test Coverage ReportOverall coverage: 80.16% Coverage breakdown available in the artifacts. |
1503779 to
8b00462
Compare
libcalibre Test Coverage ReportOverall coverage: 80.16% Coverage breakdown available in the artifacts. |
8b00462 to
d1a829f
Compare
libcalibre Test Coverage ReportOverall coverage: 80.16% Coverage breakdown available in the artifacts. |
libcalibre Test Coverage ReportOverall coverage: 80.16% Coverage breakdown available in the artifacts. |
libcalibre Test Coverage ReportOverall coverage: 80.16% Coverage breakdown available in the artifacts. |
libcalibre Test Coverage ReportOverall coverage: 80.16% Coverage breakdown available in the artifacts. |
libcalibre Test Coverage ReportOverall coverage: 80.16% Coverage breakdown available in the artifacts. |
libcalibre Test Coverage ReportOverall coverage: 80.16% Coverage breakdown available in the artifacts. |
libcalibre Test Coverage ReportOverall coverage: 80.16% Coverage breakdown available in the artifacts. |
libcalibre Test Coverage ReportOverall coverage: 80.16% Coverage breakdown available in the artifacts. |
libcalibre Test Coverage ReportOverall coverage: 80.16% Coverage breakdown available in the artifacts. |
libcalibre Test Coverage ReportOverall coverage: 80.16% Coverage breakdown available in the artifacts. |
libcalibre Test Coverage ReportOverall coverage: 80.16% Coverage breakdown available in the artifacts. |
libcalibre Test Coverage ReportOverall coverage: 80.16% Coverage breakdown available in the artifacts. |
libcalibre Test Coverage ReportOverall coverage: 80.16% Coverage breakdown available in the artifacts. |
libcalibre Test Coverage ReportOverall coverage: 80.16% Coverage breakdown available in the artifacts. |
libcalibre Test Coverage ReportOverall coverage: 80.16% Coverage breakdown available in the artifacts. |
6e7d380 to
540a7ec
Compare
…gure Credentials are stored reversibly (ADR 0005) so the pane can show the current password instead of forcing a rotation. The Argon2 verifier and the machinery that made it safe (semaphore, busy response, HMAC result cache, response padding) are removed; exponential backoff remains the online-guessing defence. Auth reads a hot-swappable username/password snapshot per request, so configuring or generating credentials no longer stops a running share. Clearing credentials still does. reconfigure applies a port or scope change to a running share. Every config check runs before the old listeners are stopped, so a rejected config leaves the share running. Usernames containing a colon are rejected, since HTTP Basic could never authenticate them. New commands: clb_query_opds_credential_secret, clb_cmd_reconfigure_opds. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The pointer-cursor overrides are gone (arrow cursor on every control), but this header still said the block signals clickability with pointer cursors. Co-Authored-By: Claude Opus 5.5 <[email protected]>
A Sharing tab in Settings for the local OPDS server. Rows layout: share toggle with the live listening URL (copyable), reachable-from scope, port, and reader sign-in with username and a reversible password field (reveal toggle, press-and-hold regenerate with a gold letter wave that flares HDR-bright on capable displays). The pane renders backend status, never local guesses. useOpdsSharing polls the service and serialises mutations; start and reconfigure treat a failed bind as failure. A port change that fails to bind rolls back to the previous port and restarts there. Errors render beside the control that caused them — server errors inline in the Port row, credential errors under the password — and a failed start clears when the port is edited or the pane is left. Sharing settings (scope, port, sign-in, username) persist through the settings store with a migration from the previous shape. Co-Authored-By: Claude Opus 5.5 <[email protected]>
540a7ec to
fcf5b84
Compare
libcalibre Test Coverage ReportOverall coverage: 80.16% Coverage breakdown available in the artifacts. |
Why
The old sharing pane was built for the pre-rework service: a picker of ~65 network interfaces, status fields the state machine no longer exposes, and no credential surface. It was driving a model that doesn't exist anymore.
The other half is credentials. We only stored an Argon2 verifier, so a generated password was shown once and then gone — getting it back into a reader meant rotating it, and rotating stopped a running share. Having to rotate to "reveal" a password is bad UX, so credentials are now stored reversibly (ADR 0005) and the pane can just show them.
What changed
Three commits, reviewable in order:
feat(opds)— backend. Reversible credential storage; the Argon2 verifier and everything that existed to make it safe (semaphore, 503-busy, HMAC result cache, response padding) is deleted, and backoff stays as the guessing defence. Auth reads a hot-swappable snapshot per request, so setting or regenerating credentials doesn't interrupt a running share. Newreconfigureapplies port/scope changes to a live share and runs every config check before stopping the old listeners. Usernames containing:are rejected, since HTTP Basic can't carry them.style— one stale comment. feat(library): main-UI library picker, double-buffered switching, Library tab redesign #166 already landed the cursor change itself.feat(settings)— the pane. Share toggle with a copyable listening URL, Local network / All networks (All networks needs sign-in), port, and reader sign-in with a reveal toggle and press-and-hold regenerate. Settings gain asharingblock (v2→v3), and v3→v4 moves the default port off 8080 — Calibre's own server's default — to 9028; a port you picked is left alone. The web build gets no OPDS client at all.The pane renders what the service reports, not local guesses. One thing that bit me: the service reports a failed bind as
Ok(status)in the error state rather than a rejected command, so the UI originally treated a busy port as success.startOutcomeinuse-opds-sharing.tsnow counts that as failure. A port change that can't bind rolls back to the previous port and restarts there, with "Port not available, please choose another" inline in the Port row. Errors render beside the control that caused them, and a failed start clears when you edit the port or leave the tab.Reviewer focus
use-opds-sharing.ts— polling vs. mutations (the mutation sequence andmutatingguard), andstartOutcome.crates/citadel-opds/src/service/mod.rs—preflight()andreconfigure.clb_query_opds_credential_secrethands the plaintext password to app JS, so a hypothetical XSS now reaches the secret. We render no untrusted HTML today; the ADR says to revisit if that changes.Validation
cargo test --workspace(0 failures; new tests cover reconfigure rejecting a bad config without tearing down the share — red without the fix — and the colon username),vitest run(249 passed),tsc --noEmitclean. Each of the three commits builds on its own.bun lint: 3 warnings, all in files from main (FirstRunFlow.module.css,Sidebar.tsx,Books.tsx).me:homeusername is flagged inline and not saved. I checked the HDR sheen on the regenerate wave with and without an HDR display.Not verified: the "Starting…" / "Waiting for a network connection…" labels (too brief to catch), and I didn't point a real OPDS reader at it in this pass.
(In one test run the port rollback didn't happen — the field kept the blocked port and sharing ended up off. Two targeted retries couldn't reproduce it, so I'm flagging it rather than claiming it's fixed.)
Follow-ups
Supersedes #151.