- Add tool-specific suffix to output filenames so downloads don't overwrite originals (batch & single-tool routes)
- Skip deleting shared models when uninstalling a bundle that shares models with another installed bundle
- Auto-detect NVIDIA GPU and swap GPU-only pip packages (onnxruntime-gpu, paddlepaddle-gpu) for CPU equivalents
- Refactor docker-compose with YAML anchors and explicit cpu/gpu profiles
- Add libheif-plugin-x265 to Dockerfile
- Fix install-all queue logic to handle concurrent individual installs and clear stale errors
- Unify playwright docker config to use same test dir with API_URL env var
- Fix flaky e2e selectors, rename Strip Metadata → Remove Metadata, handle collage custom dropzone, improve fallback test image generation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove all ML pip installs (onnxruntime, rembg, realesrgan, paddlepaddle,
mediapipe, codeformer), model downloads, and post-install fixups from the
Dockerfile. The base image now ships only Node.js + Sharp + Python with
numpy/Pillow/opencv. AI features are installed on-demand at runtime via
the feature manifest and install_feature.py script.
Key changes:
- Remove SKIP_MODEL_DOWNLOADS build arg (no longer needed)
- Remove apt-get purge of build-essential (needed for runtime pip installs)
- Remove PaddleX symlinks and facexlib weight directory setup
- Add COPY of feature-manifest.json and install_feature.py
- Update PYTHON_VENV_PATH to /data/ai/venv, add MODELS_PATH and DATA_DIR
- Entrypoint bootstraps AI venv from /opt/venv on first container start
with crash-safe temp directory pattern
- Added model mismatch warnings in colorize, enhance-faces, and upscale routes.
- Improved error handling in colorize, enhance_faces, remove_bg, restore, and upscale scripts with detailed logging.
- Updated Dockerfile to align NCCL versions for compatibility.
- Introduced a new full tool audit script to test all tools for functionality and GPU usage.
- Created Playwright E2E tests for GPU-dependent tools to ensure proper functionality and performance.
Ubuntu mirrors (security.ubuntu.com) are frequently unreachable from
GitHub Actions runners, causing all amd64 Docker builds to fail.
Instead of installing Node.js via NodeSource apt repo (which requires
working Ubuntu mirrors for the initial apt-get update), copy the Node
binary and modules directly from the official node:22-bookworm image.
Also add retry with backoff to the system deps apt-get update.
Ubuntu security mirrors can be unreachable from GitHub Actions runners.
Add a retry loop with exponential backoff (15s, 30s, 45s) around
apt-get update in the Node.js install step for the CUDA base image.
- Parallelize all 14 model downloads using ThreadPoolExecutor (6 workers)
Downloads were sequential (~30 min), now concurrent (~5-10 min)
- Switch Docker cache from type=gha to type=registry (GHCR)
GHA cache has 10 GB limit causing blob eviction and corrupted builds
Registry cache has no size limit and persists across runner instances
- Add pip download cache mounts to all pip install layers
Prevents re-downloading packages when layers rebuild
- Replace [object Object] errors with readable messages across all 20+ API
routes by normalizing Zod validation errors to strings (formatZodErrors)
- Add parseApiError() on frontend to defensively handle any details type
- Add global Fastify error handler with full stack traces in logs
- Fix image-to-pdf auth: Object.entries(headers) → headers.forEach()
- Fix passport-photo: safeParse + formatZodErrors, safe error extraction
- Fix OCR silent fallbacks: log exception type/message when falling back,
include actual engine used in API response and Docker logs
- Fix split tool: process all uploaded images, combine into ZIP with
subfolders per image
- Fix batch support for blur-faces, strip-metadata, edit-metadata,
vectorize: add processAllFiles branch for multi-file uploads
- Docker: LOG_LEVEL=debug, PYTHONWARNINGS=default for visibility
- Add Playwright e2e tests verifying all fixes against Docker container
Set U2NET_HOME=/opt/models/rembg so rembg models pre-downloaded at
build time as root are found at runtime by the non-root ashim user.
Without this every fresh container re-downloaded the 973 MB BiRefNet
models on first background-removal request.
Apply the same fix to PaddleOCR: download to /opt/models/paddlex and
symlink into both /root/.paddlex and /app/.paddlex so PaddleX finds
models regardless of which HOME gosu resolves at runtime.
Fall back to per-request spawning in bridge.ts when the persistent
dispatcher crashes mid-request (e.g. OOM loading a large ONNX model),
so the operation succeeds instead of surfacing "Python dispatcher
exited unexpectedly" to the user.
Improve entrypoint.sh permission warning to mention Windows bind mounts
as the likely cause.
When SKIP_MODEL_DOWNLOADS=true the download script never runs, so
/opt/models was never created and the subsequent chown -R ... /opt/models
failed with exit code 1. mkdir -p it alongside the other required dirs.
esbuild (used by Vite) crashes under QEMU amd64 emulation on Apple Silicon,
the same way the Go runtime did. Building the frontend on the native platform
is safe because the output (HTML/CSS/JS) contains no architecture-specific code.
Replace CGO_ENABLED=0 (which fails because gioui.org requires CGO on Linux)
with a proper C cross-compiler approach using Debian multi-arch packages.
Running caire-builder with --platform=\$BUILDPLATFORM avoids QEMU crashes on
Apple Silicon; the C cross-compiler bridges the CGO gap for the target arch.
Also adds SKIP_MODEL_DOWNLOADS=true to the CI docker build job to prevent
HuggingFace CDN 504s in CI (image structure is what matters there).
The caire-builder stage now runs the Go toolchain on the native build
platform instead of under QEMU emulation. For cross-arch builds
(arm64 host → amd64 image) CGO_ENABLED=0 avoids needing a full C
cross-toolchain; the display window is unused in server mode anyway.
The binary is staged to /tmp/caire so the COPY path is stable across
both native and cross-compiled builds.
- Fix "Cannot access 'a' before initialization" TDZ error after login
caused by manualChunks splitting react-vendor + lucide icons into
circular ES-module chunks. Removed manualChunks entirely.
- Replace `import * as icons from "lucide-react"` (pulls all ~1000 icons)
with a targeted icon-map of ~50 icons actually used by tool definitions.
Reduces shared icons chunk from 745KB to 62KB (132KB→16KB gzip).
- Exclude static files from @fastify/rate-limit via allowList so rapid
page navigations don't 429 on JS/CSS chunk requests.
- Move Docker auth defaults (AUTH_ENABLED, DEFAULT_USERNAME,
DEFAULT_PASSWORD) from Dockerfile ENV to entrypoint.sh runtime exports
to avoid SecretsUsedInArgOrEnv warnings.
- Fix Docker CMD to use pnpm --filter for workspace-scoped tsx binary.
- Set COREPACK_HOME system-wide so non-root user can access pnpm cache.
- Lazy-load all pages in App.tsx and all controls in
pipeline-step-settings.tsx to keep main bundle under 300KB.
The Docker Build Test was consistently failing because HuggingFace CDN
returns 504 Gateway Timeout when downloading the LaMa ONNX model (~200MB)
from GitHub Actions runners. Model availability is an external dependency,
not something CI can control.
Added SKIP_MODEL_DOWNLOADS build arg (default: false). When set to true,
the download_models.py step is skipped entirely. CI only needs to verify
the image structure builds — Python deps install, Node build runs, app
code is copied — not that every ML model CDN is reachable.
Production builds (docker build without the arg) still download all models
as before.
Three fixes to ensure zero network access after docker pull:
1. rembg model allowlist: validate model parameter against the 7
pre-downloaded models, preventing rembg from attempting to download
unknown models via a raw API call.
2. GFPGAN/CodeFormer auxiliary models: pre-download facexlib's
detection_Resnet50_Final.pth and parsing_parsenet.pth at build time.
These were previously downloaded on first use via basicsr. Symlinks
in /app/gfpgan/weights/ ensure codeformer-pip also finds them.
3. OpenCV colorize models: pre-download the prototxt, caffemodel, and
pts_in_hull.npy so the lightweight OpenCV colorizer fallback works
in addition to the primary DDColor method.
Co-authored-by: stirling-image <stirling-image@users.noreply.github.com>
npx attempts to reach the npm registry even when tsx is installed
locally, causing the container to crash in airgapped/offline
environments with ECONNRESET. pnpm exec resolves tsx from local
node_modules only, with no network calls.
Closes#29
Co-authored-by: stirling-image <stirling-image@users.noreply.github.com>
* feat(shared): add enhance-faces tool definition and i18n strings
* feat(ai): add face enhancement script with GFPGAN and CodeFormer support
Detects faces via MediaPipe dual-model approach, then enhances using
GFPGAN (proven) or CodeFormer (via codeformer-pip) with auto fallback.
Supports strength-based alpha blending with original image.
* feat(ai): add TypeScript bridge for face enhancement
* feat(api): add enhance-faces route with GFPGAN/CodeFormer support
* feat(web): add enhance-faces settings component and register in tool registry
* feat(docker): add CodeFormer dependency and model download
- Add codeformer-pip to both CPU and GPU requirements
- Download CodeFormer model (~375MB) at Docker build time
- Add CodeFormer to smoke test verification
* fix(enhance-faces): address code review findings
- Skip alpha blend for CodeFormer (strength already applied via fidelity weight)
- Hide "only enhance main face" checkbox when Best (CodeFormer) is selected
- Fix sensitivity slider labels (swap More/Fewer faces to match actual behavior)
- Register EnhanceFacesControls in pipeline step settings
- Remove model names from user-facing descriptions
* fix(enhance-faces): fix CodeFormer integration and Docker setup
- Add codeformer-pip install to Dockerfile with --no-deps to avoid numpy 2.x conflict
- Re-pin numpy==1.26.4 after codeformer-pip install
- Pin codeformer-pip==0.0.4 in requirements files
- Broaden auto-mode fallback to catch any Exception from CodeFormer
---------
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.
- Pin PaddlePaddle to 3.0.0 on ARM64 to fix segfault in PIR inference
engine (3.1+ crashes on aarch64 Debian Bookworm)
- Fix text extraction for PaddleOCR 3.4.x result format (rec_texts)
- Add Node.js-level fallback chain (best -> balanced -> fast) when
Python subprocess crashes
- Add multi-image OCR: processes all uploaded files sequentially with
per-file progress and filename headers in combined output
- Convert input images to PNG via Sharp before OCR so HEIC, AVIF, WebP,
TIFF all work transparently
- Implement real auto-detect language using Tesseract multi-lang script
detection (analyzes Unicode ranges for Hangul, CJK, Kana, Latin)
- Default enhance to off (hurts clean digital images)
Replace the Python seam-carving library with caire (esimov/caire v1.5.0),
a Go-based content-aware resize engine that is faster and supports both
shrinking and enlarging via seam insertion.
- Add Go builder stage in Dockerfile to compile caire from source
- Rewrite seam-carving.ts to call caire via execFile (no Python sidecar)
- Remove content-aware-resize from PYTHON_SIDECAR_TOOLS (60s timeout)
- Add new options: blur radius, edge sensitivity, square mode, face detection
- Move content-aware toggle below standard resize in UI (subtler placement)
- Rename "Don't enlarge" to "Limit to original size" with hover tooltip
- Add smooth progress bar for medium-duration tools
- Delete seam_carve.py and remove seam-carving pip dependency
- Update integration tests and visual regression screenshots
Merge CPU, CUDA, and lite Docker images into a single unified image.
One tag (latest) works on all platforms: amd64 (NVIDIA CUDA) and arm64 (CPU).
GPU auto-detected at runtime. All ML models and packages baked in.
Key changes:
- Platform-conditional Dockerfile (nvidia/cuda on amd64, node on arm64)
- tini as PID 1 for proper signal handling
- Fix FILES_STORAGE_PATH data loss bug
- Fix RealESRGAN upscaler (was broken, always fell back to Lanczos)
- Fix PaddleOCR language codes and stdout corruption
- Simplified CI/CD (single build, single tag)
- Expanded model pre-download with verification
- Shutdown timeout, improved health endpoint
- Remove unused lama-cleaner
The STIRLING_GPU=true env var was baked into the :cuda Dockerfile,
which made gpu_available() return True without checking actual
hardware. On machines without a GPU, this would crash upscale.py
(torch.device("cuda") fails) and ocr.py (PaddleOCR use_gpu=True).
Fix: the env var can only disable GPU (set to false/0), never
force-enable it. Hardware detection always runs. Removed the
baked env var from the Dockerfile since it adds no value now.
Add a :cuda Docker image tag that auto-detects NVIDIA GPU at runtime
and falls back gracefully to CPU. Same pattern as Immich.
- New gpu.py shared utility for cached CUDA detection
- Background removal (rembg): pass CUDAExecutionProvider to ONNX Runtime
- Upscaling (Real-ESRGAN): use CUDA device + FP16 when GPU available
- OCR (PaddleOCR): enable use_gpu when CUDA detected
- Dispatcher reports GPU status at startup via readiness signal
- Admin health endpoint exposes GPU availability
- Dockerfile uses ARG GPU=false with conditional NVIDIA CUDA base image
- docker-compose.gpu.yml override for GPU users
- CI/CD workflows build and publish :cuda tag (amd64 only)
Three tags: :latest (CPU), :lite (no AI), :cuda (GPU with CPU fallback)
Adds a gosu-based entrypoint that starts as root, fixes ownership of
/data and /tmp/workspace for the stirling user, then drops privileges.
This fixes "SQLITE database not found" errors when users bind-mount
host directories.
Remove hardcoded --platform=linux/amd64 from Dockerfile so buildx produces
native arm64 images for Apple Silicon and Raspberry Pi. Add audit logging
for auth events, harden file storage with extension whitelists and
double-extension attack prevention, reject null-byte buffers in validation,
add data-testid attributes to all tool settings components, update
deployment docs with architecture notes and correct CI workflow references,
and fix unit test mock to match throwWithMessage error extraction.
Add --mount=type=cache for pnpm store, turbo cache, and pip cache in
Dockerfile. This significantly reduces rebuild times by reusing
previously downloaded dependencies across builds.
- Remove @fastify/swagger and @fastify/swagger-ui (API docs live on GitHub Pages)
- Run typecheck, build, and docker CI jobs in parallel instead of sequentially
- Switch default from birefnet-general (973MB, 4min) to
birefnet-general-lite (faster, still SOTA quality)
- Fix Python script stdout pollution — progress messages now go to
stderr so the JSON result parser doesn't break
- Pre-download birefnet-general-lite in Docker build
- Switch default model from U2-Net to BiRefNet (state-of-the-art)
- Add 6 model options: BiRefNet, BiRefNet Lite, BiRefNet Portrait,
BRIA RMBG, IS-Net, U2-Net
- Add animated progress bar with stage indicators (loading model,
analyzing, removing, refining edges) and elapsed timer
- Add intuitive background color presets (Transparent, White, Black,
Red, Green, Blue) as clickable buttons + custom color picker
- Handle background color compositing in Python (PIL alpha composite)
- Add checkerboard pattern to before/after slider for transparency
- Pre-bake BiRefNet model (973MB) in Docker image for instant use
- 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