mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
33f1f974f0414b66f627a19ee69d6c5bdda738c3
1968
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
33f1f974f0 | docs: add v2.0.0 changelog entry and refresh Docker Hub overview banner | ||
|
|
531ecca9c2 | ci(trivy): ignore npm-bundled sigstore CVE-2026-48815 (build-tool internal, not runtime-reachable) [skip ci] | ||
|
|
88a611d28b | docs(readme): merge 2.0 launch + migration callouts, add Discord note, simplify quick start [skip ci] | ||
|
|
1d21184cfe |
chore(release): 2.0.0 [skip ci]
# [2.0.0](https://github.com/snapotter-hq/snapotter/compare/v1.17.2...v2.0.0) (2026-07-07) * feat!: SnapOtter 2.0.0 ([v2.0.0 |
||
|
|
9d1bbe7beb | chore(release): stage 2.0.0 launch (README banner, release notes, resume publishing) [skip ci] | ||
|
|
fb96cf8743 |
feat(ai): add a Reset AI Environment admin feature for the upgrade gap (#459)
Uninstalling a bundle only deletes its downloaded model weights, never the
shared venv's site-packages, so self-hosters who already hit an AI bundle
conflict (e.g. the scipy ABI strand) have no clean self-service path via
uninstall+reinstall: reinstalling just overlays corrected files on top of
stale ones. Adds POST /api/v1/admin/features/reset, which wipes
/data/ai/{venv,models,pip-cache}, resets installed.json, and reseeds a real
working venv from the image's baked /opt/venv (extracted docker/reseed-ai-venv.sh,
now shared with entrypoint.sh's existing base-venv-upgrade bootstrap instead
of duplicating that logic) -- leaving an empty venv directory here would
make the very next install fail with "spawn .../python3 ENOENT", caught by
testing this live rather than assuming it. Ships with a matching Settings UI
section (inline confirm, same pattern as per-bundle uninstall) and strings
across all 21 locales.
Verified against a real snapotter/snapotter:1.17.2 image migrated to 2.0.0,
with real multi-GB bundles installed (background-removal + OCR): confirmed
the migrated instance's inherited python3.11 venv (2.0.0 itself uses 3.12)
still imports the fixed scipy/numpy/paddleocr correctly, then reset + real
reinstall + actual tool execution (remove-background, verified output image)
all worked end-to-end.
|
||
|
|
60d01ab2dd |
fix: release-acceptance QA follow-ups (upload crash, scipy ABI conflict, rate limit, OCR fallback) (#458)
* fix(api): prevent a crash when an over-limit upload stream has no consumer yet busboy's "limit" handler destroyed the file stream with an error but never attached its own error listener, relying entirely on whatever consumes part.file downstream to do so. On a fast enough connection (or a fully buffered body, e.g. Fastify inject()), busboy can process enough bytes to hit the size limit before the route handler's receiveUpload() call has attached its own stream listener, leaving the resulting "error" event with zero listeners -- which crashes the whole process by default in Node. Surfaced by tonight's FULL_MATRIX+FUZZ integration run (880 uncaught exceptions, all the same root cause). Reproduces deterministically in isolation; unrelated to this release's actual code delta (file untouched since PR #413, well before the baseline QA pass). Fix: attach a baseline no-op error listener the moment the stream is created, guaranteeing at least one listener always exists. EventEmitter delivers "error" to every registered listener, so the real consumer's own error handling is unaffected. * fix(ai-bundles): rebuild upscale-enhance and photo-restoration to reconcile scipy ABI upscale-enhance and photo-restoration both depend on codeformer-pip, whose transitive closure (basicsr -> realesrgan -> gfpgan) pulls in an unpinned scipy. Both bundles were last built ~June 18-19, before PR #437 added the manifest's `constraints` array (numpy==1.26.4, scipy==1.12.0, etc.) to pin exactly this kind of dependency during bundle builds. Only the ocr bundle was rebuilt after that fix landed. install_feature.py has no pip install step -- it's a raw tarfile extraction with no cross-bundle conflict resolution, so installing OCR alongside either stale bundle left three incompatible scipy versions' files mixed in the same site-packages directory (a compiled _rotation.*.so from one release next to Python files expecting a different release's API), breaking the `upscale` tool and OCR's higher-quality tiers with an ImportError. Rebuilt both bundles for amd64-gpu and arm64-cpu from the current manifest, verified scipy/scikit-learn/scikit-image/pandas all resolve to the pinned versions in the tarballs themselves, then verified end-to-end on real hardware (Mac arm64 CPU and ubuntu_gpu .248 RTX 4070): installing all affected bundles together now yields exactly one version of each constrained package, `upscale` produces correct output, and OCR's balanced/best tiers correctly use PaddleOCR-GPU instead of erroring out. Published the rebuilt tarballs to the public deepsafe/feature-bundles HuggingFace repo and updated this manifest's sha256/sizes to match. Also adds verify-bundle-compatibility.sh: verify-bundle.sh checks each bundle in isolation (a fresh venv per bundle), which is exactly why this shipped twice -- nothing ever checked that bundles built at different times agree once layered into the one shared venv real installs use. The new script installs every bundle for an arch into one venv and asserts each constrained package has exactly one, correct version. Known follow-up (not fixed here, needs separate discussion): uninstalling a bundle only removes its downloaded model weights, never the site-packages it added, so existing installations that already hit this bug have no clean self-service fix via uninstall+reinstall -- they need a full AI-venv wipe. * fix(docker): bake a real rate limit default for the all-in-one one-liner The documented single-container `docker run` install had RATE_LIMIT_PER_MIN=0 (effectively unlimited, ~50k/min) baked in, since only docker-compose.yml carried a hardened override. A self-hoster following the one-liner path got no meaningful throttling anywhere, including auth-adjacent routes with no dedicated per-route limit. Bakes a generous-but-real 1000/min default into the Dockerfile, raises both compose files' fallback to match so the two documented install paths converge on the same posture, and updates the Zod schema default plus docs that quoted the old value. * fix(api): boot log undercounted tool routes by the conversion-preset total The "Tool routes: N active" line logged before registerConversionPresets(app) ran, so it only ever reported the base 158 tools, 83 short of the real 241-tool total. Presets have to register after the base loop (they delegate to each base tool's own processV2), so the fix moves the log line to after that call and has registerConversionPresets return its count instead of reordering the dependency. * fix(ai): forward {info}/{warning} stderr JSON instead of dropping it The dispatcher stderr parser only recognized {ready} and {progress,stage} shaped JSON lines; anything else that parsed as valid JSON (like ocr.py's GPU-to-tesseract downgrade notice, an {"info": ...} line) matched neither branch and fell through silently, never reaching docker logs. Adds explicit {info}/{warning} handling that forwards to console.log/console.warn, same as the existing [prefix]-tagged non-JSON path. * fix(api): fall back to a lower OCR tier when PaddleOCR itself is unusable ocr.ts already retries lower quality tiers on a crashed dispatcher, but the condition only matched crash-style messages (segfault, exited unexpectedly). ocr.py's own ImportError/exception handlers already produce messages telling the caller to use a lower tier (e.g. on the scipy ABI conflict class of bug), but nothing ever acted on them, so a broken PaddleOCR hard-failed with 422 instead of degrading to Tesseract like ocr-pdf effectively does. Broadens the retry condition to also catch PaddleOCR-engine-unusable messages. Note: ocr-pdf's tesseract-only behavior turned out to be an unrelated, pre-existing, deliberate design choice (PaddleOCR segfaults on rasterized PDF pages on arm64), not a graceful-fallback mechanism to copy -- the two tools weren't actually solving the same problem, so this fixes ocr.ts's own gap rather than trying to mirror ocr-pdf. |
||
|
|
52169c7a36 |
test(landing): update stale homepage e2e assertions to current hero copy (#457)
Updates the homepage.spec.ts hero assertions (page title, headline, subtitle, trust badges) and the navbar contact link label to match the current landing copy. Verified 18/18 homepage e2e tests pass against the live landing. |
||
|
|
aaf39a1b72 |
feat(landing): one-command Docker quickstart + live-demo CTA in hero (#455)
Adds a click-to-copy Docker quickstart command and a small live-demo button to the landing hero, between the category cards and the stats. Removes the divider above the stats and tightens hero spacing so the stats stay above the fold. New HeroQuickstart.astro is a scoped copy-to-clipboard island (clipboard + execCommand fallback, aria-live, reduced-motion). |
||
|
|
2a899ef904 |
fix: remove duplicated 1.x migration callout in README (#456)
Two PRs (#448 and #454) independently added the same "Coming from 1.x?" callout, and the merge landed both copies back to back. |
||
|
|
865c7789bf |
Fix stale image-only copy and add 1.x-to-2.0 migration guide (#454)
* docs: add 1.x-to-2.0 migration guide and upgrade notice Adds MIGRATING.md with backup and upgrade steps, plus a short "coming from 1.x?" callout in README and the docs upgrade guide pointing existing users at it. * fix: replace stale image-only and pre-rename data copy across product SnapOtter grew from an image-only tool into a 5-modality suite (Image, Video, Audio, PDF, Files), but copy in several places never caught up. Fixes: - dropzone.defaultFormats (i18n): every non-English locale still had the pure pre-2.0 image-only format list; English omitted Files entirely. Corrected across all 21 locales. - settings.about.appDescription (i18n): "document, and data" workflow copy updated to "PDF, and file" across all 21 locales. - constants.ts: Files category's raw name was still "Data Files". - Landing hero subtitle, JSON-LD schema, llms.txt, and 7 spots in the competitor-comparison pages. - Docs: VitePress config, supported-formats, deployment, and an architecture.md modality-naming nit. - OpenAPI description, root package.json description/keyword, and a GitHub issue template dropdown option. DOCKERHUB.md's separate "v1.x, image tools only" pre-release notice is left untouched since 2.0 hasn't published to Docker Hub yet. * test: update dropzone format-hint assertion to match corrected copy The expected string still had the stale image-only/duplicated PDF-Documents text from before the dropzone.defaultFormats fix. |
||
|
|
072dff6c6c |
chore(deps): bump wavesurfer.js in the production-deps group (#452)
Bumps the production-deps group with 1 update: [wavesurfer.js](https://github.com/katspaugh/wavesurfer.js). Updates `wavesurfer.js` from 7.12.9 to 7.12.10 - [Release notes](https://github.com/katspaugh/wavesurfer.js/releases) - [Commits](https://github.com/katspaugh/wavesurfer.js/compare/7.12.9...7.12.10) --- updated-dependencies: - dependency-name: wavesurfer.js dependency-version: 7.12.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production-deps ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
44e1f90e4a |
feat(i18n): raise translation coverage across all 20 locales (round 2) (#453)
An audit comparing every locale's leaf values against en.ts found structural key-parity was already perfect (0 missing/extra keys), but ~2,356 leaf values across the 20 non-English locales were still byte-identical to the English source, meaning they'd shipped untranslated. The largest single cause: the feedback widget (PR #428) shipped with English placeholder text in every locale except Italian, which had a separate community translation (#425/#426). Translates the 1,289 strings that were genuinely missing translations, locale by locale, matching each file's own established register, terminology, and loanword conventions (verified against already-translated sibling strings rather than assumed). Leaves the remaining ~1,067 flagged values untouched where they're legitimately identical to English: brand names, format/protocol acronyms, literal URLs, hex colors, and terms this project already treats as loanwords in that language. Verified against current main: pnpm typecheck 0 errors (9/9 workspaces), i18n-parity/i18n-locale/tool-i18n/template-i18n 45/45 passing (23/23 cross-locale parity), biome check clean. |
||
|
|
0fafeb2676 |
fix(i18n): complete the Italian translation update (#438) (#450)
Lands albanobattistella's Italian translation update from #438, with one duplicate key corrected so it passes CI. Their PR retranslated several terms in it.ts, but a stray edit left watermark-video with two `submit` keys (the second, "Applica spaziatura interna", was unrelated leftover text). Removed the duplicate so `submit` stays "Applica filigrana", consistent with the other watermark strings in the same diff. Original translation by albanobattistella in #438; duplicate-key fix by the maintainer. Claude-Session: https://claude.ai/code/session_018tNg52r7b3RMEeybv5LHCX Co-authored-by: albanobattistella <34811668+albanobattistella@users.noreply.github.com> |
||
|
|
b28c9dd2da |
chore(deps): pin nvidia/cuda base to 12.6.x (#449)
Dependabot proposed nvidia/cuda 12.6.3 to 12.9.2 (#441), a minor bump the existing semver-major ignore did not catch. The 12.6 base is deliberate: the AI bundle wheels are cu126, libcublas-12-6 matches, and a 12.9 base requires driver R575+ so the container fails to start on common 570.x production drivers (the regression PR #334 fixed). Ignore nvidia/cuda >= 12.7 so 12.6.x patch bumps still flow while 12.7+ is held back. |
||
|
|
d6ae79bebb |
chore(deps): bump posthog-js (#447)
Bumps the production-deps group with 1 update in the / directory: [posthog-js](https://github.com/PostHog/posthog-js). Updates `posthog-js` from 1.396.7 to 1.396.8 - [Release notes](https://github.com/PostHog/posthog-js/releases) - [Changelog](https://github.com/PostHog/posthog-js/blob/main/CHANGELOG.md) - [Commits](https://github.com/PostHog/posthog-js/compare/posthog-js@1.396.7...posthog-js@1.396.8) --- updated-dependencies: - dependency-name: posthog-js dependency-version: 1.396.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production-deps ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
e09feadd68 |
docs: add 1.x to 2.0 migration guide pointer and community note (#448)
Root MIGRATING.md pointer + README upgrading link + 1.x community note across README/MIGRATING/docs; drop stale v2.0.0 callout. Claude-Session: https://claude.ai/code/session_01P22pYJttXCe5cbCzRyfZac |
||
|
|
d019217969 | chore(deps): bump the production-deps group | ||
|
|
36dde9ad87 |
fix(ai): gate AI tools on per-framework GPU detection, not a shared boolean (#445)
gpu_available() answers "can ANY framework use a GPU" (torch, then ONNX, then
paddle). But torch tools consumed that shared boolean directly as
device = torch.device("cuda" if gpu_available() else "cpu"). On a GPU host where
gpu_available() is True via paddle or ONNX while torch is a CPU-only build, those
tools would route to a CUDA torch cannot use and crash. Transcription had the
mirror problem: it runs on CTranslate2 (not torch), so on a transcription-only
GPU box gpu_available() returned False and Whisper ran on CPU despite a GPU.
Add per-framework helpers to gpu.py:
- torch_gpu_available(): torch.cuda.is_available(), honoring SNAPOTTER_GPU.
- ctranslate2_gpu_available(): ctranslate2.get_cuda_device_count() > 0.
Point each tool at the helper for its own framework: upscale, noise_removal,
enhance_faces and restore use torch_gpu_available(); transcribe uses
ctranslate2_gpu_available(). ocr.py keeps gpu_available() (paddle-aware) and the
dispatcher keeps it for its startup GPU-status line. The SNAPOTTER_GPU override
check is factored into a shared _override_disables_gpu() helper.
TDD: 7 new tests in tests/test_gpu_detection.py cover both helpers (override,
CPU-only, absent framework), including the crux that torch_gpu_available() stays
False on a CPU-only torch build even when a GPU exists for another framework.
Claude-Session: https://claude.ai/code/session_01NfaRxjek8ex5nawvx3mVMf
|
||
|
|
bc59114dcb |
chore(deps-dev): bump the dev-deps group with 9 updates (#444)
Bumps the dev-deps group with 9 updates: | Package | From | To | | --- | --- | --- | | [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) | `2.5.1` | `2.5.2` | | [@semantic-release/github](https://github.com/semantic-release/github) | `12.0.8` | `12.0.9` | | [@testcontainers/postgresql](https://github.com/testcontainers/testcontainers-node) | `12.0.3` | `12.0.4` | | [@testcontainers/redis](https://github.com/testcontainers/testcontainers-node) | `12.0.3` | `12.0.4` | | [adm-zip](https://github.com/cthackers/adm-zip) | `0.5.17` | `0.5.18` | | [turbo](https://github.com/vercel/turborepo) | `2.10.0` | `2.10.3` | | [@tailwindcss/vite](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-vite) | `4.3.1` | `4.3.2` | | [tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss) | `4.3.1` | `4.3.2` | | [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `8.0.16` | `8.1.3` | Updates `@biomejs/biome` from 2.5.1 to 2.5.2 - [Release notes](https://github.com/biomejs/biome/releases) - [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md) - [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.2/packages/@biomejs/biome) Updates `@semantic-release/github` from 12.0.8 to 12.0.9 - [Release notes](https://github.com/semantic-release/github/releases) - [Commits](https://github.com/semantic-release/github/compare/v12.0.8...v12.0.9) Updates `@testcontainers/postgresql` from 12.0.3 to 12.0.4 - [Release notes](https://github.com/testcontainers/testcontainers-node/releases) - [Commits](https://github.com/testcontainers/testcontainers-node/compare/v12.0.3...v12.0.4) Updates `@testcontainers/redis` from 12.0.3 to 12.0.4 - [Release notes](https://github.com/testcontainers/testcontainers-node/releases) - [Commits](https://github.com/testcontainers/testcontainers-node/compare/v12.0.3...v12.0.4) Updates `adm-zip` from 0.5.17 to 0.5.18 - [Release notes](https://github.com/cthackers/adm-zip/releases) - [Changelog](https://github.com/cthackers/adm-zip/blob/master/history.md) - [Commits](https://github.com/cthackers/adm-zip/compare/v0.5.17...v0.5.18) Updates `turbo` from 2.10.0 to 2.10.3 - [Release notes](https://github.com/vercel/turborepo/releases) - [Changelog](https://github.com/vercel/turborepo/blob/main/RELEASE.md) - [Commits](https://github.com/vercel/turborepo/compare/v2.10.0...v2.10.3) Updates `@tailwindcss/vite` from 4.3.1 to 4.3.2 - [Release notes](https://github.com/tailwindlabs/tailwindcss/releases) - [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.2/packages/@tailwindcss-vite) Updates `tailwindcss` from 4.3.1 to 4.3.2 - [Release notes](https://github.com/tailwindlabs/tailwindcss/releases) - [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.2/packages/tailwindcss) Updates `vite` from 8.0.16 to 8.1.3 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.1.3/packages/vite) --- updated-dependencies: - dependency-name: "@biomejs/biome" dependency-version: 2.5.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-deps - dependency-name: "@semantic-release/github" dependency-version: 12.0.9 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-deps - dependency-name: "@testcontainers/postgresql" dependency-version: 12.0.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-deps - dependency-name: "@testcontainers/redis" dependency-version: 12.0.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-deps - dependency-name: adm-zip dependency-version: 0.5.18 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-deps - dependency-name: turbo dependency-version: 2.10.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-deps - dependency-name: "@tailwindcss/vite" dependency-version: 4.3.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-deps - dependency-name: tailwindcss dependency-version: 4.3.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-deps - dependency-name: vite dependency-version: 8.1.3 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-deps ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
eb40e68274 |
chore(deps): bump the actions group with 8 updates (#442)
Bumps the actions group with 8 updates: | Package | From | To | | --- | --- | --- | | [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) | `4.1.0` | `4.2.0` | | [docker/login-action](https://github.com/docker/login-action) | `4.2.0` | `4.4.0` | | [github/codeql-action/init](https://github.com/github/codeql-action) | `4.36.2` | `4.36.3` | | [github/codeql-action/autobuild](https://github.com/github/codeql-action) | `4.36.2` | `4.36.3` | | [github/codeql-action/analyze](https://github.com/github/codeql-action) | `4.36.2` | `4.36.3` | | [docker/metadata-action](https://github.com/docker/metadata-action) | `6.1.0` | `6.2.0` | | [docker/build-push-action](https://github.com/docker/build-push-action) | `7.2.0` | `7.3.0` | | [github/codeql-action/upload-sarif](https://github.com/github/codeql-action) | `4.36.2` | `4.36.3` | Updates `docker/setup-buildx-action` from 4.1.0 to 4.2.0 - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5...bb05f3f5519dd87d3ba754cc423b652a5edd6d2c) Updates `docker/login-action` from 4.2.0 to 4.4.0 - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/650006c6eb7dba73a995cc03b0b2d7f5ca915bee...af1e73f918a031802d376d3c8bbc3fe56130a9b0) Updates `github/codeql-action/init` from 4.36.2 to 4.36.3 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/8aad20d150bbac5944a9f9d289da16a4b0d87c1e...54f647b7e1bb85c95cddabcd46b0c578ec92bc1a) Updates `github/codeql-action/autobuild` from 4.36.2 to 4.36.3 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/8aad20d150bbac5944a9f9d289da16a4b0d87c1e...54f647b7e1bb85c95cddabcd46b0c578ec92bc1a) Updates `github/codeql-action/analyze` from 4.36.2 to 4.36.3 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/8aad20d150bbac5944a9f9d289da16a4b0d87c1e...54f647b7e1bb85c95cddabcd46b0c578ec92bc1a) Updates `docker/metadata-action` from 6.1.0 to 6.2.0 - [Release notes](https://github.com/docker/metadata-action/releases) - [Commits](https://github.com/docker/metadata-action/compare/80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9...dc802804100637a589fabce1cb79ff13a1411302) Updates `docker/build-push-action` from 7.2.0 to 7.3.0 - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/f9f3042f7e2789586610d6e8b85c8f03e5195baf...53b7df96c91f9c12dcc8a07bcb9ccacbed38856a) Updates `github/codeql-action/upload-sarif` from 4.36.2 to 4.36.3 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/8aad20d150bbac5944a9f9d289da16a4b0d87c1e...54f647b7e1bb85c95cddabcd46b0c578ec92bc1a) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-version: 4.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: docker/login-action dependency-version: 4.4.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: github/codeql-action/init dependency-version: 4.36.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions - dependency-name: github/codeql-action/autobuild dependency-version: 4.36.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions - dependency-name: github/codeql-action/analyze dependency-version: 4.36.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions - dependency-name: docker/metadata-action dependency-version: 6.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: docker/build-push-action dependency-version: 7.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: github/codeql-action/upload-sarif dependency-version: 4.36.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
3aaaacc7a1 |
feat: pin frequently-used tools to the top of the dashboard (#440)
* feat(i18n): add pin/unpin/pinned strings, retire addToFavourites stub Claude-Session: https://claude.ai/code/session_01Ad5LjCDJyW1tLFd3P4Hedp * feat(web): add per-user pinned-tools store Claude-Session: https://claude.ai/code/session_01Ad5LjCDJyW1tLFd3P4Hedp * feat(web): add opt-in pin toggle to ToolCard Claude-Session: https://claude.ai/code/session_01Ad5LjCDJyW1tLFd3P4Hedp * feat(web): render Pinned section on the dashboard All tab Claude-Session: https://claude.ai/code/session_01Ad5LjCDJyW1tLFd3P4Hedp * test(web): cover pin toggle (component) and dashboard pin flow (e2e) Claude-Session: https://claude.ai/code/session_01Ad5LjCDJyW1tLFd3P4Hedp |
||
|
|
ec78d36d95 | docs: merge api sync documentation branch | ||
|
|
6e9933446e | docs: sync api documentation | ||
|
|
b69c7362e1 | docs: sync api documentation | ||
|
|
7cd514dd8c |
fix(ai): detect a paddle-only GPU so OCR uses PaddleOCR-GPU not Tesseract (#439)
gpu_available() probed torch, then ONNX Runtime, then nvidia-smi, but never paddle. The OCR bundle ships paddlepaddle-gpu with no torch or ONNX, so on an OCR-only GPU host every probe missed the GPU: nvidia-smi saw it but returned False by design, and OCR silently fell back to Tesseract (CPU, lower quality) with no signal why. Add a paddle probe as the last resort in gpu_available(). It runs only after nvidia-smi confirms a GPU is physically present, and in an isolated subprocess, because importing paddlepaddle-gpu on a GPU-less host segfaults and would wedge the shared AI dispatcher. It returns True only when paddle reports both a CUDA build and a visible device, signalling the result through the exit code so paddle's own import chatter on stdout cannot corrupt the reading. CPU-only and torch/ONNX GPU hosts are unaffected: the probe never runs on the former (nvidia-smi finds nothing) and is never reached on the latter (the torch step already returns True first). Claude-Session: https://claude.ai/code/session_01NfaRxjek8ex5nawvx3mVMf |
||
|
|
8574615ef5 |
docs(deployment): refresh hardware requirements from the 2.0 benchmark (#436)
Update the Hardware Requirements section with fresh six-machine benchmark data: - Tiers corrected to the resource-sweep floor (2c/2G minimum; 512MB cannot start, 1GB is single-file-only, batches need 2GB). - Add the 64-bit-only architecture requirement (Pi 4/5 yes; 32-bit ARM and 512MB boards no). - Correct the AI-on-CPU viability (colorize/face-enhance are ~10s and usable, not "marginal to no") and add the AI RAM lever (~360MB idle without bundles vs ~2.6GB with all seven installed). - Fix bogus GPU speedups (noise-removal/blur-faces are CPU-bound, ~1x, not 13,400x/100x); real wins are upscale ~47x, face-enhance ~12x, transcribe ~4.5x, remove-bg ~4x; photo-restoration is CPU-bound even on a GPU. - Call out video transcode as the one CPU-heavy tool; refresh concurrency numbers. Claude-Session: https://claude.ai/code/session_01UvVCMNUBrgpghk8gye5gav |
||
|
|
fd39f66f46 |
fix(ai-bundles): lock the numpy-1.x ABI closure so the OCR bundle can't strand scipy (#437)
* fix(ai-bundles): lock the numpy-1.x ABI closure so the OCR bundle can't strand scipy The OCR bundle installs paddleocr[doc-parser] 3.4, whose dependency closure drags numpy 1.26.4 up to 2.5.1 and pulls scipy/scikit-learn/pandas wheels built against the numpy 2.x ABI. build-bundle.sh re-pinned only numpy (basePackages), so those numpy-2.x wheels stayed behind; the by-dir-name site-packages diff then shipped them, and once merged onto the numpy==1.26.4 base they raise "numpy.dtype size changed" on import. Because the dispatcher pre-imports every ML library at startup and disables all AI after 5 crashes in 60s, one stranded scipy takes down every AI tool, not just OCR (observed on a CPU host: remove-background worked before the OCR bundle and broke after). All-7 installs escaped it through last-writer-wins ordering; a subset install did not, which is why it surfaced only intermittently. Fix: add a manifest "constraints" list (numpy, scipy, scikit-learn, scikit-image, pandas pinned to numpy-1.x-ABI versions) and apply it via PIP_CONSTRAINT to every bundle pip install, so no bundle can pull a numpy-2.x wheel. paddleocr 3.4.1 still resolves cleanly under the lock and the pinned stack imports without ABI error on numpy 1.26.4 (validated on py3.12). Also import scipy/sklearn in the OCR path of verify-bundle.sh so CI catches this class in isolation, and add a manifest regression test. Note: the published bundles must be rebuilt and republished (ai-bundles.yml) for this to reach already-installed bases. Claude-Session: https://claude.ai/code/session_01UvVCMNUBrgpghk8gye5gav * chore(ai-bundles): sync OCR manifest sha256 to the rebuilt numpy-1.x bundles Rebuilt the OCR bundle for both arches with the numpy-1.x-ABI constraints from this PR and republished the tars to deepsafe/feature-bundles/v2.0.0, then updated the baked manifest sha256 and sizes so installs verify against the fixed archives: amd64-gpu 5.93 GB sha 2a00a3184f6a635f1fa9ae2a6517ad740a11f9e5ff58c098d2fd369a2bb1e16b arm64-cpu 1.98 GB sha 6868c264069dcb74c6675c0b1f58dc1c9f60d9aa4459725e3dbde07a99a6a09a Both tars ship scipy 1.12.0 / scikit-learn 1.4.2 / pandas 2.2.2 (numpy-1.x-ABI) and zero numpy-2.x wheels, verified by listing the archive contents. Stopgap note: these tars were built against the ghcr.io latest base (the 2.0.0 image is not published to GHCR), so they are not byte-identical to what the CI build will produce. When ai-bundles.yml rebuilds at the 2.0.0 release, it will mint fresh sha256 values and this manifest must be re-synced to them. Claude-Session: https://claude.ai/code/session_01UvVCMNUBrgpghk8gye5gav |
||
|
|
48494afc1e |
chore(ai-bundles): sync OCR manifest sha256 to the rebuilt numpy-1.x bundles
Rebuilt the OCR bundle for both arches with the numpy-1.x-ABI constraints from this PR and republished the tars to deepsafe/feature-bundles/v2.0.0, then updated the baked manifest sha256 and sizes so installs verify against the fixed archives: amd64-gpu 5.93 GB sha 2a00a3184f6a635f1fa9ae2a6517ad740a11f9e5ff58c098d2fd369a2bb1e16b arm64-cpu 1.98 GB sha 6868c264069dcb74c6675c0b1f58dc1c9f60d9aa4459725e3dbde07a99a6a09a Both tars ship scipy 1.12.0 / scikit-learn 1.4.2 / pandas 2.2.2 (numpy-1.x-ABI) and zero numpy-2.x wheels, verified by listing the archive contents. Stopgap note: these tars were built against the ghcr.io latest base (the 2.0.0 image is not published to GHCR), so they are not byte-identical to what the CI build will produce. When ai-bundles.yml rebuilds at the 2.0.0 release, it will mint fresh sha256 values and this manifest must be re-synced to them. Claude-Session: https://claude.ai/code/session_01UvVCMNUBrgpghk8gye5gav |
||
|
|
67c55669d6 |
fix(ai-bundles): lock the numpy-1.x ABI closure so the OCR bundle can't strand scipy
The OCR bundle installs paddleocr[doc-parser] 3.4, whose dependency closure drags numpy 1.26.4 up to 2.5.1 and pulls scipy/scikit-learn/pandas wheels built against the numpy 2.x ABI. build-bundle.sh re-pinned only numpy (basePackages), so those numpy-2.x wheels stayed behind; the by-dir-name site-packages diff then shipped them, and once merged onto the numpy==1.26.4 base they raise "numpy.dtype size changed" on import. Because the dispatcher pre-imports every ML library at startup and disables all AI after 5 crashes in 60s, one stranded scipy takes down every AI tool, not just OCR (observed on a CPU host: remove-background worked before the OCR bundle and broke after). All-7 installs escaped it through last-writer-wins ordering; a subset install did not, which is why it surfaced only intermittently. Fix: add a manifest "constraints" list (numpy, scipy, scikit-learn, scikit-image, pandas pinned to numpy-1.x-ABI versions) and apply it via PIP_CONSTRAINT to every bundle pip install, so no bundle can pull a numpy-2.x wheel. paddleocr 3.4.1 still resolves cleanly under the lock and the pinned stack imports without ABI error on numpy 1.26.4 (validated on py3.12). Also import scipy/sklearn in the OCR path of verify-bundle.sh so CI catches this class in isolation, and add a manifest regression test. Note: the published bundles must be rebuilt and republished (ai-bundles.yml) for this to reach already-installed bases. Claude-Session: https://claude.ai/code/session_01UvVCMNUBrgpghk8gye5gav |
||
|
|
47a60e7fad |
fix(files): record the source tool in toolChain on Save to Files (#435)
Save to Files posted only the blob, so userFiles.toolChain stayed null and the library showed "Tools Used: None". Thread the producing toolId through /api/v1/files/upload (validated optional field) and store it as a one-element toolChain, matching the pipeline path. Claude-Session: https://claude.ai/code/session_01UvVCMNUBrgpghk8gye5gav |
||
|
|
cf884b52cd |
fix: offline CodeFormer face-enhance (ship RealESRGAN_x2plus in upscale-enhance bundle) (#433)
* fix: ship RealESRGAN_x2plus.pth in the upscale-enhance bundle for offline CodeFormer codeformer-pip 0.0.4 downloads RealESRGAN_x2plus.pth at import of codeformer.app, unconditionally, even though enhance_faces calls inference_app with background_enhance=False and never uses the background upsampler. The weight was not bundled, so explicit CodeFormer face-enhance (enhance-faces model=codeformer) failed in strict offline mode (SNAPOTTER_ALLOW_MODEL_DOWNLOAD=0) on a host that had never cached it -- the guard raised before the import could complete. Add RealESRGAN_x2plus.pth to the upscale-enhance bundle manifest (only that bundle uses codeformer-pip; photo-restoration uses the CodeFormer ONNX path) and link it in prepare_codeformer_weights alongside the other three weights, replacing the download-or-error guard. Once the bundle ships it, the import resolves offline and strict mode works. Archive SHA256s updated in a follow-up once the bundle is rebuilt. Claude-Session: https://claude.ai/code/session_01XGB4pGvTvb7sUX4JN745U7 * fix: require face-detection bundle for enhance-faces + point manifest at the x2plus archives enhance-faces runs MediaPipe face detection (blaze_face_short_range.tflite) before CodeFormer/GFPGAN. That model ships in the face-detection bundle, not the tool's primary upscale-enhance bundle, so a standalone upscale-enhance install failed face detection (offline: hard error; online: a surprise download) before reaching the codeformer path. Declare the dependency in TOOL_EXTRA_BUNDLES like passport-photo does. Update the upscale-enhance archive SHA256/sizes to the rebuilt bundles that include RealESRGAN_x2plus.pth (amd64-gpu + arm64-cpu), verified to install and run enhance-faces model=codeformer in strict offline mode with zero downloads. Claude-Session: https://claude.ai/code/session_01XGB4pGvTvb7sUX4JN745U7 |
||
|
|
dadf766899 |
fix(migrator): correct and harden the 1.x to 2.0 SQLite import (#434)
* feat(api): parse DATA_DIR from env for 1.x import auto-detection Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w * test(migrator): build 1.17.2 fixtures by replaying legacy migrations Discovered the legacy migrations seed a Default team (0005) and builtin roles (0007), so the replayed fixture carries them. Seed uses a distinct custom team. Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w * fix(migrator): self-adjusting column copy, jobs.status map, drop sessions, advisory lock The importer now inserts only the intersection of source and live target columns, so the three analytics_* columns 2.x dropped no longer break the first users INSERT (and future dropped columns are handled generically). jobs.status is mapped onto the 2.x enum (error->failed). Sessions are no longer migrated. A pg_advisory_xact_lock serializes concurrent replicas. Includes login-after-migrate and library assertions. Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w * test(migrator): CI drift guard fails when a required column is unfillable from 1.17.2 Introspects every NOT-NULL-no-default column of each migrated table in the current schema and asserts the engine can fill it from a real 1.17.2 source. Turns a future breaking schema change into a PR-time failure instead of a production import break. Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w * feat(migrator): orchestrator with detection, boot states, marker, blob count sqlite-import.ts owns source resolution (explicit path, 'off' sentinel, DATA_DIR probe), the four boot states (import/leftover/locked/none), the persisted sqlite_import marker, and a read-only library-blob count. runBootImport wires them together and catches TargetNonEmptyError as a benign multi-replica skip. Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w * feat(api): route boot through the 1.x import orchestrator; hide marker from non-admins index.ts now calls runBootImport (which owns detection + the four boot states) instead of the inline SQLITE_MIGRATE_PATH block. The sqlite_import marker is added to SENSITIVE_KEYS (but not REDACTED_KEYS) so admins see the counts for the banner while non-admins don't see the key at all. Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w * feat(migrator): add analyzeSqlite + dry-run/verify CLI analyzeSqlite is a read-only pre-flight (no live Postgres): per-table row counts, library-blob presence, and out-of-enum job statuses. The migrate:sqlite CLI now lives in the orchestrator and supports --dry-run/--verify (prints the analysis and exits without writing) alongside the existing import and --force. Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w * docs: add 1.x to 2.0 upgrade guide; fix volume-name casing New apps/docs upgrade guide covering auto-detect, the SQLITE_MIGRATE_PATH override + off opt-out, the dry-run, what carries over, locked-state recovery, and non-destructive rollback. Leads with 'back up the WHOLE /data volume, not just snapotter.db' because 1.x WAL mode leaves data in snapotter.db-wal (surfaced by the real-image upgrade test). Standardizes README/DOCKERHUB compose volume names on the canonical SnapOtter-data casing so they match the repo compose and don't orphan an upgrader's volume. Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w * feat(web): admin 1.x migration banner + 21-locale strings A one-time admin banner reads the sqlite_import marker from /v1/settings and shows the import result (user + saved-file counts) on success, or a warning when a 1.x database was found but not imported. Dismissal persists to a sqlite_import.dismissedAt settings key. shouldShowMigrationBanner/parseMigrationMarker sit in feedback.ts with the other shouldShow helpers; strings added to en.ts and all 20 other locales. Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w * style(landing): biome-format Hero.astro trustBadges array Pre-existing formatting drift on main (its Lint check was skipped on the merge that introduced it); this PR's full Lint run surfaced it. Formatting-only, applied via the repo's own biome formatter to unblock the required Lint check. Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w |
||
|
|
dc589fd0a1 |
docs(readme): drop Otter.ai from the alternatives list (#432)
Otter.ai is a transcription-only service and reads as off-topic next to the file-conversion tools in the comparison line. Remove it and fix the list conjunction. Claude-Session: https://claude.ai/code/session_01EhgRGhvhNGJVQHcdFdcNMa |
||
|
|
8451f9be08 |
feat(landing): refocus hero on privacy-sensitive teams (#431)
Retitle the homepage hero to "File processing for privacy-sensitive teams", drop the redundant infrastructure subline, and trim the trust badges to the four foundational signals (self-hosted, open source, air-gap capable, compliance-friendly). Align the homepage title and meta description to the new positioning, overriding the site-wide defaults for the homepage only. Claude-Session: https://claude.ai/code/session_01EhgRGhvhNGJVQHcdFdcNMa |
||
|
|
8cdd85a493 |
feat(feedback): route failed-run Report issue through the offline handoff (#429)
Apply the always-on handoff to the failed-run Report issue button too. Un-gate the two buttons in tool-page.tsx from the analytics toggle, and extend the dialog offline handoff to source=failed_job, prefilling the GitHub issue with the tool id and error category so it is actionable even with an empty message. Follows #428. Claude-Session: https://claude.ai/code/session_01XVrHKXwzZDWBWgkGQdPZ3A |
||
|
|
e0dbf2a5c3 |
fix: set a writable HOME for the app user so PaddleOCR works in non-root deployments (#430)
The container dropped privileges to the non-root snapotter user via gosu
(external) and s6-setuidgid (embedded), both of which preserve the
environment without setting HOME. The app therefore kept root's HOME=/root,
which is not writable by snapotter, and PaddleOCR died with
PermissionError: '/root/.paddlex/temp' -- breaking the ocr tool at default
quality in every non-root deployment. Prior GPU QA ran the app as root, which
masked it.
Fix: export HOME=/data/.home (persistent, writable, hidden) at every
privilege-drop point:
- entrypoint.sh external gosu path and non-root tini path (the latter uses
$DD/.home so a DATA_DIR override stays consistent).
- the s6 snapotter/run service (scoped there, not globally before /init, so
postgres/redis do not inherit a snapotter-owned HOME).
The root preflight creates /data/.home and the existing chown sweep owns it as
the PUID/PGID-remapped snapotter; the dir is added to both ensure_writable
probes so an unwritable HOME fails fast with the storage-permission guidance
instead of crashing late. The Dockerfile passwd home moves from /app
(read-only) to /data/.home as the getpwuid fallback when HOME is unset.
Because bridge.ts forwards HOME to the Python sidecar, this also repairs the
expanduser("~") caches in inpaint/outpaint/restore/noise_removal/remove_bg,
not just PaddleOCR.
Also fixes a test-harness inconsistency: tool-default-settings passport-photo
countryCode "us" -> "US" (the route exact-matches uppercase PASSPORT_SPECS
codes; the UI already sends "US", so users were never affected).
Claude-Session: https://claude.ai/code/session_01XGB4pGvTvb7sUX4JN745U7
|
||
|
|
5dcc06a99e |
feat(feedback): always-on nav button with GitHub/email handoff when analytics is off (#428)
Keep the top-nav feedback button always visible (icon plus label on desktop, icon-only on mobile) instead of hiding it when an instance opts out of analytics. When analytics is off, the dialog keeps the typed message and hands off to a prefilled GitHub issue plus a contact@snapotter.com email, with no fake Thanks. Adds a feedback.yml issue template, URL builders, and feedback strings across all 21 locales. Claude-Session: https://claude.ai/code/session_01XVrHKXwzZDWBWgkGQdPZ3A |
||
|
|
7b04317ed2 |
feat: add a Keep it free sponsor button to the top nav (#427)
Adds a prominent Keep it free sponsor button to the top nav, linking to https://github.com/sponsors/snapotter-hq. Solid orange pill on desktop (left of the avatar), orange heart icon on mobile. Opens in a new tab with rel=noopener noreferrer, so no referrer or user data leaks, and it adds no passive network activity (offline-mode compatible). Fires an opt-in, property-less sponsor_clicked analytics event. Adds sidebar.sponsor and a11y.sponsorLink across all 21 locales. Claude-Session: https://claude.ai/code/session_01DnYLLA5z4Uf1GDeEPENVgr |
||
|
|
3ae48cc76a |
fix(i18n): complete the Italian feedback translations (#426)
Lands albanobattistella's Italian translation of the feedback strings from #425, with two mistyped keys corrected (great and adminCardDescription). Verified against main: typecheck 0 errors, i18n parity 23/23, Biome clean. Co-authored-by: albanobattistella <34811668+albanobattistella@users.noreply.github.com> |
||
|
|
23efce9df0 |
fix(analytics): harden analytics opt-out and feedback surfaces (#423)
Server stops phoning Sentry home after opt-out (release-health sessions + client reports off); settings saves diff-send only changed keys so a stale tab cannot revert an instance-wide opt-out; disabling analytics hides the feedback UI immediately; optIn resumes PostHog after re-enable; onboarding survey writes time out at 15s; inline tool-feedback prompt arms a shown-cooldown. |
||
|
|
6e3a14ec6b |
fix: remove automatic third-party egress of user data + optional strict offline mode (OSM tiles, Scalar fonts, editor fonts, AI model downloads) (#422)
* fix: remove all automatic third-party egress (OSM tiles, Scalar fonts, editor Google Fonts, AI model download fallbacks) Phone-home audit follow-up. The product no longer makes any automatic third-party request; user-initiated click-outs stay, and production now fails closed on missing AI models. 1. GPS leak via OSM tiles: the strip-metadata panel auto-loaded tile.openstreetmap.org tiles encoding the photo's GPS position. The Leaflet mini-map is gone; coordinates render as text plus an explicit View on map link (openstreetmap.org, opens on click only). Removed tile.openstreetmap.org from the CSP img-src, dropped the leaflet dependency, added the viewOnMap i18n key to all 21 locales. 2. Scalar docs fonts: /api/docs loaded Inter and JetBrains Mono from fonts.scalar.com. Scalar now renders with withDefaultFonts: false and both --scalar-font and --scalar-font-code pinned to system stacks; fonts.scalar.com removed from the docs CSP font-src. Verified by injecting GET /api/docs/: config carries withDefaultFonts false and the served page has no fonts.scalar.com reference. 3. Editor Google Fonts: the editor font picker built fonts.googleapis.com stylesheet URLs for 25 web fonts the served CSP already blocked. The remote loading path is deleted; the picker now offers system fonts only, with a SELF_HOSTED_FONTS seam (FontFace API, same origin) for bundling fonts later. Unknown families saved in old documents fall back to the browser default. 4. Python sidecar fails closed on model downloads: new packages/ai/python/offline_guard.py gates every runtime download fallback (inpaint, outpaint, restore, noise_removal, detect_faces, enhance_faces, face_landmarks, red_eye_removal, remove_bg, ocr, transcribe, upscale) behind SNAPOTTER_ALLOW_MODEL_DOWNLOAD=1 with an actionable error. Bundled models keep working untouched. 5. OCR and transcription library-internal downloads: unbundled PaddleOCR language and detection fallbacks now raise the guard error naming the language instead of resolving models over the network; faster-whisper gets local_files_only when downloads are off. 6. GFPGAN and CodeFormer cwd-relative weights: facexlib and codeformer-pip resolve helper weights relative to the process cwd and fetch them from GitHub when absent. They are now symlinked from the installed bundle files under MODELS_PATH/gfpgan/facelib before the libraries load, failing closed when unresolvable. Defense in depth: HF_HUB_OFFLINE=1 and TRANSFORMERS_OFFLINE=1 are set in the runtime image and in the sidecar spawn env; install_feature.py lifts them for user-initiated bundle installs and restores them afterwards (it can run in-process inside the dispatcher). SNAPOTTER_ALLOW_MODEL_DOWNLOAD is documented in .env.example, default off. Validation: typecheck 9/9 workspaces, Biome clean on touched files, 5178 unit tests pass, py_compile on all touched scripts, guard behavior exercised in both dispatcher exec and per-request import modes, zero remaining runtime references to the three hosts. Docker build and live AI inference need post-merge verification on the GPU host. Claude-Session: https://claude.ai/code/session_01XGB4pGvTvb7sUX4JN745U7 * fix: allow AI model downloads by default, make strict offline mode opt-in Product call: ease of use first. The download gating from the previous commit inverts its default: runtime model fetches (public model weights only, never user data) are allowed out of the box so AI tools self-heal, and SNAPOTTER_ALLOW_MODEL_DOWNLOAD=0 becomes the explicit strict offline mode for airgapped deployments, where every fallback raises the actionable error instead of fetching. Changes: offline_guard blocks only on an explicit 0/false; the unconditional HF_HUB_OFFLINE/TRANSFORMERS_OFFLINE image ENV is removed and bridge.ts sets those flags for the sidecar only in strict mode; .env.example documents the new default; install_feature's lift/restore stays. All bundled-path preferences, pre-existence checks, and symlink pre-placement remain, so installed bundles never trigger a download. The OSM, Scalar font, and editor font fixes are unchanged. Validation rerun: typecheck 9/9, Biome clean on touched files, 5178 unit tests pass, py_compile on touched scripts, guard behavior verified for unset/1 (allowed) and 0/false (blocked with the new message). Claude-Session: https://claude.ai/code/session_01XGB4pGvTvb7sUX4JN745U7 |
||
|
|
7b6765030b | ci: gate demo deploy | ||
|
|
f6f7b5a4bc | fix: sync demo theme with app | ||
|
|
7e6939bf0d |
docs: show "200+" for the landing tool-count stat (#420)
Match the drift-proof public phrasing used elsewhere (the Languages stat already reads "20+"). Avoids the below-hero number going stale as tools are added. Claude-Session: https://claude.ai/code/session_01JQ8LmV8LPLi8yNTayHzSTQ |
||
|
|
7473d2d0fc | ci: deploy demo automatically | ||
|
|
f7b9a818b5 |
docs: promote one-command quick start and refresh social card (#419)
README: - Add a one-command Quick Start above Key Features (single docker run) to show how fast setup is; rename the detailed section to Deployment and drop the duplicated one-liner. - List TinyWow among the alternatives. - Tighten the sponsor call to action. Branding: - Replace the social card's GDPR/HIPAA badges with Self-hosted, Privacy-sensitive, Compliance-friendly, Air-gap capable, Open source, matching the landing Hero. - Regenerate branding/social-preview.png and the synced landing/web OG images (counts refreshed to the live catalog). Claude-Session: https://claude.ai/code/session_01JQ8LmV8LPLi8yNTayHzSTQ |
||
|
|
2a36b3dfe0 |
fix(ai): update arm64 bundle sha256/size after protobuf<5 rebuild (#421)
Rebuilt the object-eraser-colorize, ocr, and transcription arm64-cpu bundles with the protobuf<5 pin (PR #417) and republished them to deepsafe/feature-bundles/v2.0.0. Update the manifest archive checksums, compressed sizes, and (previously 0) extracted sizes to match the new tarballs so install_feature.py's sha256 verification passes. All three rebuilt bundles bake protobuf 4.25.9; verified gzip-clean and that paddle 3.2.2 / onnxruntime coexist with protobuf 4.25.9 on aarch64. Claude-Session: https://claude.ai/code/session_01VtvE6K8iEr5jGFJJpHEaPA |
||
|
|
ea004a3308 |
feat(landing): swap Try Demo for Get Started Free CTA in top nav (#418)
Book a Demo moves into the outline nav slot (Try Demo is removed), and a new Get Started Free primary CTA links to the docs getting-started guide in a new tab. Applies to both the desktop bar and the mobile menu. Claude-Session: https://claude.ai/code/session_01STf4CGEkYKNwN26rMxVpn6 |
||
|
|
2c2fb65fca |
fix(ai): pin protobuf<5 on arm64 so mediapipe face landmarks work (#417)
* fix(ai): pin protobuf<5 on arm64 so mediapipe face landmarks work aarch64 has no mediapipe wheel above 0.10.18, and 0.10.18 calls MessageFactory.GetPrototype (removed in protobuf 5+). With protobuf unpinned, the paddle/onnxruntime deps pull protobuf 7.x into the shared AI venv and break mediapipe FaceLandmarker, so red-eye-removal fails on every input (blur-faces and smart-crop keep working via a prebuilt graph). Split mediapipe by platform and pin protobuf>=4.25.3,<5 for aarch64 only. x86_64 keeps mediapipe 0.10.35, which works with protobuf 7, so requirements-gpu.txt (amd64 only) stays unpinned. Also fixes a latent issue where mediapipe>=0.10.21 was unsatisfiable on aarch64. Verified live on the arm64 container: red-eye-removal completes on real jpg and heic faces; OCR (tesseract) and paddle import unaffected. Claude-Session: https://claude.ai/code/session_01VtvE6K8iEr5jGFJJpHEaPA * fix(ai): pin protobuf<5 in arm64 bundles lacking a mediapipe constraint Bundles are built from docker/feature-manifest.json, not requirements.txt, so this is the change that actually fixes the shipped arm64 bundles. On arm64, object-eraser-colorize (onnxruntime), ocr (paddle) and transcription (faster-whisper pulls onnxruntime) install a protobuf-dependent package with no mediapipe to cap protobuf, so they bake protobuf 7.x. All bundles share one /data/ai/venv at install time, so whichever of those installs last overwrites protobuf to 7.x and breaks mediapipe FaceLandmarker (red-eye-removal). Pin protobuf>=4.25.3,<5 in those three arm64 lists (appended last so it downgrades after the puller installs). The four mediapipe bundles already resolve <5. Dry-run on aarch64 confirmed paddle + protobuf 4.25.9 resolve with no conflict. Claude-Session: https://claude.ai/code/session_01VtvE6K8iEr5jGFJJpHEaPA * refactor(ai): keep protobuf fix in feature-manifest.json only requirements.txt is not consumed by the Docker image build (the base /opt/venv is installed from a hardcoded package list, and the ML libs ship via bundles), so the requirements changes had no effect on shipped artifacts and only tripped the dependency-review scanner on the protobuf range. Revert them; the operative arm64 bundle fix lives entirely in docker/feature-manifest.json. Claude-Session: https://claude.ai/code/session_01VtvE6K8iEr5jGFJJpHEaPA |