diff --git a/.release-notes/v2.2.0.md b/.release-notes.md similarity index 100% rename from .release-notes/v2.2.0.md rename to .release-notes.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 659e7737..89ea6297 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,103 +1,3 @@ -# [2.2.0](https://github.com/snapotter-hq/snapotter/compare/v2.1.0...v2.2.0) (2026-07-29) - - -### Bug Fixes - -* **a11y:** focus indicators meet the 3:1 non-text contrast bar ([#574](https://github.com/snapotter-hq/snapotter/issues/574)) ([6339370](https://github.com/snapotter-hq/snapotter/commit/63393700939e34ad3d847043b03728e3c2907445)), closes [#A85518](https://github.com/snapotter-hq/snapotter/issues/A85518) [#F0A766](https://github.com/snapotter-hq/snapotter/issues/F0A766) -* **a11y:** WCAG AA contrast retune for the Otter Orange palette ([#567](https://github.com/snapotter-hq/snapotter/issues/567)) ([5102262](https://github.com/snapotter-hq/snapotter/commit/51022628dc19d8a03525bab98473f3c23b1a2709)), closes [#E07832](https://github.com/snapotter-hq/snapotter/issues/E07832) [#1A1814](https://github.com/snapotter-hq/snapotter/issues/1A1814) -* **ai-bridge:** surface sidecar exit reasons in Sentry via SafeError ([#538](https://github.com/snapotter-hq/snapotter/issues/538)) ([55e1e95](https://github.com/snapotter-hq/snapotter/commit/55e1e95f20792cae4f1f2257bafe839b83332c93)) -* **ai-bundles:** stop CPU onnxruntime from clobbering onnxruntime-gpu ([#544](https://github.com/snapotter-hq/snapotter/issues/544)) ([c8629c9](https://github.com/snapotter-hq/snapotter/commit/c8629c9d22b455b6deac206e3816ae0f730f06c6)), closes [#490](https://github.com/snapotter-hq/snapotter/issues/490) -* **ai:** advance the progress bar during upscale and background removal ([#608](https://github.com/snapotter-hq/snapotter/issues/608)) ([e56edc6](https://github.com/snapotter-hq/snapotter/commit/e56edc659f742430798104b52a08806a8d8fbff2)), closes [#591](https://github.com/snapotter-hq/snapotter/issues/591) -* **ai:** warn that upscale and background removal are slow without a GPU ([#605](https://github.com/snapotter-hq/snapotter/issues/605)) ([d43208b](https://github.com/snapotter-hq/snapotter/commit/d43208b85e1ba1f0ac687970b8fb52ca1738ae15)), closes [#591](https://github.com/snapotter-hq/snapotter/issues/591) -* **api:** contain library stored-name path traversal ([#600](https://github.com/snapotter-hq/snapotter/issues/600)) ([4333432](https://github.com/snapotter-hq/snapotter/commit/43334324c446c53eb1dc2f8ef14cbecca4e2a676)) -* **api:** enforce job ownership on cancel endpoint ([#599](https://github.com/snapotter-hq/snapotter/issues/599)) ([577d74b](https://github.com/snapotter-hq/snapotter/commit/577d74bdb134d9a7d02c27ba976731839d071e63)) -* **api:** gate every tool endpoint and stop ZIP streams failing quietly ([#646](https://github.com/snapotter-hq/snapotter/issues/646)) ([2d8b57c](https://github.com/snapotter-hq/snapotter/commit/2d8b57c57fad4e1d98ab7eb327198809aa64bd09)), closes [#645](https://github.com/snapotter-hq/snapotter/issues/645) [#643](https://github.com/snapotter-hq/snapotter/issues/643) [#645](https://github.com/snapotter-hq/snapotter/issues/645) -* **api:** wait for Postgres and Redis at startup instead of crash-looping ([#537](https://github.com/snapotter-hq/snapotter/issues/537)) ([4ac89fe](https://github.com/snapotter-hq/snapotter/commit/4ac89fe6505e50645174e8de5fcdc40c46420cc4)) -* **audio:** expose sample rate setting in Convert Audio ([#561](https://github.com/snapotter-hq/snapotter/issues/561)) ([d4eaa65](https://github.com/snapotter-hq/snapotter/commit/d4eaa655b24236da5633006871415afbf0c92d08)), closes [#558](https://github.com/snapotter-hq/snapotter/issues/558) -* **auth:** close the MFA policy lockout and add self-service enrollment ([#531](https://github.com/snapotter-hq/snapotter/issues/531)) ([190d4c2](https://github.com/snapotter-hq/snapotter/commit/190d4c2a002c6d3d240ff18480e37217fcbeae12)), closes [#529](https://github.com/snapotter-hq/snapotter/issues/529) [#515](https://github.com/snapotter-hq/snapotter/issues/515) -* **auth:** give OIDC/SAML logins a real MFA challenge instead of a hard block ([#536](https://github.com/snapotter-hq/snapotter/issues/536)) ([bbfcbe9](https://github.com/snapotter-hq/snapotter/commit/bbfcbe9c8212afb244bb2738a6e3643d06dcee68)), closes [#533](https://github.com/snapotter-hq/snapotter/issues/533) -* **background-removal:** surface failures with a diagnosable SafeError ([#535](https://github.com/snapotter-hq/snapotter/issues/535)) ([43ddf96](https://github.com/snapotter-hq/snapotter/commit/43ddf96ccf7baf94416050ec6229efca68124679)) -* **ci:** repair the chronically-failing nightly workflow ([#624](https://github.com/snapotter-hq/snapotter/issues/624)) ([44f5aea](https://github.com/snapotter-hq/snapotter/commit/44f5aea3268f022641832af4b8c60cbd2d5e2c7e)) -* **compress-pdf:** land close to the target size, honestly ([#522](https://github.com/snapotter-hq/snapotter/issues/522)) ([7d938af](https://github.com/snapotter-hq/snapotter/commit/7d938af1f9e601238fd49af4c2cd157d16616266)) -* **deps:** patch 4 HIGH/CRITICAL transitive CVEs (fast-uri, svgo, sharp, tar) ([#619](https://github.com/snapotter-hq/snapotter/issues/619)) ([10a2aab](https://github.com/snapotter-hq/snapotter/commit/10a2aabe589b5afd470f6c47ccdb1bf8bca15c02)) -* **doc-engine:** guard sidecar JSON parsing against non-JSON stdout ([#532](https://github.com/snapotter-hq/snapotter/issues/532)) ([a2cb1a8](https://github.com/snapotter-hq/snapotter/commit/a2cb1a8261b2669ae7e656a418999b70a37f6b4f)) -* **docker:** harden Postgres readiness checks in compose and startup ([#595](https://github.com/snapotter-hq/snapotter/issues/595)) ([4ba7503](https://github.com/snapotter-hq/snapotter/commit/4ba7503f15a4444c4937576dbcadafdb19a8848d)), closes [#592](https://github.com/snapotter-hq/snapotter/issues/592) -* **docs:** keep the nav within the viewport on tablets and laptops ([#570](https://github.com/snapotter-hq/snapotter/issues/570)) ([69f72a6](https://github.com/snapotter-hq/snapotter/commit/69f72a6c712512e8489b23bcf78a233d872d897c)), closes [#556](https://github.com/snapotter-hq/snapotter/issues/556) -* **docs:** keep the translated locale trees out of the search index ([#662](https://github.com/snapotter-hq/snapotter/issues/662)) ([42e1dc9](https://github.com/snapotter-hq/snapotter/commit/42e1dc9799f60c7dde46b9485e5f37a5a29630ae)) -* **docs:** localize the docs homepage, nav, and sidebar chrome across 20 languages ([#547](https://github.com/snapotter-hq/snapotter/issues/547)) ([9247947](https://github.com/snapotter-hq/snapotter/commit/92479477042740d7026de9b9bb3c71eeb0668231)) -* **download:** ask reverse proxies not to buffer file downloads ([#604](https://github.com/snapotter-hq/snapotter/issues/604)) ([89d7585](https://github.com/snapotter-hq/snapotter/commit/89d75853f45c98d6e9cae661dbda02bc8285009b)), closes [#590](https://github.com/snapotter-hq/snapotter/issues/590) -* **download:** reset the socket when a stream is shorter than Content-Length ([#617](https://github.com/snapotter-hq/snapotter/issues/617)) ([0467e87](https://github.com/snapotter-hq/snapotter/commit/0467e87bfe410b313fa21d9758f8c162b12113a6)), closes [#590](https://github.com/snapotter-hq/snapotter/issues/590) [#590](https://github.com/snapotter-hq/snapotter/issues/590) [#590](https://github.com/snapotter-hq/snapotter/issues/590) -* **editor:** repair rotate/flip/resize, levels/curves, filters, and layer lock ([#597](https://github.com/snapotter-hq/snapotter/issues/597)) ([a6bce68](https://github.com/snapotter-hq/snapotter/commit/a6bce6825ae27890951becf7c858d4626c513714)) -* enforce role authority for user management ([#616](https://github.com/snapotter-hq/snapotter/issues/616)) ([1f8a42e](https://github.com/snapotter-hq/snapotter/commit/1f8a42e54822142132270df1aefa768f4626613f)) -* enforce settings authority boundaries ([#618](https://github.com/snapotter-hq/snapotter/issues/618)) ([44d8109](https://github.com/snapotter-hq/snapotter/commit/44d8109486aa6efd9383e573f0a36deb4ffda460)) -* **erase-object:** crop-based HD inpainting to remove ghosting and blur ([#501](https://github.com/snapotter-hq/snapotter/issues/501)) ([380419d](https://github.com/snapotter-hq/snapotter/commit/380419dd0603693ad542082bd6197e6505f9b1e9)), closes [#491](https://github.com/snapotter-hq/snapotter/issues/491) [hi#res](https://github.com/hi/issues/res) [#141](https://github.com/snapotter-hq/snapotter/issues/141) -* **files:** decode CLI-decoded formats before URL-import preview generation ([#637](https://github.com/snapotter-hq/snapotter/issues/637)) ([841f47f](https://github.com/snapotter-hq/snapotter/commit/841f47f6ca25e79dc56dc1e735afa37f02023f99)), closes [#634](https://github.com/snapotter-hq/snapotter/issues/634) -* **files:** store null instead of 0x0 for undecoded upload dimensions ([#636](https://github.com/snapotter-hq/snapotter/issues/636)) ([511633f](https://github.com/snapotter-hq/snapotter/commit/511633fa1c76f00fe9c4e2a0935a02b5601cae6a)), closes [#635](https://github.com/snapotter-hq/snapotter/issues/635) -* give remove-background job timeouts an actionable failure message ([#518](https://github.com/snapotter-hq/snapotter/issues/518)) ([58121f2](https://github.com/snapotter-hq/snapotter/commit/58121f205f7ae2b34b3d789c80db37c8bf0950bf)), closes [#494](https://github.com/snapotter-hq/snapotter/issues/494) -* **help:** render the help dialog from i18n instead of hardcoded English ([#647](https://github.com/snapotter-hq/snapotter/issues/647)) ([2848dd0](https://github.com/snapotter-hq/snapotter/commit/2848dd0e53d8a7c1e19ee82c1a1f2d485380d356)), closes [#644](https://github.com/snapotter-hq/snapotter/issues/644) -* honor unlimited processing timeout ([#638](https://github.com/snapotter-hq/snapotter/issues/638)) ([025851b](https://github.com/snapotter-hq/snapotter/commit/025851beefaac6f8d12c1dbcdb69276965072ac9)) -* **image-tools:** surface Sharp encode failures instead of "Error: Error" ([#534](https://github.com/snapotter-hq/snapotter/issues/534)) ([9cccbc9](https://github.com/snapotter-hq/snapotter/commit/9cccbc957681f8d12547e87be0e335f98e789cb7)) -* **image:** decode real iPhone HEIC files instead of rejecting them at validation ([#631](https://github.com/snapotter-hq/snapotter/issues/631)) ([098ed50](https://github.com/snapotter-hq/snapotter/commit/098ed50d0693ba181fae6f934bf427079e1f8e9f)), closes [#622](https://github.com/snapotter-hq/snapotter/issues/622) -* **image:** image-to-pdf presets no longer 404 on 2+ files ([#633](https://github.com/snapotter-hq/snapotter/issues/633)) ([330cf55](https://github.com/snapotter-hq/snapotter/commit/330cf559e00b6e5f1ca8df199cabee4c0fa650d0)), closes [#627](https://github.com/snapotter-hq/snapotter/issues/627) -* **jobs:** classify BullMQ stall (UnrecoverableError) as operational ([#610](https://github.com/snapotter-hq/snapotter/issues/610)) ([e537cb0](https://github.com/snapotter-hq/snapotter/commit/e537cb04013d7bf4fd0fb125fb72f42f5a51917f)) -* **jobs:** make timeout messages tool-agnostic and CPU-aware ([#596](https://github.com/snapotter-hq/snapotter/issues/596)) ([37c915c](https://github.com/snapotter-hq/snapotter/commit/37c915c3da6b2183b0befe1521a643bb36edcce6)), closes [#591](https://github.com/snapotter-hq/snapotter/issues/591) -* **landing-i18n:** commit missed landing-seo re-translation (remove.bg comparison) ([b8c3700](https://github.com/snapotter-hq/snapotter/commit/b8c3700c15333b2eb328cab0979f8d943c15d7ab)) -* **landing,docs:** use build-time GitHub star count, drop per-page api.github.com fetch ([#560](https://github.com/snapotter-hq/snapotter/issues/560)) ([54073a7](https://github.com/snapotter-hq/snapotter/commit/54073a7c501788dea49f77f6e94bdebb9b8f0f65)) -* **landing:** derive conversion-preset copy from real format differences ([#663](https://github.com/snapotter-hq/snapotter/issues/663)) ([a75a22d](https://github.com/snapotter-hq/snapotter/commit/a75a22dd3ff8e675002a13da0ec12d00f91136c8)) -* **landing:** keep mixed-case locale casing in emitted URLs ([#562](https://github.com/snapotter-hq/snapotter/issues/562)) ([67f5434](https://github.com/snapotter-hq/snapotter/commit/67f54347b2ca711f40495fa7fcb05e132ea8f3da)), closes [#554](https://github.com/snapotter-hq/snapotter/issues/554) -* **landing:** link English-only tool-detail and self-hosted pages to un-prefixed URLs ([#553](https://github.com/snapotter-hq/snapotter/issues/553)) ([6ecc598](https://github.com/snapotter-hq/snapotter/commit/6ecc598fc4fc8fa6da5db9d9d0c66e53d2f2376d)) -* **landing:** open Product dropdown below the navbar bottom line ([#571](https://github.com/snapotter-hq/snapotter/issues/571)) ([e3a01b6](https://github.com/snapotter-hq/snapotter/commit/e3a01b6be6fa077fa00c6c7245be7ba18b6e913c)) -* **landing:** rework footer badge/switcher layout ([#514](https://github.com/snapotter-hq/snapotter/issues/514)) ([74e7d23](https://github.com/snapotter-hq/snapotter/commit/74e7d23abad924f04b348f00edf34e67188c4f2e)) -* **landing:** stop mangling #hash fragments in localized links ([#516](https://github.com/snapotter-hq/snapotter/issues/516)) ([5bd0522](https://github.com/snapotter-hq/snapotter/commit/5bd052286c5a7920161b21cb791cc122fe3a1616)), closes [#hash](https://github.com/snapotter-hq/snapotter/issues/hash) -* make OCR portable and reliable across AMD64 and ARM64 ([#519](https://github.com/snapotter-hq/snapotter/issues/519)) ([991c981](https://github.com/snapotter-hq/snapotter/commit/991c981529c81ebb719199aba084db4c2d9a7c6f)) -* **ocr:** unblock and harden accurate-OCR install ([#552](https://github.com/snapotter-hq/snapotter/issues/552)) ([bda4db3](https://github.com/snapotter-hq/snapotter/commit/bda4db3f358eebd659a1f8b7892112061a82b000)) -* **pdf:** flag scanned PDFs in pdf-to-text and serve text as UTF-8 ([#603](https://github.com/snapotter-hq/snapotter/issues/603)) ([7d37f6e](https://github.com/snapotter-hq/snapotter/commit/7d37f6e6f5aa68aa5954182d76bb6ccf1c6b0649)), closes [#589](https://github.com/snapotter-hq/snapotter/issues/589) -* **pdf:** pdf-to-image presets no longer 404 on 2+ files ([#643](https://github.com/snapotter-hq/snapotter/issues/643)) ([a713795](https://github.com/snapotter-hq/snapotter/commit/a7137958a158309ff8128490de96313e517ae554)), closes [#627](https://github.com/snapotter-hq/snapotter/issues/627) [#633](https://github.com/snapotter-hq/snapotter/issues/633) [#632](https://github.com/snapotter-hq/snapotter/issues/632) -* **pdf:** restore downloads on PDF conversion preset pages ([#629](https://github.com/snapotter-hq/snapotter/issues/629)) ([e0a7aec](https://github.com/snapotter-hq/snapotter/commit/e0a7aecde8f194b6375747a5a5f8ea4c57053813)), closes [#623](https://github.com/snapotter-hq/snapotter/issues/623) -* **pdf:** stop page tools failing on short and encrypted PDFs ([#594](https://github.com/snapotter-hq/snapotter/issues/594)) ([73df107](https://github.com/snapotter-hq/snapotter/commit/73df1077580904c37d159dafe51a294b250bb074)) -* preserve colored blocks in PDF-to-Word ([#500](https://github.com/snapotter-hq/snapotter/issues/500)) ([e7cfc00](https://github.com/snapotter-hq/snapotter/commit/e7cfc00fe1d294256a0a4663797b8a8cdf58d8c0)) -* **readme:** self-host the star history chart ([#625](https://github.com/snapotter-hq/snapotter/issues/625)) ([90483c9](https://github.com/snapotter-hq/snapotter/commit/90483c944a4d4c912d3c5e2c09b2ee61bed3d150)) -* release QA hardening across processing, media, security, and CI gates ([#649](https://github.com/snapotter-hq/snapotter/issues/649)) ([d10d0f5](https://github.com/snapotter-hq/snapotter/commit/d10d0f544f2a093d2adb13cbd69d54a31c0cbf7a)) -* **release:** resolve the release by id, and make the vulnerability gate cover HIGH ([#661](https://github.com/snapotter-hq/snapotter/issues/661)) ([935861b](https://github.com/snapotter-hq/snapotter/commit/935861bced95cd778ea77d5a3741ff1da1a2ffed)), closes [#649](https://github.com/snapotter-hq/snapotter/issues/649) -* **security:** bump Pillow to 12.3.0, fixes 5 disclosed CVEs ([#517](https://github.com/snapotter-hq/snapotter/issues/517)) ([30d36d7](https://github.com/snapotter-hq/snapotter/commit/30d36d7949817751b869916775138dc4fa7a34d2)) -* **security:** close the gaps a full 2.0 re-audit left open ([#620](https://github.com/snapotter-hq/snapotter/issues/620)) ([079fcd2](https://github.com/snapotter-hq/snapotter/commit/079fcd2631216147db4b68acbcaca58ac4f93fb4)) -* **seo:** emit clean docs URLs and de-index the demo ([#598](https://github.com/snapotter-hq/snapotter/issues/598)) ([e6718ad](https://github.com/snapotter-hq/snapotter/commit/e6718ada719e8931f643e1184aee354d4698c1da)) -* **settings:** let admins relax the minimum password length to 1 ([#543](https://github.com/snapotter-hq/snapotter/issues/543)) ([846044a](https://github.com/snapotter-hq/snapotter/commit/846044a463f18f490f7752b0cb6b4b225b107367)), closes [#136](https://github.com/snapotter-hq/snapotter/issues/136) -* **sign-pdf:** surface PDF load failures instead of a blank canvas ([#545](https://github.com/snapotter-hq/snapotter/issues/545)) ([1f4878a](https://github.com/snapotter-hq/snapotter/commit/1f4878ac4df417331a8cff9b8d5ab4da2edc788a)) -* **telemetry:** classify environmental database errors as operational ([#540](https://github.com/snapotter-hq/snapotter/issues/540)) ([631d82e](https://github.com/snapotter-hq/snapotter/commit/631d82eaae36a15d401c7408182d52a0754b1e36)) -* **telemetry:** data-quality pass (opt-in noise, onboarding split, file_count, OIDC) ([#614](https://github.com/snapotter-hq/snapotter/issues/614)) ([b20bca3](https://github.com/snapotter-hq/snapotter/commit/b20bca3c3c4ef3a63aca6ba420986d49bbeb905b)), closes [hi#level](https://github.com/hi/issues/level) -* **telemetry:** fingerprint stackless uncaught errors so they stop collapsing ([#611](https://github.com/snapotter-hq/snapotter/issues/611)) ([82f5708](https://github.com/snapotter-hq/snapotter/commit/82f5708193984e863b95319e0e0ec8be47e46075)) -* **telemetry:** sharpen Sentry signal for v2.1.0 residual defects ([#498](https://github.com/snapotter-hq/snapotter/issues/498)) ([b457596](https://github.com/snapotter-hq/snapotter/commit/b4575966495c1a022154149832aedcd727d411e2)) -* **telemetry:** surface AI sidecar and DOMException failure reasons in Sentry ([#612](https://github.com/snapotter-hq/snapotter/issues/612)) ([6a0768b](https://github.com/snapotter-hq/snapotter/commit/6a0768b39da648d3b62b39f2370b97f9ce876da0)), closes [#535](https://github.com/snapotter-hq/snapotter/issues/535) -* **tools:** classify expected input and timeout errors, not bugs ([#539](https://github.com/snapotter-hq/snapotter/issues/539)) ([39b89b9](https://github.com/snapotter-hq/snapotter/commit/39b89b9fbd4f67012b55ecdef35ddf369a3fb874)) -* **video:** write faststart mp4/mov output from stabilize-video ([#593](https://github.com/snapotter-hq/snapotter/issues/593)) ([df92f7e](https://github.com/snapotter-hq/snapotter/commit/df92f7ee42b22b5b6cb3434a6b972851388c67ad)), closes [#588](https://github.com/snapotter-hq/snapotter/issues/588) -* **web:** use h-dvh for app shells so mobile tool controls stay reachable ([#559](https://github.com/snapotter-hq/snapotter/issues/559)) ([3f7214b](https://github.com/snapotter-hq/snapotter/commit/3f7214bac22fe392eb7d179752e9c4583e4dbe0e)) - - -### Features - -* add landing testimonial wall, unblock the onboarding survey ([#639](https://github.com/snapotter-hq/snapotter/issues/639)) ([5cc0a85](https://github.com/snapotter-hq/snapotter/commit/5cc0a850c68e0af7a427b8442c24c55f6157416e)) -* **analytics:** instance census, full capture, richer error context ([#511](https://github.com/snapotter-hq/snapotter/issues/511)) ([e1b8c24](https://github.com/snapotter-hq/snapotter/commit/e1b8c24e5d78153a62629972932a65cd1491ee77)) -* clearer, disambiguated tool names across all surfaces ([#520](https://github.com/snapotter-hq/snapotter/issues/520)) ([f858c4c](https://github.com/snapotter-hq/snapotter/commit/f858c4cea00ae7165718509c8a78881fe6fc24fc)) -* **docs-i18n:** translate all documentation into 20 languages ([4963ab3](https://github.com/snapotter-hq/snapotter/commit/4963ab3bbd36fe86b856a3be791f192af4582142)) -* **erase-object:** add freeform lasso selection mode ([#503](https://github.com/snapotter-hq/snapotter/issues/503)) ([601557e](https://github.com/snapotter-hq/snapotter/commit/601557edaed32f331b5271334923f1c010098d91)) -* **erase-object:** optional high-quality diffusion inpainting bundle ([#566](https://github.com/snapotter-hq/snapotter/issues/566)) ([1bac663](https://github.com/snapotter-hq/snapotter/commit/1bac663a2e76c98313a7a56c97231c2bbc1b36f2)), closes [hi#quality](https://github.com/hi/issues/quality) [#141](https://github.com/snapotter-hq/snapotter/issues/141) -* **feedback:** gate onboarding survey on first processing, add prompt lifecycle events ([#615](https://github.com/snapotter-hq/snapotter/issues/615)) ([129e42b](https://github.com/snapotter-hq/snapotter/commit/129e42b95cd9c078eebcb3c52be10e8661dd7903)) -* **files:** add save-as-new vs overwrite choice for library file edits ([#564](https://github.com/snapotter-hq/snapotter/issues/564)) ([a23158d](https://github.com/snapotter-hq/snapotter/commit/a23158d968c32e8cdf319225d73bf51120f20cbf)), closes [#495](https://github.com/snapotter-hq/snapotter/issues/495) -* **i18n:** 21-language pipeline, landing/docs/API wiring, landing+API translations ([00b651c](https://github.com/snapotter-hq/snapotter/commit/00b651c9f851a8754cac755f6929ba2eeac8602b)) -* **image:** add rounded-square and squircle crop tool ([#602](https://github.com/snapotter-hq/snapotter/issues/602)) ([e7ffb37](https://github.com/snapotter-hq/snapotter/commit/e7ffb37e981c553cf17db88155ac12667f104062)), closes [#601](https://github.com/snapotter-hq/snapotter/issues/601) -* **landing-i18n:** recall a returning visitor's chosen language ([1bc9fe9](https://github.com/snapotter-hq/snapotter/commit/1bc9fe93ec4ca82295e77f2c8068bc700ef94367)) -* **landing:** add a live system status indicator to the footer ([#641](https://github.com/snapotter-hq/snapotter/issues/641)) ([d690a6e](https://github.com/snapotter-hq/snapotter/commit/d690a6e26d82f9783cac154faa11f19481902a0e)) -* **landing:** add Twelve Tools and Wired Business footer badges ([#512](https://github.com/snapotter-hq/snapotter/issues/512)) ([ce54d06](https://github.com/snapotter-hq/snapotter/commit/ce54d06e797c8f3fe25ed8c00ef42047ddf63882)) -* **landing:** move language switcher from navbar to footer ([#513](https://github.com/snapotter-hq/snapotter/issues/513)) ([2dee9ea](https://github.com/snapotter-hq/snapotter/commit/2dee9ead0b1c84c02831c5e92082cd4598b7b8f5)) -* **library:** wire save-mode into the five custom-client tool submitters ([#577](https://github.com/snapotter-hq/snapotter/issues/577)) ([1113c76](https://github.com/snapotter-hq/snapotter/commit/1113c761ead67664e4a7120b8bdf1b8e9da52c43)) -* **onboarding:** collect install method and friction area in the usage survey ([#499](https://github.com/snapotter-hq/snapotter/issues/499)) ([430b87e](https://github.com/snapotter-hq/snapotter/commit/430b87eda0078c9afe31aa49da1f2293c928a147)) -* **resize:** add aspect-ratio proportion presets ([#530](https://github.com/snapotter-hq/snapotter/issues/530)) ([d88999e](https://github.com/snapotter-hq/snapotter/commit/d88999e7a91d65adb37ea4cb3f9f7fe5650ad6c5)) -* **search:** start typing anywhere to fill the search box ([#644](https://github.com/snapotter-hq/snapotter/issues/644)) ([0058fc6](https://github.com/snapotter-hq/snapotter/commit/0058fc610f4b88ddf0660889b2ea78bf33a527d1)) -* **telemetry:** add a safe input_format tag to worker error reports ([#541](https://github.com/snapotter-hq/snapotter/issues/541)) ([281b4a0](https://github.com/snapotter-hq/snapotter/commit/281b4a06e39756a3a2b981e93d033454eb494c8c)) -* **telemetry:** Sentry + PostHog quality pass ([#546](https://github.com/snapotter-hq/snapotter/issues/546)) ([8625143](https://github.com/snapotter-hq/snapotter/commit/86251434b5858c63049c50dda44acb8e281a4cdf)) -* **tools:** remove background from animated GIFs (WebP, APNG) ([#502](https://github.com/snapotter-hq/snapotter/issues/502)) ([cb5db59](https://github.com/snapotter-hq/snapotter/commit/cb5db59f77e6b77fd9a575996b8514d6760aaf5b)) - # [2.1.0](https://github.com/snapotter-hq/snapotter/compare/v2.0.0...v2.1.0) (2026-07-10) diff --git a/apps/docs/changelog.md b/apps/docs/changelog.md index ced6789a..1c1dd361 100644 --- a/apps/docs/changelog.md +++ b/apps/docs/changelog.md @@ -4,125 +4,6 @@ description: Release notes and version history for SnapOtter. See what's new, im # Changelog {#changelog} -## v2.2.0 - -Most of 2.2 is hardening. A non-admin holding a delegated `users:manage` role could take over an instance, and every tool endpoint turned out to be missing its permission check. Upgrade if you run more than one user. - -The rest is the long tail of self-hosting: downloads that started and never finished, presets that broke the moment you added a second file, containers that crash-looped while Postgres was still coming up. Two new tools landed too. Docs and the website are now translated into 21 languages, and the interface finally clears WCAG AA contrast. - -> [!IMPORTANT] -> If you granted `users:manage` to a non-admin through a custom role, that role could take over administrator accounts on 2.0.0 through 2.1.0. Details in Security. - -### Security - -- **Privilege escalation through a delegated `users:manage` role (GHSA-9xgh-95qh-2x7h).** A non-admin holding `users:manage` through a custom role could reset the password of, or delete, a higher-privileged account, a built-in administrator included, and take over the instance. Password reset and account deletion now verify the caller's authority over the target, so a delegated role cannot reach above its own level. Affected: 2.0.0 through 2.1.0. Reported by 李春来 (Chunlai Li). (#616) -- **Every tool endpoint is now gated.** Tool access was enforced route by route, so all 45 hand-written routes had to remember the same call and none of them did. A role without `tools:use` could still run image-to-pdf, erase-object, upscale, sign-pdf and the rest. The check now lives in one preHandler keyed off the matched route, so it covers sub-paths and any route added later. (#646) -- **Rate limiting was bypassable on Docker installs.** The image shipped `TRUST_PROXY=true`, so the client IP came from a request header and a forged `X-Forwarded-For` walked past the login limiter. The default is now a private-network trust list. (#649) -- **Settings writes are checked per setting**, not once at the door, and config import is now transactional so a rejected key cannot leave half a config applied. (#618) -- **Library filenames cannot escape their storage root.** A crafted stored name, which a malicious 1.x SQLite import copies verbatim, could read or delete files outside the files directory. Only reachable after an operator imports an attacker-supplied 1.x SQLite database, which is the one path that lets a stored name be chosen. Reported by @Alpastx. (GHSA-55w2-8cqf-w969, #600) -- **Job cancellation enforces ownership**, so an authenticated user can no longer cancel another user's job by its id. Reported by @Alpastx. (GHSA-wqxf-gj2p-689x, #599) -- **A re-audit of the whole 2.0 tree caught what was still open**: a captured SAML assertion could be replayed, an MFA challenge survived any number of wrong codes, the per-request API-key lookup was a full table scan, `MAX_WORKSPACE_SIZE_GB` was dead config, and the SVG sanitizer let through unquoted `javascript:` hrefs. (#620) -- **Nine disclosed CVEs patched**: fast-uri, svgo, sharp and tar transitively (#619), plus Pillow 12.3.0 (#517). RAW decoding on arm64 now builds LibRaw 0.22.2 from source instead of linking an unpatched system copy. (#649) - -### New Features - -- **Rounded Crop**: rounded-square and iOS-style squircle masks, with a corner-radius control. Built for favicons and app icons. (#602) -- **Remove GIF Background**: strip the background from an animated GIF, WebP or APNG frame by frame and get a transparent animation back. (#502) -- **Object Eraser lasso and High Quality mode**: drag a freeform loop instead of painting every pixel, and optionally install a diffusion inpainting bundle for cleaner large-area fills. (#503, #566) -- **Save as new, or overwrite.** Editing a library file used to silently supersede the original. You now choose per edit, and the default keeps it. (#564, #577) -- **Aspect-ratio presets in Resize.** Pick 1:1, 4:3 or 16:9 and the two dimensions stay locked, without upscaling. (#530) -- **Two-tier OCR**: a fast tier is baked into the image and runs offline with no download, and a more accurate runtime installs on demand. (#519) -- **Type anywhere to search.** Start typing on the dashboard or the homepage and it lands in the search box. (#644) -- **Documentation and website in 21 languages**: the docs site, the landing pages and the API reference are all translated now, joining the app interface. A new low-resource deployment guide covers Raspberry Pi and 2 GB machines. (#548) -- **Clearer tool names**: 18 ambiguous names now self-qualify, so "Compress" became "Compress Image". Tool ids and routes are unchanged, so nothing bookmarked breaks. (#520) - -### Improvements - -- **WCAG AA contrast across the interface**: the palette was retuned so vivid orange stays on fills while text and labels use accessible ink tokens, and all 57 focus indicators now clear the 3:1 non-text bar. (#567, #574) -- **Upscale and background removal stop looking frozen**: the progress bar advances during inference instead of parking at 30%, and both tools now warn that a CPU-only host is much slower. (#605, #608) -- **Timeout messages name the real cause** and account for CPU-only hosts instead of blaming the upload. (#596) -- **SnapOtter waits for Postgres and Redis at startup** instead of crash-looping, in Compose and the all-in-one image alike. A Proxmox LXC install was restarting 76 times before this. (#537, #595) -- **Downloads survive a reverse proxy**: SnapOtter asks nginx and compatible proxies not to buffer file responses, which is the usual reason a self-hosted download starts and never finishes. (#604, #607) -- **Compress PDF lands near the target size**, and says so honestly when a target is out of reach. (#522) -- **Mobile tool controls stay reachable** now that the app shells size to the dynamic viewport. (#559) -- **Convert Audio exposes a sample-rate setting.** (#561) -- **Admins can relax the minimum password length**, down to 1 for a trusted LAN instance. (#543) -- **MFA is self-service**, the policy lockout is closed, and OIDC or SAML logins get a real MFA challenge instead of a hard block. (#531, #536) -- **A bare `Error: Error` now names its cause.** Sharp encode failures, AI sidecar exits, non-JSON document sidecar output, and background-removal failures all used to arrive with the reason scrubbed off. (#532, #534, #535, #538, #612) -- **The help dialog is translated.** Its shortcut labels and getting-started text were hardcoded English while finished translations sat unused in all 21 locale files. (#647) - -### Bug Fixes - -- **iPhone HEIC files were rejected** as unreadable before they reached the decoder that handles them. (#631) -- **Conversion presets failed on a second file**: jpg-to-pdf and its image-to-pdf siblings, plus pdf-to-png, pdf-to-jpg and pdf-to-tiff, all returned `Tool not found` once you uploaded two files. (#633, #643) -- **PDF conversion presets lost their download button.** (#629) -- **An unlimited processing timeout was not honored**, and stalled progress streams now recover instead of leaving the interface waiting. (#638) -- **Downloads hung instead of failing** when a stored file turned out shorter than its recorded length. (#617) -- **PDF page tools failed on short and encrypted files**: Remove Pages defaulted to a page range no document under six pages has, and password-protected PDFs failed cryptically inside the worker. (#594) -- **PDF to Text silently returned an empty file** for scanned PDFs. It now points you at OCR and serves text as UTF-8. (#603) -- **PDF to Word dropped colored text blocks** and split them across the page. (#500) -- **Object Eraser left ghost remnants** and blurred small objects in high-resolution images. (#501) -- **Stabilize Video wrote unplayable output** without faststart. (#593) -- **Image editor repairs**: rotate, flip, resize, levels, curves, filters and layer lock. (#597) -- **Sign PDF showed a blank canvas** instead of reporting why a PDF failed to load. (#545) -- **The file library recorded 0x0 dimensions** and skipped previews for HEIC, RAW and PSD uploads. (#636, #637) -- **Installing more than one AI bundle** left the shared virtualenv multi-versioned and quietly broke three tools. (#649) -- **Converting to JXL at quality 1 through 4 returned a 500**, and a missing ffmpeg was reported to you as a corrupt upload. (#649) -- **A transient Postgres outage stranded finished jobs**, leaving output on disk with no row pointing at it. A reconciler now adopts that work instead of dropping it. (#649) -- **A Redis endpoint that changed address wedged every consumer** while health checks still answered 200. (#649) -- **Website and docs fixes**: localized links no longer drop `#` fragments or lowercase `zh-CN`, the docs nav stays inside the viewport on tablets, and neither site calls the GitHub API from your browser any more. (#516, #562, #570, #560) - -### Upgrade Notes - -Nothing to migrate, but two shipped defaults changed: - -- **`TRUST_PROXY` now defaults to `loopback,linklocal,uniquelocal`** instead of trusting every peer. Docker bridge and Compose networks sit inside that range, so most setups need no change. If your reverse proxy reaches SnapOtter from a public address, set `TRUST_PROXY` explicitly, or rate limiting and audit logs will attribute every request to the proxy. -- **`MAX_AI_JOBS_PER_USER` defaults to 5** in-flight single-file AI jobs per user. Batch and pipeline AI runs stay uncapped. - -New optional knobs: `DB_STARTUP_TIMEOUT_MS`, `SUBPROCESS_MEMORY_LIMIT_MB` (off by default) and `GIF_BG_MAX_FRAMES`. - -### Acknowledgements - -A good part of this release started as someone else's bug report. - -Code and contributions: - -- **@mvanhorn** ❤️: Rewrote the remove-background timeout failure into a message that says what to do about it, instead of a bare timeout. (#518, #494) -- **@EuanTop** ❤️: Restored the download action on PDF conversion preset pages by making the synchronous route return the standard tool-result contract. (#629, #623) -- **@harshjainnn** ❤️: Diagnosed the download that starts and never finishes, and proposed the socket-reset direction the fix was built on. (#617, #590) - -Security disclosures: - -- **李春来 (Chunlai Li)** ❤️ ([@laijunyue](https://github.com/laijunyue)): Privately disclosed the privilege escalation through a delegated `users:manage` role, with a full source-to-sink analysis and a working exploit chain. (GHSA-9xgh-95qh-2x7h, #616) -- **@Alpastx** ❤️ (Alpesh Bhagwatkar): Disclosed an authenticated IDOR on job cancellation, and a path traversal in library stored filenames reachable after a malicious 1.x SQLite import, with a working proof of concept for each. (#599, #600) - -Bug reports: - -- **@riz467** ❤️: Real iPhone HEIC files rejected at validation, with the root cause worked out in the report. (#622) -- **@coupej** ❤️: PDF preset pages hanging forever, and jpg-to-pdf failing as soon as a second file was added. Two distinct bugs, correctly separated. (#623, #627) -- **@linuxuser1** ❤️: `PROCESSING_TIMEOUT_S=0` documented as unlimited but capped at five minutes. (#630) -- **@bezibaerchen** ❤️: Convert Audio hid the sample-rate setting its own description promised. (#558) -- **@hell-toupee** ❤️: The accurate OCR bundle failing to install with a `libpaddle` error. (#505) -- **@Michael1260** ❤️ and **@And-CSH** ❤️: Confirmed that OCR install failure independently and established that manual extraction works while the in-app installer does not, which pinned the bug to the installer. (#505) -- **@TomErnst1972** ❤️: Enabling the MFA-required policy locking an admin out of their own instance. (#515) -- **@thokich** ❤️: Object erasing producing blurry, unusable fills, which drove both the HD inpainting rewrite and the optional high-quality bundle. (#141) -- **@Hennie-git** ❤️: A Proxmox LXC install restarting 76 times against a Postgres that was not yet accepting connections. (community-scripts/ProxmoxVE#15796) -- **MickLesk** ❤️ (Proxmox VE community-scripts): Triaged that report in real time and produced the diagnosis the startup fix was built on. (#537) - -Feature requests and feedback: - -- **@LECOQQ** ❤️: Asked to relax password complexity on a home-server install. (#136) -- **@killervette42** ❤️: Asked for an Unraid Community App, now published to the Unraid CA store. (#96) -- **@alienatedsec** ❤️: Pointed out the GPU-falls-back-to-CPU fix was buried in a closed issue and undiscoverable, which is why it is in the deployment docs now. (#490, #587) -- **@neilp316** ❤️: Confirmed the Blackwell GPU failure on an RTX 5060 and mapped a working CUDA 12.8 upgrade path. (#120) -- **@Roiki11** ❤️: Argued for shared storage with path references over HTTP file transfer for off-box AI compute. (#189) - -Thank you as well to the community members who reported these over Discord and email, whose names we did not record: the squircle crop request (#602), the image-to-PDF download that started and never finished (#604, #607), the Delete Pages tool getting stuck (#594), Arabic text missing from pdf-to-text (#603), and the diagnosis behind the ONNX GPU fallback (#490). - -[Full diff on GitHub](https://github.com/snapotter-hq/SnapOtter/compare/v2.1.0...v2.2.0) - ---- - ## v2.0.0 {#v2-0-0} SnapOtter 2.0 turns the image toolkit into a full file-manipulation suite: 200+ tools across five modalities (Image, Video, Audio, PDF, and Files), rebuilt on Postgres 17 and a Redis-backed job queue, with a one-command `docker run`. This is a major release; read Breaking changes before upgrading from 1.x.