Release v1.2.0 beta.1 - #25
Conversation
Refuse to boot on a weak or placeholder JWT_SECRET unless ALLOW_INSECURE_JWT_SECRET=1 is set for local development, and pin token verification to HS256. Postgres TLS now verifies the server certificate by default, with PGSSL_REJECT_UNAUTHORIZED as the opt-out for self-signed managed databases. The local login route no longer accepts a password via query string, and DB error logs no longer include query parameters. Add a rate-limited public /api/favourites/counts endpoint, backed by a new index on favorites(key), so cards can show how many StreamBooru users faved a post, and persist each site's enabled flag through /api/sites. The British and US spellings of the favourites routes now share a single handler, and bulk upsert writes every row in one round trip instead of a statement per fave. Signed-off-by: Stephen Howe <[email protected]>
Logging back in no longer overwrites favourites saved while offline: sync merges the server copy with local faves and uploads the local-only ones on Electron, web, and Android, and live removals from other devices still apply through targeted stream events. Faving a post also favourites it on the source site when credentials are configured (Danbooru, Moebooru, e621), and cards show how many StreamBooru users faved each post. Save is now Fave everywhere, since it never wrote files to disk. Add tag autocomplete, a collapsible lightbox tag panel and card tag chips, per-site enable/disable with a source-chip row, a settings panel (theme, grid density, card layout, autoplay, safe-mode blur, filename template), bulk-download progress with cancel, toast notifications in place of every blocking alert dialog, empty and skeleton states, and touch gestures plus Android back-button handling in the lightbox. Popular-feed re-sorts reuse cards through keyed reconciliation, folder and save dialogs are async, and the scroll handler and natural-aspect grid no longer thrash layout. Security: the Electron window blocks in-app navigation to remote origins, Discord OAuth callbacks require a client-generated nonce on both the desktop loopback listener and the Android deep link, the image proxy refuses loopback, private, and link-local targets, and downloads reject parent-directory path segments. On Linux, Chromium's caches move out of ~/.config/streambooru into ~/.cache. Fixes the desktop Gelbooru XML fallback, Unlink Discord, the Settings version string, lightbox keyboard zoom, lightbox navigation across Popular-feed re-sorts, user_favorited normalization, the unreachable name-format popover, Derpibooru's missing auth fields, and the mobile menu opening Account twice. Signed-off-by: Stephen Howe <[email protected]>
server/webapp was a hand-maintained copy of renderer that had drifted, so the hosted web app shipped without fixes the desktop build already had. scripts/build-webapp.mjs mirrors renderer into it and prunes removed files, npm run webapp:check fails CI when the two diverge, and the Nixpacks deploy runs the same script instead of an inline copy. Update dependencies to current releases (Electron 43.4, Capacitor CLI 8.5, Express 4.22, pg 8.23, jsonwebtoken 9.0.3, dotenv 16.6) and pin uuid and body-parser through overrides; npm audit and bun audit are both clean. Signed-off-by: Stephen Howe <[email protected]>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
The periwinkle blue accent is replaced by cyan (#22e3ff) for interactive state and magenta (#ff2fb0) for favourites and emphasis, with a gradient between them for the wordmark, the active tab, Download All, and the download progress bar. Most of the old accent colours were hardcoded literals scattered through the stylesheet rather than tokens, so they now go through --accent, --accent-2, --accent-grad, --ring and friends, which is what makes the light theme possible: bright cyan is unreadable on white, so that theme resolves the same tokens to a deeper teal and magenta. Modal surfaces lose their navy tint so they read as neutral next to the new accents, and mobile.css drops its duplicate --acc token. Signed-off-by: Stephen Howe <[email protected]>
The Android launcher icon and splash screen were still the stock Capacitor placeholder, and the desktop build had no icon at all because electron-builder's buildResources directory did not exist. The mark is four gallery tiles arranged around a play cut-out: the grid says booru, the play says stream. Vector masters live in branding/ and everything else is rendered from them, so the set stays consistent and can be regenerated at any size. That covers build/icon.png for the desktop packages, all five Android densities for the legacy, round, and adaptive-foreground variants, the eleven splash sizes, and the web favicon and apple-touch-icon. The adaptive foreground is scaled to 46% of its canvas so the tile corners survive a launcher masking it to a circle, and the adaptive background colour changes from white to the plate colour. Signed-off-by: Stephen Howe <[email protected]>
The local server tests need the sync server's dependencies, which no CI job installed, and the DCO check walked every commit in the PR range including the merge commit the forge generates itself, which no contributor can sign off. Signed-off-by: Stephen Howe <[email protected]>
Atlas Commons PR review
Overall: pass
View PR #25 · |
Atlas Commons code review (LLM)SummaryLarge, well-scoped v1.2.0 beta covering favourite merge sync (with server-side deletion tombstones), meaningful server hardening (secret guards, HS256 pinning, stream-only SSE tickets, Postgres TLS verification), client UX (settings, toasts, tag autocomplete, bulk-download cancel), and operational fixes (generated Potential issues
// electron/main.js — re-keys on pull
const key = it.post?.site?.baseUrl ? favKey(it.post) : String(it.key);
// renderer/js/platform.js — uses server key as-is
const k = String(it.key || '');
Suggestions
Overall assessmentThis is a strong release candidate: the favourite merge redesign, security tightening, and webapp generation fix address real production pain, and the PR description's deployment notes for Advisory review only — not a merge approval. View PR #25 · |
The unpacked build only gets a root-owned setuid chrome-sandbox from the .deb/rpm installers, so Chromium falls back to user namespaces. The GitHub runner image restricts those, so it tried the misconfigured SUID helper and aborted with SIGTRAP. The previous guard only dropped the sandbox when running as root, which covered the Forgejo container but never the non-root runner. Check the helper itself instead. Signed-off-by: Stephen Howe <[email protected]>
Merge sync treated every local-only favourite as something to re-upload, so a device that was offline when another one unfaved a post pushed it straight back. Removals are recorded in favorite_deletions for 180 days and the newer timestamp wins, so a deliberate re-fave still beats an older deletion. Bulk upload enforces this in SQL rather than trusting the client to have checked, and the favourites list hands clients the tombstones so they can drop what they still hold. clampPost's allowlist predated the artist, copyright and character fields and the video fields, so any favourite that made a server round trip came back without its grouped tags and without grid autoplay. Electron also keyed favourites off the raw site URL while the renderer normalized it, which split one post across two keys whenever an older config had a trailing slash or different host casing; keys now go through normalizeBaseUrl and existing favourites are re-keyed and de-duplicated on load. ENC_SECRET is held to the same standard as JWT_SECRET and checked at startup rather than on the first credential write — crypto.getKey() already refused anything under 16 characters, but a long placeholder is just as public. Browsers now trade the account token for a 60-second stream-only ticket instead of putting a 90-day credential in a URL that proxy logs keep; tickets are rejected everywhere else, and the stream endpoint pins HS256 like the rest of the API. Raised in review on #25. Signed-off-by: Stephen Howe <[email protected]>
…w palette Manage Sites rendered API keys and Moebooru password hashes as plain text inputs, so they sat on screen in full view of anyone glancing over, screen sharing, or screenshotting the window. Secret fields are masked with a per-field Show/Hide toggle; login, user ID, and the Derpibooru filter ID are not secrets and are left alone. The landing, health, and OAuth callback pages still wore the old periwinkle blue on a navy background, which no longer matched anything. They pick up the app's tokens, the brand gradient on the wordmark and primary button, and the icon and favicon. Signed-off-by: Stephen Howe <[email protected]>
should fix the following issues
#3 #8 #9 #23
Summary
First beta of v1.2.0, covering the client UI, favourite sync, and the sync server.
Favourites now merge instead of replace. Logging back in used to overwrite anything
saved while offline. Sync merges the server copy with local faves and uploads the
local-only ones on Electron, web, and Android. Deletions are tracked server-side for 180
days so an offline device can't resurrect a fave another device removed, with the newer
timestamp winning so a deliberate re-fave still beats an older deletion. Faving also
favourites on the source site when credentials are configured (Danbooru, Moebooru, e621),
and cards show how many StreamBooru users faved a post. "Save" is now "Fave" everywhere,
since it never wrote files to disk.
Client. Tag autocomplete, a collapsible lightbox tag panel and card tag chips,
per-site enable/disable, a settings panel (theme, grid density, card layout, autoplay,
safe-mode blur, filename template), bulk-download progress with cancel, toasts in place of
every blocking
alert(), empty and skeleton states, and touch gestures plus Androidback-button handling. Popular-feed re-sorts reuse cards through keyed reconciliation, and
the scroll handler and natural-aspect grid no longer thrash layout.
Server. Refuses to boot on a weak
JWT_SECRETorENC_SECRET, pins JWT verificationto HS256, verifies Postgres TLS by default, and drops password-via-query-string on login.
Browsers connect to the event stream with a 60-second stream-only ticket rather than
putting the 90-day account token in a URL. Adds a rate-limited public
/api/favourites/counts, persists each site's enabled flag, and bulk upsert writes in oneround trip instead of one statement per fave.
Security. The Electron window blocks in-app navigation to remote origins, Discord
OAuth callbacks require a client-generated nonce on both the desktop loopback listener and
the Android deep link, the image proxy refuses loopback/private/link-local targets, and
downloads reject parent-directory path segments. On Linux, Chromium's caches move out of
~/.config/streambooruinto~/.cache.Branding. StreamBooru gets its own icon and a cyan/magenta accent pair, replacing the
stock Capacitor placeholder and the desktop build's missing icon.
server/webappwas a hand-maintained copy ofrendererthat had drifted, so the hostedweb app shipped without fixes the desktop build already had; it's generated now and CI
fails if the two diverge.
Deployment note (breaking)
Two environment changes are required before this deploys cleanly:
ENC_SECRETmust be set to a real value (16+ chars, not the.env.exampleplaceholder)or the server will refuse to start.
ALLOW_INSECURE_ENC_SECRET=1is the local-dev escapehatch. Do not change an existing value — stored site credentials become unreadable.
JWT_SECRETis held to the same standard. Changing it invalidates every issued token andlogs all users out.
PGSSL_REJECT_UNAUTHORIZEDnow defaults totrue. Set it tofalseonly if yourmanaged Postgres presents a self-signed cert.
Migrations
0006–0008apply on startup.Related issues
Fixes #3
Fixes #8
Fixes #9
Fixes #23
Checklist
git commit -s) for DCO