Releases: arediss/Oscarr
v0.8.1
⚠️ BREAKING —OSCARR_SECRET_KEYis now MANDATORYBefore upgrading, generate a 32-byte hex key and add it to your environment. Without it, Oscarr will refuse to boot.
Generate one (host with Node):
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"No Node on host? Just boot the container without the key — Oscarr exits with a friendly message and a freshly-generated key you can copy-paste:
docker compose up # … look for the "Oscarr — secret key required" block in the logs: # OSCARR_SECRET_KEY=<copy this line into your .env>Then add to your compose / env file:
environment: - JWT_SECRET=your_random_jwt_secret - OSCARR_SECRET_KEY=your_64_hex_chars_key🔐 Lose this key = stored service credentials become unrecoverable and need to be re-entered manually. Treat it like a password — back it up.
Existing plaintext credentials get flagged in the admin (floating banner, bottom-right) and re-encrypted automatically on next save.
Compose gotcha
Compose reads .env for YAML interpolation only — vars don't reach the container unless declared in environment: or loaded via env_file:. The bundled docker-compose.yml was patched post-release to do both. If you maintain your own compose file, make sure it has either:
env_file:
- .env…or…
environment:
- OSCARR_SECRET_KEY=${OSCARR_SECRET_KEY}Verify with: docker compose config | grep OSCARR_SECRET_KEY (must show the real value, not the placeholder).
Highlights
New
- Seerr-compatible API at
/api/v1/*— third-party tools (Homarr, Doplarr) can talk to Oscarr without a custom integration. Endpoints not yet implemented return a clean501 Not Implemented. - Per-app API keys with revocation (Admin → Access → API Keys) — each external integration gets its own scoped credential.
- AES-256-GCM encryption at rest for service credentials. Master key derived from
OSCARR_SECRET_KEYvia HKDF-SHA256. - Secure password reveal/copy modal — replaces the native
prompt()for credential reveal in the admin. - TMDB trending backdrop on the login page — random pick from the top 10 trending titles.
Improved
- qBittorrent 5.x compatibility — provider now uses API-key auth (
Authorization: Bearer). Drops username/password. Pause/resume hit the new/torrents/stopand/torrents/startendpoints. - Searchable plugin Discover with filters (Hide installed, Nouveau pill).
- Reusable
ConfirmModal— replacesconfirm()/alert()across destructive admin actions.
Removed
- *Drop arr tag-based request sync — replaced by the upcoming Seerr import path (#192).
Fixes
- Bump
better-sqlite3to12.9for Node 24+ support. - Slim README + extract
docs/installation.md.
CI
- Advisory React Doctor job on push + PR.
Image
ghcr.io/arediss/oscarr:0.8.1
ghcr.io/arediss/oscarr:latest
Multi-arch (linux/amd64, linux/arm64), keyless-signed (Sigstore / cosign), SPDX SBOM attached.
v0.8.0 — Plugin self-update, generic storage, indexer + download-client connector pack
Highlights
Plugins now self-update from inside Oscarr with a permission diff and a reload/reboot split — no more SSH dance for routine bumps. They also gain a generic isolated storage API (KV + SQLite, gated by storage:plugin), so plugin state lives next to the plugin and is wiped with it. On the integrations side, a connector pack lands: Prowlarr (indexer) plus four torrent/Usenet clients (NZBGet, SABnzbd, Transmission, Deluge) — connection-test only for now, the action layer will ship as separate plugins.
Features
- In-app plugin update flow with permission diff. Plugins self-update from inside Oscarr without SSH — the admin sees the upcoming version, the diff of requested permissions, and chooses between a hot reload (no downtime) and a full reboot (required when manifest perms or native deps changed). Reload vs reboot is decided by the engine, not guessed by the user.
- Generic plugin storage API (KV + SQLite). Two isolated primitives — a key/value store and a per-plugin SQLite database — exposed via
PluginContext.storageand gated by the newstorage:pluginpermission. State is scoped to the plugin and removed with it, so the core schema doesn't leak plugin concerns. - Indexer + download-client connector pack (Prowlarr, NZBGet, SABnzbd, Transmission, Deluge). Five new service connectors land on the Services tab — connection testing only. Actions (queue/history/pause/etc.) will be exposed by separate plugins talking to these connectors. Each ships with a typed
test()that distinguishes auth failures from network errors and surfaces protocol-specific cases like Deluge's daemon-detached state and Transmission's CSRF challenge. - Searchable service-type picker with provider icons + 'Untested' badge. The service-creation modal now opens a Headless UI Combobox instead of a native
<select>, with a search input, provider icons rendered inline, and an amber 'Untested' pill on connectors that haven't been validated against a real instance yet. Picking an untested connector shows a one-click GitHub feedback link prefilled with the connector name — the badge is removed in the same commit that confirms the integration works. - Support module extracted to a standalone plugin. The legacy in-core Support module is now shipped as
Oscarr-Plugin-Support, removable like any other plugin. The migration carries an export of existing tickets so admins can install the plugin and keep their data, while keeping the core lean and aligned with the plugin-first vision.
Fixes
- SonarCloud reliability sweep — 53 bugs cleared. Three refactor tiers landed in this cycle (mechanical sweep, cosmetic batch, redundant cast removal). Pure code-health work — no behavioural change expected.
- Log injection sanitizer hardened. CodeQL was failing to recognise the log sanitizer; rewriting it as explicit per-channel branches with separate
replace()calls satisfies the static analyzer and removes the dead suppression. Closes part of the security code-scanning queue (#182).
Other
- Faster multi-arch (amd64 + arm64) release builds. The release pipeline now produces both
linux/amd64andlinux/arm64images noticeably faster. The lockfile's platform-native deps were also restored so installs on distroless / musl images stop failing.
Full changelog: v0.7.8...v0.8.0
Upgrading
```
docker pull ghcr.io/arediss/oscarr:0.8.0
```
v0.7.8 — Account modal redesign, Jackett indexer & qBittorrent plugin contract
Highlights
A redesigned account modal Discord-style replaces the old avatar dropdown, ships a new account.section plugin hook, and unlocks the first official plugin built on it: qBittorrent Manager. Admins gain user-defined topbar shortcuts, a quick admin/back jump button, and a Jackett connector for indexer integrations.
Features
- Discord-like account modal with plugin sections. The avatar dropdown is replaced by a full-screen
AccountModalwith a sidebar (avatar header → section list → logout) and a content pane that swaps between built-in sections (Account, Preferences) and plugin-contributed ones via the newaccount.sectionhook point. Plugins declare an icon, label and React component; built-ins and plugin entries merge in a single sorted list. - User-selectable avatar source. #169 — pick which connected provider (Plex, Jellyfin, Emby, …) supplies your avatar. The new
AvatarEditorsurfaces every provider image you're linked to and lets you swap on the fly without touching the file system. - Admin-defined custom topbar links. #167 — admins can publish arbitrary URL shortcuts to the home topbar (label, icon, URL, target). Useful for pinning Tautulli, Overseerr, your dashboard, or any service users should reach in one click — without writing a plugin.
- Admin jump button + tooltip system. A discreet shield/home button now sits in every topbar (admin-gated) for one-click jumps between the user app and the admin panel — replacing the slower path through the avatar dropdown. Backed by a new shared
Tooltipcomponent with a 100ms fade (vs the browser's ~1.5s native delay), reused on the avatar cog and the notification bell. - Jackett indexer connector. New
indexerservice category with Jackett as its first member. The connection test uses the public-with-apikey Torznab caps endpoint so it works on Jackett instances locked behind an admin password (where/server/configrequires a cookie session). API key passes via query (notX-Api-Key— Jackett doesn't honour that header). - Curated icon set expanded.
DynamicIcon(used by plugin manifests for nav/admin tabs/widgets) gains 8 new icons: Coins, CreditCard, Gift, Crown, Award, Trophy, Gauge, Rocket — to cover subscription/achievements/stats plugin patterns. Unknown icons now log a one-time dev-mode console warning instead of silently falling back to Puzzle, and the available list is documented indocs/plugins.md.
Fixes
- qBittorrent connection test no longer 403s. The qBittorrent provider used to hit
/api/v2/app/versiondirectly without first authenticating, surfacing a misleadingHTTP_FORBIDDEN. It now performs the full/auth/loginflow, captures the SID cookie, and reports typed errors on bad creds (AUTH_FAILED), IP ban after too many failed attempts (AUTH_BANNED) or missing session cookie (AUTH_NO_SESSION) instead of the bare HTTP code. - Admin-only plugins no longer reachable by URL. Any plugin shipping a
frontendfield was previously mountable at/p/<id>whether or not it published anavcontribution — admin-only plugins (subscription, qbittorrent-manager…) were therefore reachable by typing the URL even though their data routes were permission-gated. The/p/<id>route now refuses to mount any plugin that hasn't contributed tohookPoint='nav', falling back toplugin.not_found.
New plugin available
Oscarr-Plugin-qBittorrent v0.1.0 ships alongside this release. Discover it via Admin → Plugins → Discover once the registry update lands. Read-only torrent queue + bulk pause/resume/delete + magnet add, with cross-check against Sonarr/Radarr import history so seeds already in the library are flagged for safe cleanup.
Full changelog: v0.7.7...v0.7.8
Upgrading
docker pull ghcr.io/arediss/oscarr:0.7.8
or
docker pull ghcr.io/arediss/oscarr:latest
v0.7.7 — Customisable dashboard, plugin install metrics & live nav refresh
Highlights
The admin dashboard becomes customisable per tab and per widget, the Discover tab surfaces install counts, and the nav refreshes live when you toggle a plugin. Plus the usual sweep of fixes for stale badges, broken caches, and stuck "Copy" buttons.
Features
- Customisable dashboard tabs and widgets. Pick an icon for each tab from a curated 56-icon set. Each widget gets a Pencil edit button in edit mode → custom title, custom icon, or just toggle "show header" to surface the widget's default title. Custom overrides survive drag/resize.
- Live install count on the Discover tab. Each plugin card now shows the total number of GitHub release-asset downloads next to the star count. GitHub increments the counter on every install — no extra infra. Plugins that ship a source tarball (instead of a Release asset) show 0 since GitHub doesn't track those.
- Verbose request log toggle. New opt-in toggle in Admin → Système → Instance → Debug that persists every
/api/*response to AppLog with method, path, status, redirect Location, duration, IP and User-Agent. Useful for tracing OAuth round-trips or plugin proxy redirects on a live instance. Off by default; URLs and Referer scrubbed via the existing scrubSecrets pipeline. - Plugin tab URL sync. Admin → Plugins → Installed / Discover sub-tab now syncs to the URL via
?sub=. Refresh, bookmark or share a link and you land on the right tab.
Fixes
- Live nav refresh on plugin toggle. Toggling a plugin used to leave its nav entries / dashboard widgets stuck until F5.
usePluginUIgains a pub/sub: invalidating the cache notifies every mounted hook so contributions refetch in place. The toggle pill itself shows a spinner with "Enabling…" / "Disabling…" copy while the request is in flight. - Stale "update available" badge. After installing a fresh version (say v0.1.2), the badge would still claim "v0.1.1 available" until the 1h cache TTL passed — it compared with
latest !== installedinstead of strict semver. The check is nowlatest > installed(semver-aware) AND the cache invalidates on install/uninstall so the badge clears immediately. - Update resolver follows
/releaseswhen/latest404s. GitHub returns 404 on/releases/latestwhen no release is explicitly flagged as latest (some CI workflows skip the flag, plus transient API hiccups). The resolver now falls back to/releases?per_page=30sorted by semver desc — same logic GitHub uses internally. - Bust stale browser caches on version flip. Stale HTML cached for
/api/*URLs (from a past misconfig) could load the SPA shell on backend routes and produce blank pages. The SPA fallback now setsCache-Control: no-storeonindex.html, and/api/app/versionsendsClear-Site-Data: "cache"when the client'soscarr_vcookie doesn't match the running version — Chrome/Firefox/Edge purge their HTTP cache for the site on the spot. Sessions, settings and JWT survive. - Logs "Copy" button works on LAN dev.
navigator.clipboardrequires a secure context (HTTPS or localhost). Dev over a LAN IP had no clipboard, so the button silently failed. New helper falls back todocument.execCommand('copy')via a hidden textarea so the copy works on plain HTTP origins.
Schema migration
New boolean column AppSettings.verboseRequestLog (default false). Idempotent on existing rows.
Full changelog: v0.7.6...v0.7.7
Upgrading
```
docker pull ghcr.io/arediss/oscarr:0.7.7
```
or
```
docker pull ghcr.io/arediss/oscarr:latest
```
v0.7.5 — Plugin install hotfix (CSP + bind-mount perms)
Hotfix
Two prod-blocking bugs uncovered while testing the 0.7.4 plugin install flow against a live self-hosted instance.
Fixes
-
Resolve plugin install URL server-side. 0.7.4 added a frontend
fetch()toapi.github.comto prefer a Release asset over the GitHub-generated source tarball. The prod CSP blocksapi.github.comfromconnect-src, so the fetch silently failed and the install fell back to the source tarball — same broken end state as 0.7.3 for plugins like kedaewyn/Leonarr that don't commitdist/to their repo. The resolution now lives backend-side (already trusted with GitHub calls for the update check), so the fix actually applies in prod. The/plugins/installendpoint accepts{ repository }from the registry path and a raw{ url }for manual installs. -
Auto-chown the plugins directory at boot. Self-hosters with a host bind-mount for
/app/packages/plugins(the docker-compose default) hitEACCESwhen Oscarr tried tomkdirthe staging dir for an install — the host directory wasn't owned by uid 1001 (the in-containeroscarruser). The entrypoint nowchowns +chmod 2775(setgid) the plugins dir at boot, the same way it already handles/data. Files dropped by the host maintainer keep the right group and both sides can write.
If GITHUB_TOKEN is set on the Oscarr container's env, the install resolution path uses it — useful if your registry's source is rate-limited.
Full changelog: v0.7.4...v0.7.5
Upgrading
```
docker pull ghcr.io/arediss/oscarr:0.7.5
```
or
```
docker pull ghcr.io/arediss/oscarr:latest
```
v0.7.4 — Multi-tab dashboard + plugin install via release assets
Highlights
The admin dashboard becomes multi-tab — name them, add new ones, drag your widgets where you want them. And plugin installs finally respect prebuilt release assets, unblocking the plugins that ship a clean source repo with dist/ shipped only on the GitHub Release page.
Features
- Multi-tab dashboard. The dashboard hosts multiple named tabs above the grid. Add a tab with the
+button, double-click a tab to rename, remove the active one in edit mode (the last remaining tab is locked so you're never empty). Widgets stay unique across the whole layout. - Reset confirm modal. The Reset-to-default action moved out of the toolbar into a proper a11y-aware modal (Escape, focus-trap, focus-return) and only renders in edit mode where it belongs.
Fixes
- Plugin install respects release assets. The Discover-tab installer used to hardcode
tarball/HEAD— GitHub's auto-generated source archive that omits gitignored build outputs. Plugins that shipdist/as a Release asset (the recommended pattern) would land an unbuilt tree and fail withCannot find module .../dist/index.js. Oscarr now resolves the install URL via the GitHub Releases API: the first.tar.gzasset on the latest release wins, with a fallback totarball/HEADfor plugins that still commitdist/to their repo.
Docs
- Plugin release workflow. docs/plugins.md grows a Releasing a plugin section with a copy-paste GitHub Actions workflow that builds, packs and uploads a tarball asset on every
v*tag — so future plugin authors land on the asset path by default and stop polluting their git history with built code.
Schema migration
The dashboard layout schema bumps to v2 (tabs). Existing v1 layouts are auto-migrated on the next GET into a single Main tab — the row is only rewritten on the next save. No DB migration required.
Full changelog: v0.7.3...v0.7.4
Upgrading
```
docker pull ghcr.io/arediss/oscarr:0.7.4
```
or
```
docker pull ghcr.io/arediss/oscarr:latest
```
v0.7.3 — Composable admin dashboard + plugin install fix
Highlights
The admin Dashboard tab is now a composable widget grid: drag-and-drop, three built-in widgets shipped, and plugins can contribute their own via a typed hook point. Plus the plugin install path is fixed for the Docker setups where /tmp and /app live on different filesystems.
Features
- Composable admin dashboard. A drag-and-drop widget grid replaces the static DashboardTab. Three built-ins ship out of the box: stats counters (users / pending requests / services / plugins), service health (reachability + version per configured service), and system (Oscarr version + plugin update count). The layout is persisted globally per Oscarr instance.
- Plugin-extensible. Plugins contribute widgets via the new
admin.dashboard.widgethook point. A typed Zod sub-schema validates widget props (id,title,icon,defaultSize,minSize,maxSize) at plugin load — malformed manifests are rejected before the widget can reach the dashboard. - WYSIWYG edit mode. Editing the dashboard no longer wraps each widget in an extra card with a redundant title. The drag handle and remove button float in the corners with a dashed outline; what you see in edit is what you get in view.
Fixes
- Plugin install across filesystems. Plugin tarballs used to be staged under
os.tmpdir()and then renamed into<pluginsDir>/<id>. In Docker setups where/tmpis a tmpfs mount and/applives on the persistent volume,fs.renamethrew EXDEV ("cross-device link not permitted") and installs failed after a successful download. The installer now stages inside the plugins dir itself (under a hidden name the loader ignores) so the final rename is atomic.
Plugin contract
A new section in docs/plugins.md documents the admin.dashboard.widget hook point — manifest example, props schema, frontend contract.
Full changelog: v0.7.2...v0.7.3
Upgrading
```
docker pull ghcr.io/arediss/oscarr:0.7.3
```
or
```
docker pull ghcr.io/arediss/oscarr:latest
```
v0.7.2 — Prisma CLI resolution fix
Hotfix
Quick follow-up to v0.7.1 — the boot-time `prisma migrate deploy` was using a hardcoded path that worked in the prod image but failed in dev, since npm workspaces hoist the prisma binary to the monorepo root in dev (no packages/backend/node_modules/.bin/prisma).
Fix
ensureMigratednow resolves the prisma CLI via Node's module resolution (require.resolve('prisma/package.json')+pkg.bin) instead of a hardcoded path. Works in both setups:- Dev (npm workspaces): finds prisma in
<root>/node_modules/prisma - Prod image: finds prisma in
packages/backend/node_modules/prisma
- Dev (npm workspaces): finds prisma in
- No
npxdependency reintroduced — the prod image stays free of npm/yarn/corepack.
Production deploys of v0.7.1 weren't affected (the old hardcoded path matched the prod layout). This release future-proofs against any layout change.
Full changelog: v0.7.1...v0.7.2
Upgrading
```
docker pull ghcr.io/arediss/oscarr:0.7.2
```
or
```
docker pull ghcr.io/arediss/oscarr:latest
```
v0.7.1 — CSP hotfix (self-host Inter, permissive img-src)
Hotfix
Quick follow-up to v0.7.0 — the CSP set in v0.7.0 was rejecting Google Fonts (Inter) and external avatar sources (Plex sync, Discord OAuth) in the browser console.
Fixes
- Inter font is now self-hosted via
@fontsource/inter— bundled with the frontend (weights 300/400/500/600/700/800), no more roundtrip tofonts.googleapis.com/fonts.gstatic.com. Faster boot, zero external tracking, zero CSP friction. img-srcrelaxed to'self' data: blob: https:— any HTTPS image source is allowed. Avatars synced from Plex, Discord, Jellyfin, Emby, or any future provider load without per-source allowlisting.<img>can't execute code, and on a self-hosted reverse-proxied app the alternative (endless allowlist updates) was friction with no real security benefit.- Everything else stays strict:
script-src 'self'+ the static importmap hash,connect-srclimited to TMDB only,default-src 'self', no'unsafe-inline'on scripts.
Full changelog: v0.7.0...v0.7.1
Upgrading
```
docker pull ghcr.io/arediss/oscarr:0.7.1
```
or
```
docker pull ghcr.io/arediss/oscarr:latest
```
v0.7.0 — Plugin engine v1.1, hardening waves & audit pass
What's New
Plugin Engine v1.1
- Extended
PluginContext— additivetmdb:read/requests:read/requests:writecapability buckets,media.batchStatus,requests.create(shared pipeline with the HTTP route),getArrClients,findUserByEmail,listFolderRules, and aneventsbus with on/off/emit - Capability gating tightened — every ctx method that touches data now declares a capability, manifests are runtime-validated against the capability enum, and disabling a service in the admin panel immediately stops leaking its url+apiKey to plugins
- Lifecycle cleanup — event-bus listeners and notification providers a plugin registered are now dropped on disable + uninstall (no more zombie handlers wired to the singleton)
- Plug-and-play link flows — plugins can mint a JWT cookie via
ctx.app.internalFetch, and a logged-out user hitting an OAuth?action=linkURL is redirected to/login?next=…with a same-origin guarded post-login bounce - Per-plugin Tailwind bundles — each plugin compiles its own CSS bundle so a plugin's classes can't conflict with the host app
- Hot-installed plugins are immediately schedulable — manual triggers and cron ticks pick up newly registered jobs without a process restart
Auth Provider System
- Discord OAuth — full sign-in / link / sync flow with single-use state nonces and intent binding
- Per-provider signup gates — each AuthProvider has its own
allowSignuptoggle (including email), secure-by-default, no global master switch - Generic user sync — provider-agnostic sync interface with Plex as the first concrete adapter
- Selective import via review modal — admins cherry-pick which provider users land in Oscarr instead of importing everyone
User.disabledflag — soft-disable accounts with a configurable login mode (friendlyshows a localised banner,blockreturns generic invalid credentials)
Security Hardening (waves 1 + 2)
- CSRF gate — every
/api/admin/*request must carry theX-Requested-With: oscarrheader - SSRF guard —
assertPublicUrlconsistently applied on admin-typed URLs; plugin installer pins resolved IPs viaAgent.connect.lookupto defeat DNS rebind - Open-redirect proof —
?next=and OAuth callback bounces use a URL-parser + origin match (rejects//evil.com,/\evil.com,data:,javascript:) - RBAC fail-closed — DB outage during user-state lookup no longer keeps a disabled user authorised; falls back to last-known-good cache or treats as disabled
view-as-roleallow-list — header is now restricted to known roles- Login error tokens i18n — backend returns UPPER_SNAKE tokens (
ACCOUNT_DISABLED,INVALID_CREDENTIALS) so the frontend can localise per-user - Webhook auth — timing-safe API key comparison
- Process-level guards —
uncaughtException/unhandledRejectionlog to AppLog then exit hard so the supervisor respawns cleanly
Performance
- SQLite WAL mode enabled — readers no longer block while a write transaction holds the lock; the UI stays fluid during long syncs
- Fire-and-forget admin job triggers —
POST /admin/jobs/:key/runreturns 202 immediately and the frontend polls for completion - Per-key job mutex — same job can't double-run when a manual trigger collides with a cron tick
- Hero progress bars — switched from
width: 0% → 100%(per-frame layout) to GPU-compositedtransform: scaleXfor smooth animation under load - Backup async —
runAutoBackupno longer blocks the event loop during scheduled backups
Sync Subsystem (audit fixes)
- TV placeholder upgrade — Sonarr's real
tmdbIdis now written at sync time, so homepage availability pills match without a click. Existing placeholder rows are merged into their canonical positive-tmdbId row on the next full sync, deduping requests by userId - Race-safe upgrade —
processSingleMediauses try-update / catch-P2002 / fall-back-to-merge instead of probe-then-act, so a concurrentfindOrCreateMediacan no longer race the unique constraint - Webhook 'added' parity — webhooks now enrich freshly-created rows with poster / quality / seasons via
getMediaByIdinstead of leaving them poster-less until the next periodic sync - Notification fan-out — silent
.catch(() => {})onsendAvailabilityNotificationsreplaced withlogEvent, so failed pushes for 'media available' surface in the admin Logs tab - Keyword sync — transient TMDB errors no longer poison
keywordIds = '[]'permanently; the sentinel is written only on explicit not-found (404 / 422)
Architecture Refactors
- Backend bootstrap split —
index.tsreduced to start orchestration; security / docs / routes / plugins / static / jobs each in its ownbootstrap/module - Routes decomposition —
routes/requestsandroutes/admin/serviceseach split into focused submodules (create, list, lifecycle, maintenance, …) - Admin tabs — Plugins and Services tabs separated into hooks + presentational components (
usePluginsTab,useServicesTab) - TMDB submodule — large
services/tmdb.tssplit into list / details / helpers - Shared media helpers —
findMediaByExternalIdandresolveTvdbIdextracted from three duplicated inline patterns - Notification translation — single source of truth in
@oscarr/shared/notificationTemplates; backend pre-translates at emit time so plugin subscribers receive readable text
UI / UX
- Admin layout — viewport-locked: top bar + sidebar fixed, only the content area scrolls (no more empty band at the bottom of the menu when scrolling)
- Centered admin search — desktop search bar now matches the
/hometopbar layout (absolute centered, max-w-lg) - Mobile inputs — 16px font-size on iOS Safari to suppress focus-zoom
- Sidebar search results — semantic groups + warning indicators for tabs needing attention
Backup
- Restore + rollback double-failure —
applyDbBufferreturns a structured{ ok, safetyPath, rollbackFailed, error }shape; the route surfaces a dedicated 500 + AppLog entry pointing at the safety copy when the rollback also fails - HMAC-signed backups — restore gates HMAC + admin password re-auth + SQLite magic byte check
Image hardening
- Stripped
npm,corepack, andyarnfrom the production image — none are needed at runtime, and they regularly ship transitive CVEs the scanner picks up.prisma migrate deployat boot now calls the local binary directly instead of going throughnpx
Full changelog: v0.6.3...v0.7.0
Upgrading
```
docker pull ghcr.io/arediss/oscarr:0.7.0
```
or
```
docker pull ghcr.io/arediss/oscarr:latest
```
WAL is enabled automatically on first boot. The next full_sync (manual or scheduled) will merge any duplicate TV rows created before this version.