- New apps/demo/ that reuses apps/web components with mocked API layer
- Full UI shell: login, change password, analytics consent, dashboard, all tool pages
- Stateful mock tracks session flow (password change, analytics consent)
- Demo banner with link to GitHub repo
- Processing attempts show info message with GitHub link
- Deployed to Cloudflare Pages as static site (no backend)
Also links demo across all surfaces:
- README: "Live Demo" badge
- Landing navbar: "Try Demo" CTA button (replaces "Book a Demo")
- Landing hero: "No sign-ups. No credit card." tagline
- Docs getting-started: "Try before installing" tip box
Other changes:
- Docs: move NVIDIA GPU section above GHCR, demote GHCR to collapsed details
- Fix before-after slider checkerboard background for transparency
- Fix remove-bg preview reset when no effects applied
archiver v8 changed its default export, breaking all ZIP-producing
tools (pdf-to-image, split, batch, favicon, bulk-rename, svg-to-raster).
Reverted to v7 -- the lodash vulnerability via archiver is _.template
which is never called directly.
@fastify/static v9 has breaking changes incompatible with the current
static file serving setup. Reverted to v8 -- the path traversal CVEs
in v8 are mitigated by the existing path traversal guards in files.ts.
Updated edge-cases test to expect 400 for >64KB settings payloads
(new security limit).
Auth: login rate limit 30/min (was 500), global rate limit 1000/min (was
unlimited), password/username max lengths on all Zod schemas, session
invalidation on role change, API key legacy scan bounded to 100 keys.
SVG: hardened regex sanitizer with CDATA stripping, XML entity decoding,
set/animate/iframe/embed blocking, comprehensive data: URI blocking,
use element external href blocking. 11 attack payload fixtures added.
SSRF: fixed DNS rebinding TOCTOU by pinning resolved IPs via custom
HTTP/HTTPS agents. Added 6to4 and NAT64 to blocked IPv6 ranges.
Docker: capability dropping (cap_drop ALL + minimal cap_add), resource
limits (4g/8g mem, 512/1024 pids), healthcheck timeout, password
removed from startup banner, default password warning comments.
Network: CSP and HSTS applied in all environments (not just production),
stack traces removed from all error responses, internal paths stripped
from error details, per-route rate limits on uploads (60/min) and URL
fetches (200/hour).
Files: exclusive temp file creation (O_EXCL), disk space circuit
breaker, per-user storage quotas, settings payload 64KB size guard.
Python sidecar: script name allowlist in dispatcher, minimal environment
for subprocess spawns.
Dependencies: fixed 6 production CVEs (drizzle-orm, fastify, fast-uri,
@fastify/static, next, archiver/lodash). Pinned all GitHub Actions to
SHA hashes.
114 security tests added. Full OWASP Top 10 penetration test matrix
verified against production Docker container (30/30 pass after
hardening).
- 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
Replace static llms.txt and llms-full.txt with auto-generated versions
that stay in sync with docs on every build. The plugin also generates
per-page .md files for individual page fetching by LLMs.
- 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
Integrates feature/multi-image-ux branch with 20 commits including:
- Multi-image viewer with arrow navigation and filmstrip thumbnails
- Batch processing across all tool settings components
- File store rewrite with FileEntry model for multi-image support
- Resize settings redesigned with tab-based UI (presets, custom, scale)
- Side-by-side comparison for resize results
- Per-file metadata caching in strip-metadata
- Client-side ZIP extraction via fflate
- SSE progress correlation via clientJobId
Consolidate all access to localhost:1349 — Vite dev server serves on 1349
and proxies API calls to an internal dev port (13490). Production API
defaults to 1349. Also includes strip-metadata improvements, UI component
updates, and compress operation fixes.
- Set up semantic-release with zero-touch CI pipeline on push to main
- Add version sync script to keep all package.json files and APP_VERSION
constant in sync automatically
- Consolidate Docker publishing into single tag-triggered workflow that
pushes to both Docker Hub and ghcr.io with semver tags
- Add help dialog with keyboard shortcuts, getting started guide, and
resource links
- Sync all versions to 0.2.1 to match Docker Hub latest
- 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
- Add replace-color tool with pixel-level color replacement and tolerance
- Register all 19 new Phase 3 tools in routes/tools/index.ts
- Map all new tool IDs to settings components in tool-page.tsx
- Add PDF, ZIP, ICO, JSON content types to download route
- Install qrcode, jsqr, potrace, pdfkit dependencies
Backend: POST /api/v1/tools/:toolId/batch accepts multiple files +
settings, processes via p-queue with CONCURRENT_JOBS concurrency limit,
streams ZIP response using archiver. Tool registry in tool-factory
enables batch to reuse any registered tool's process function. SSE
endpoint at GET /api/v1/jobs/:jobId/progress provides real-time updates.
Handles partial failures gracefully, preserves filenames, deduplicates
collisions. Frontend: use-batch-processor hook handles upload, SSE
progress tracking, and automatic ZIP download.
- Register @fastify/multipart plugin with size limits from env config
- Workspace manager: createWorkspace, getWorkspacePath, cleanupWorkspace
- File validation: magic byte detection, format check, megapixel limit
- POST /api/v1/upload: multipart upload with validation, returns jobId + file metadata
- GET /api/v1/download/:jobId/:filename: serve files with Content-Disposition
- Path traversal guards on all file-serving endpoints
- Add @stirling-image/image-engine and sharp as API dependencies
- Add apiUpload, getDownloadUrl, apiDownloadBlob to web client