Refresh the official Deno sidecar pin to v2.9.1 after checking the upstream release notes.
Update Windows and Linux archive checksums in engine-sources.lock.json and replace the bundled macOS arm64 executable with the official v2.9.1 artifact.
Treat max_tries as the configured retry count instead of total attempts across native, media, aria2, and fallback paths.
Route queue slot acquisition through retirement-aware permit handling so shrinking concurrency does not dispatch new work while active transfers still meet the smaller limit.
Update retry-budget and queue-manager tests to cover the corrected attempt counts and paused aria2 resume behavior.
Update npm lockfile packages, compatible Rust lockfile entries, and GitHub Actions release workflow actions.
Bundle the macOS aria2 OpenSSL legacy provider and set OPENSSL_MODULES for aria2 version checks and daemon startup so packaged builds do not depend on Homebrew OpenSSL provider paths.
Keep the Rust Tauri crate family on the previously verified patch line and hold time at 0.3.49 because newer compatible lockfile candidates failed local cargo verification.
Disable the native Windows frame at startup and render in-app window controls so packaged builds use Firelink's own titlebar placement instead of the default OS chrome.
Apply CREATE_NO_WINDOW to raw child processes used by the aria2 daemon and shared engine version checks. Route ffmpeg and deno checks through the same hidden version-check helper so Settings > Engine does not flash console windows.
Normalize Windows extended-length paths before returning approved download roots to the UI, while keeping canonicalization for backend safety checks. Detect yt-dlp's embedded runtime per platform so Windows onedir builds look for python*.dll instead of macOS Python.framework.
Verification: npm run build; cargo check --manifest-path src-tauri/Cargo.toml; cargo test --manifest-path src-tauri/Cargo.toml --lib; node --check scripts/smoke-packaged-app.js; Ruby YAML parse for .github/workflows/release.yml; git diff --check.
When a server (like fuckingfast.co) ignores the upper bound of a range request (returning bytes=start-end/total), aria2 aborts with 'Invalid range header'. This patch treats that error as transient and mutates the download payload to force connections=1 before re-enqueuing. This ensures aria2 issues an open-ended range request (bytes=start-) on the retry, seamlessly resuming the download.
aria2 prefixes HTTP statuses with 'status=XXX' in its onDownloadError payload instead of 'HTTP XXX'. These were falling through the transient check and causing instant, permanent failure instead of triggering the escalating backoff schedule.
Fallback to GET Range for metadata when HEAD fails or misses Content-Length. Fetch aria2 WS error payload via tellStatus. Escalate 429 retries with specific 60/120/300s backoff. Persist download fraction for paused tasks.
- Pass captured cookies and headers from UI state into the backend `fetch_metadata` command.
- Implement secure domain-matching redirect policy in the backend to prevent leaking cookies, headers, and Basic Auth credentials to third-party domains upon redirect.
- Ensure metadata fetches correctly identify authenticated files (e.g. from Gofile) without being redirected to login/error pages.
- Update 'fetch_metadata' to detect 'Accept-Ranges' HTTP header
- Persist 'resumable' flag in DownloadItem across backend and frontend
- Add confirmation dialog when pausing non-resumable downloads (single and bulk actions)
- Improve UI for start/pause/options buttons by adopting the application accent color on hover
- Pass '--stop-with-process' to the aria2c command using the app's Process ID.
- Prevents aria2c from becoming an orphaned process and continuing downloads invisibly in the background after the app is forcefully closed or terminated.
- Prevent backward state transitions for downloads in frontend store.
- Add Downloading event emission upon aria2 unpause.
- Fix toast container visual bleed on exit by adding overflow-hidden.
- Add retry backoff for deleted files to prevent Windows file lock errors.
- Lowercase checksum hash types before passing to aria2.
- Clean up lingering files upon checksum verification failure.
- Convert Tauri AppHandle arguments to support generic runtimes.
- Remove deprecated `cocoa` crate in favor of direct `objc` usage to fix future-incompatibilities warning from `block` dependency.
- Fix irrefutable `if let` pattern warning in macOS `SleepPreventer::Drop`.
- Fix memory leaks by properly releasing `alloc`-ed `NSString` and `NSAppleScript` objects.
- Fix AppKit thread safety by routing dock badge updates to the main thread via `run_on_main_thread`.
- Fix "Date added" showing "Invalid date" on redownload by standardizing to ISO string format
- Update handleExtensionDownload to fallback to AddDownloadsModal on silent download dispatch failure
- Fix macOS sleep prevention by utilizing IOKit assertions (PreventSystemSleep and NetworkClientActive) to maintain network connection when display sleeps
- Improve resumeDownload to eagerly update UI state to "Queued" to fix progress bar freezing on resume
- Ensure robust error handling for removeDownload to maintain consistent frontend store state
- Fix re-entrant deadlock in `legacy_download_queue_paths` by scoping DB lock
- Ensure `release_registered_id` runs in `remove_download` even if cleanup fails
- Add fallback to `std::fs::remove_file` if `trash::delete` fails
- Ignore 'cannot be paused now' error in `detach_download_for_reconfigure`
- Apply `break-all` and `whitespace-pre-wrap` to Toast messages to prevent UI overflow
The root cause was hydrate_extension_pairing_token accessing the
keychain when keychainAccessGranted was true in the DB. Any keychain
access on macOS triggers the system prompt when the binary signature
changes after an update.
Architecture change: two-store model.
- The SQLite settings DB is now the primary store for the token.
hydrate_extension_pairing_token reads from it exclusively -- it
never touches the OS keychain. No system prompt on startup.
- The OS keychain remains defence-in-depth: grant_keychain_access
still writes the token there, but it is only reached from the
explicit Grant Access button, so any system prompt is user-initiated.
DB helpers: load_pairing_token_from_settings / save_pairing_token_to_settings
hydrate_extension_pairing_token: reads from DB, skips keychain entirely
grant_keychain_access: syncs token to DB after keychain access
Frontend: extensionPairingToken included in partialize for auto-persist
- Revert hydrate_extension_pairing_token to consult the DB's
keychainAccessGranted flag instead of unconditionally skipping the
keychain. When the flag is true the backend tries the keychain; if
macOS trusts the current binary (no code-signature change) the read
succeeds silently and the modal is suppressed. When the flag is
false the keychain is skipped and the modal is shown exactly once.
- Fix the Grant Access button in the KeychainPermissionModal by
writing the result (token, persistent flag) directly into the store
instead of calling hydratePairingToken() again. The re-hydration
raced with Zustand's async persist middleware, which could read
keychainAccessGranted=false from the DB and flip persistent back
to false, re‑showing the modal in an endless loop.
- The scheduler defaults (enabled: false) were already correct in
both the Rust default_settings() and the frontend Zustand store;
no code change needed.
- Disable log capture by default in both backend (LOG_PAUSED=true) and
frontend (isPaused=true) to avoid unnecessary disk I/O and battery drain.
- Add a global contextmenu listener that prevents the webview's default
right-click menu (Reload, etc.) so the app behaves like a native macOS
window. Custom context menus in DownloadItem, LogsView, and Sidebar
still work because their handlers preventDefault() before the document
listener fires.
- Sort the download table by queuePosition when viewing a specific queue
so the move-up/down controls produce a visible reorder instead of a
silent position swap with no UI feedback.
- Rework the keychain access flow to eliminate the OS credential prompt
that appeared before the app's own KeychainPermissionModal after every
binary update. hydrate_extension_pairing_token now always skips the
keychain; only the explicit Grant Access button (grant_keychain_access)
triggers the OS prompt, which is user-initiated and therefore acceptable
even when macOS trust resets after a code-signature change.
- Ensure yt-dlp partial files are cleaned up on error exits.
- Handle Semaphore closure gracefully without panicking.
- Replace busy-polling in wait_permit_released with Notify.
- Fix extension server silent failure with retry loop and logging.
- Ignore malformed existing scheduler settings instead of failing.
- Add final symlink checks before revealing/opening downloads.
- Delay listener registration in App.tsx until core is ready.
- Sanitize Referer header to prevent injection.
This entirely removes 'sanitize_current_settings_and_restore_token' from 'db::init' and removes the keychain hydration from 'setup()'. The backend will now never access the keychain during bootstrapping, relying purely on the frontend to explicitly trigger token hydration and migration via Tauri IPC when the UI mounts. This guarantees the macOS system prompt will never block the app from opening, and will only appear after the window is fully visible.
This fixes a severe issue where 'hydrate_pairing_token' was hardcoded to fetch from the keychain on startup and via the 'hydrate_extension_pairing_token' command. This caused multiple macOS keychain prompts to appear back-to-back before the app even loaded. We now correctly check the user's 'keychainAccessGranted' preference from the DB payload and skip keychain operations entirely if they haven't explicitly granted access yet.
This fixes the issue where the app forces a macOS system prompt for credential store access on first launch/update. Instead, the legacy token is safely preserved in the persistent JSON document and is now only forcefully migrated to the macOS keychain when the user manually hits 'Grant Access' in the UI modal.