* 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
Proxmox LXC and other headless Linux installs lack libgl1, causing
all AI tools to show a misleading "install opencv-python-headless"
error even though the pip package is already installed. Detect the
libGL ImportError and suggest `apt-get install -y libgl1` instead.
- Add Cloudflare Pages deployment for landing page (snapotter.com) and
docs (docs.snapotter.com)
- Create deploy-landing.yml and update deploy-docs.yml workflows
- Update CI to ignore apps/landing/** paths
- Fix logo transparency (remove white background) across all apps
- Recreate social-preview.png with SnapOtter branding
- Update all docs URLs from GitHub Pages to docs.snapotter.com
- Update VitePress config: light theme default, fix llms.txt paths
- Add .vitepress/cache/ and .env.* to gitignore
- Add safe_onnx_session() to gpu.py with graceful CUDA EP → CPU fallback
- Replace bare ort.InferenceSession() calls across colorize, restore, inpaint, remove_bg
- Add libcublas-12-6 to production Dockerfile for ONNX Runtime CUDA EP
- Add skipIfFeatureNotInstalled guards to remove-bg, blur-faces, smart-crop, ocr, noise-removal e2e specs
- Add AI tool install prompt detection in tools-all.spec.ts
- Add smart-crop to PYTHON_SIDECAR_TOOLS so frontend shows install prompt correctly
- Create Dockerfile.test.dockerignore to include tests/ in test image builds
- Add libheif-examples and exiftool to Dockerfile.test for HEIC and metadata tests
- Regenerate visual regression baselines for Docker/Linux and skip on non-Docker platforms
The on-demand feature download system stores models at /data/ai/models/
(set via MODELS_PATH env var), but all Python scripts hardcoded
/opt/models/ as the base path. Each script now reads MODELS_PATH and
falls back to /opt/models for backward compatibility.
enhance_faces.py relied on implicit PyTorch auto-detection for both
GFPGAN and CodeFormer, bypassing the centralized gpu.py module.
inpaint.py queried ort.get_available_providers() directly, which
reports compiled-in backends rather than actual hardware.
Both tools now go through gpu.py so STIRLING_GPU=false correctly
forces CPU across every AI tool.
Co-authored-by: stirling-image <stirling-image@users.noreply.github.com>
Update erase-object pipeline, eraser canvas, and inpainting Python script.
Add LaMa model download script and update Dockerfile for model support.
Update multi-file tool routes for consistency.
Replace the external mask file upload workflow with an interactive
canvas-based brush tool. Users now paint directly on the image to
mark areas for erasure. Adds EraserCanvas component with adjustable
brush size, undo/clear, and mask export. Switch Python inpainting
from broken lama-cleaner to OpenCV cv2.inpaint (Telea algorithm).
Add before/after comparison slider after processing.
- Add user management endpoints (register, list, delete, change password)
- Add API key management (create, list, delete)
- Add settings persistence endpoints (get, put)
- Wire settings dialog to real backend (People, API Keys, System, Security)
- Fix login auth flow (window.location.href for full reload)
- Fix download URLs returning 401 (make public since UUIDs are unguessable)
- Fix border tool shadowColor validation (accept 6-8 hex digits)
- Fix remove-bg alpha matting fallback (retry without on failure)
- Fix AI tool silent fallbacks (report errors instead of no-ops)
- Add checkerboard background to before/after slider for transparency
- Add progress bars to all AI tool components
- Add Playwright E2E test suite (131 tests across 9 test files)
- Rewrite Dockerfile for production (tsx runtime, pre-baked AI models)
- Add .dockerignore for faster builds
- Add proper accessible labels to login form