Commit Graph
31 Commits
Author SHA1 Message Date
SnapOtterandGitHub b37faed95f fix: QA sweep - tool routes, security, i18n, a11y, + AI bundle install hardening (#393)
* fix(api): correct format/filename/container handling across tool routes

Found during a comprehensive QA sweep exercising every tool against its
full accepted-format matrix:

- watermark-image, compose: preserve the requested output format and a
  matching download filename/extension instead of always emitting the
  source format
- compose: crop oversized overlays to the visible base area instead of
  crashing Sharp's composite, and reject only overlays fully outside the
  base image instead of any oversized one
- compare, vectorize: switch to the shared image input handler so
  filenames and formats like .svgz/.tga/RAW survive validation instead
  of being rejected pre-processing
- tool-factory, images-to-video: normalize frames through Sharp before
  handing them to FFmpeg, fixing GIF/AVIF/RAW image-to-video jobs that
  previously failed or hung
- media-tool, replace-audio, embed-subtitles: fix legacy container
  MIME/codec handling for MPEG sources and subtitle remux cases
- files: expand download MIME mapping for text/data/document/video/audio
  outputs that were falling back to a generic content type
- convert-document/presentation/spreadsheet: same-format conversions now
  return the original validated file instead of erroring or producing
  corrupt tiny output

Claude-Session: https://claude.ai/code/session_019fpSXhLGLXWwfyZY2tWhLG

* fix(web): dropzone a11y, stale localStorage getter, dead code

- dropzone: stop making the whole drop-zone section clickable/focusable.
  A section acting as an interactive element around a real upload button
  is a nested-interactive-element anti-pattern that confuses screen
  readers; drag-and-drop doesn't need focus semantics, only the button
  fallback does. Keeps that button semantic and keyboard-reachable.
  Updates the two e2e call sites that clicked the section directly.
- api, use-auth: read through window.localStorage via the existing API
  storage helper instead of the bare global, which resolves to Node's
  experimental localStorage getter under Vitest and threw
- find-duplicates-settings, info-settings, login-page: remove dead code
  (unused zip-download handler, a stale mount-only effect dependency
  that left cached info stuck at reused indices, an unused response
  variable)

Claude-Session: https://claude.ai/code/session_019fpSXhLGLXWwfyZY2tWhLG

* fix(i18n): pt-BR, zh-CN, zh-TW were silently falling back to English

The locale loader looked up dynamic-import exports by the raw locale
code (mod["pt-BR"], mod["zh-CN"], mod["zh-TW"]), but those three modules
export camelCased bindings (ptBR, zhCN, zhTW) since identifiers can't
contain hyphens. The lookup returned undefined and every consumer
silently fell back to English for these three locales. Replaces the
generic lookup with explicit per-locale loaders so the mapping can't
drift out of sync again.

Also updates the dropzone helper copy across all 21 locales to match
the drag-only dropzone wording from the previous commit.

Claude-Session: https://claude.ai/code/session_019fpSXhLGLXWwfyZY2tWhLG

* fix(docs): clear build warnings in the VitePress site

- config.mts: add an onwarn handler for the @vueuse INVALID_ANNOTATION
  warnings emitted during the docs build
- deployment.md: the caddyfile code fence language isn't a shiki grammar
  VitePress ships with, so it warned on every build; use txt instead

Claude-Session: https://claude.ai/code/session_019fpSXhLGLXWwfyZY2tWhLG

* test(qa): update QA harness for the drag-only dropzone and regen metadata

- api-sweep, qa-helpers, verify-ai: add JSON-body tools, multi-input
  secondary fixtures, async polling for slow valid jobs, 501
  FEATURE_NOT_INSTALLED skip handling, and safer per-tool settings
- input-preview, pipeline-ui specs: update upload flow for the
  drag-only dropzone surface
- add tests/fixtures/data/valid/chart.json, a valid chart fixture the
  updated helpers route to
- regenerate tools-meta.json against current TOOLS[]

Claude-Session: https://claude.ai/code/session_019fpSXhLGLXWwfyZY2tWhLG

* fix(security): close a login timing side-channel, harden zip-slip tests

Found during a black-box security sweep of the real auth-enabled
production container: a nonexistent username returned 401 in ~3-10ms,
while a wrong password for a real user took ~35-42ms, because scrypt
verification only ran when a user row existed. That timing gap lets an
attacker enumerate valid usernames without ever guessing a password.
Now runs verification against a cached dummy hash on the unknown-user
path too, so both cases cost the same regardless of outcome.

extract-zip already had a relative-traversal regression test
(../evil.txt), but its absolute-path rejection branches
(name.startsWith("/") / startsWith("\\")) had none. Added the three
missing cases: deep relative traversal, absolute Unix path, and
Windows-style absolute path.

Claude-Session: https://claude.ai/code/session_019fpSXhLGLXWwfyZY2tWhLG

* test(qa): add UI-driven AI bundle install scripts

QA_PROMPT.md's Phase 2 requires installing AI models the way a user
does -- through the UI, on demand from HuggingFace -- and treats the
curl-based admin install endpoint as fallback-only. Nothing in the
harness actually drove that flow; tests/qa/seed-ai-models.sh installs
via docker exec + pip, which is further from a real user than even the
API fallback.

install-ai-bundles-ui.mts logs in, opens Settings > AI Features,
screenshots the pre-install state, clicks Install All, and screenshots
progress -- then exits, since installs continue server-side once
triggered. verify-ai-install-complete.mts polls bundle status,
screenshots the completed state, and runs one real tool per installed
bundle to prove the freshly-downloaded model actually executes.

Claude-Session: https://claude.ai/code/session_019fpSXhLGLXWwfyZY2tWhLG

* fix(qa): correct the apiToolPath import in the AI verify script

Dynamic import of the package name failed under tsx's module resolution
from apps/api's node_modules context; use the same relative-path import
api-sweep.mts already uses successfully.

Claude-Session: https://claude.ai/code/session_019fpSXhLGLXWwfyZY2tWhLG

* fix(web): correct AI bundle size estimates shown before install

Measured real downloads during GPU-node QA verification: photo-restoration
pulls ~4.4GB (was advertised as 800MB-1GB, off by 4-5x) and ocr pulls
~5.5GB (was advertised as 3-4GB). Both estimates only accounted for model
weights, not the pip dependencies (torch/paddle) that come down with them.
Updated to reflect actual total download size, since that's what a user
deciding whether they have the disk/bandwidth actually needs to know.

Claude-Session: https://claude.ai/code/session_019fpSXhLGLXWwfyZY2tWhLG

* fix(web): make desktop Settings reachable when auth is disabled

AvatarDropdown (the only desktop entry point to Settings) was gated
behind `!isMobile && authEnabled`. With AUTH_ENABLED=false the synthetic
anonymous admin user should have full Settings access per how auth.ts
documents this mode -- and the mobile bottom nav already worked this way,
showing Settings unconditionally. Desktop just had a stray extra gate the
component doesn't need: AvatarDropdown already resolves its own username
internally (falling back to "admin") and reads authEnabled itself where
it actually matters (hiding the Logout button). Removed the outer gate;
verified end-to-end against a fresh AUTH_ENABLED=false instance -- avatar
now renders, Settings opens, shows the anonymous/Admin identity correctly.

Also documents (not changes) a related finding in install_feature.py:
detect_arch() always resolves amd64 hosts to the GPU-bundled archive
variant regardless of actual GPU presence, since no CPU-only amd64
archive is published to the bundle repo yet. Left as a code comment
rather than a behavior change, since requesting an unpublished archive
key would hard-fail installs entirely -- worse than the current
oversized-but-working download. Full detail in the QA report.

Claude-Session: https://claude.ai/code/session_019fpSXhLGLXWwfyZY2tWhLG

* fix(ai): stop logging expected dispatcher reloads as crashes

After each AI bundle install the Python dispatcher reloads because the
venv changed, and after every app shutdown it's SIGTERMed. Both took the
close handler's `code !== 0` branch (SIGTERM makes the exit code null),
so they were counted as crashes -- producing an alarming "crash" line in
the logs and a pointless ~1s recovery backoff after each of 7 installs.
A `stopping` flag set in shutdown() lets the close handler tell an
intentional stop apart from a real crash. The request-timeout kill path
deliberately does not set it, so a genuinely hung script still records a
crash and the 5-in-60s permanent-disable threshold is untouched.

Claude-Session: https://claude.ai/code/session_019fpSXhLGLXWwfyZY2tWhLG

* fix(api): return a clean message when content-aware resize times out

Carving a very high-resolution image down to a tiny target could exceed
the caire subprocess timeout, and the raw error forwarded to the user was
caire's terminal output -- ANSI color codes and progress-spinner control
characters -- instead of anything actionable. Now: the timeout path
throws a clear "timed out; try a smaller image or larger target" message
(keeping the raw stderr as `cause` for server logs); friendlyError()
strips ANSI/control chars centrally so any subprocess dump surfaced
through the shared sanitizer is plain text; and the content-aware-resize
route (a custom route that bypassed the sanitizer) now routes its error
paths through friendlyError like every other tool.

Claude-Session: https://claude.ai/code/session_019fpSXhLGLXWwfyZY2tWhLG

* fix(ai): stop bundle installs from exhausting host disk

Installing an AI bundle on a tight-disk host could push the root
filesystem to zero bytes free after the preflight check had already
passed. Two root causes:

- move_tree used copytree+rmtree, so during the move the extracted
  payload existed in both staging and the venv at once -- a full
  transient doubling on disk. Rewrote it to rename entries (a cheap
  metadata op on the same filesystem, no copy), falling back to a copy
  only across filesystems.
- the preflight budget used the manifest's extractedSize verbatim, which
  is 0 for several archives, collapsing the estimate to just the
  compressed size. Added a conservative fallback (3x compressed) so a
  missing value can't under-reserve.

Also added a real-on-disk re-check immediately before the first
destructive venv write (measuring the actual extracted payload and
whether the move needs extra space for a cross-filesystem copy), which
also now covers the offline-import path that previously skipped the disk
check entirely; wrapped the moves so an out-of-space failure returns a
clean actionable error instead of a traceback; and made the disk check
resolve the nearest existing ancestor so it never throws on a
not-yet-created venv path.

Claude-Session: https://claude.ai/code/session_019fpSXhLGLXWwfyZY2tWhLG

* feat(web): show the real per-arch AI bundle download size

The bundle cards and install prompt showed a hardcoded, architecture-blind
estimatedSize string. That's misleading: amd64 hosts always pull the
CUDA-inclusive archive (there's no CPU-only amd64 variant published), so a
bundle labelled "1-2 GB" can actually download several times that, while
arm64 pulls a much smaller archive for the same label. The manifest
already carries the real per-arch compressedSize (and extractedSize where
measured), so surface those: a new optional downloadBytes/installedBytes
on FeatureBundleState, populated in getFeatureStates() for this host's
arch (resolver mirrors install_feature.py detect_arch), shown by the UI
when present with estimatedSize kept as the fallback label. Also nudged
upscale-enhance's fallback string (4-5 -> 5-6 GB) to match its real
compressed size, consistent with the earlier photo-restoration/ocr fixes.

Fields are optional so demo/mock and existing tests stay compiling; the
manifest's extractedSize is 0 for a few archives, which now surfaces as
null rather than a bogus 0.

Claude-Session: https://claude.ai/code/session_019fpSXhLGLXWwfyZY2tWhLG

* fix(web): move the AI install queue to the server so it survives tab close

Installing multiple bundles could silently lose all but the first. The
server rejected a concurrent install with 409, so the client worked
around it by queueing the rest in browser-local state and only POSTing
each once it saw the previous finish. A single POSTed install is durable
(the installer child is detached from the request), but a queued one had
zero server footprint -- close the tab mid-queue and those installs
vanished with no error, while the UI still showed them "Queued". The
client "mutex" didn't even serialize: the queued bundles' local waits all
resolved at once and raced into concurrent POSTs that 409'd each other.

Now the queue lives on the server (a small in-memory FIFO leaf module).
The install endpoint enqueues instead of 409-ing and returns
202 {jobId, queued}; a pump starts the next bundle when the current one's
child exits (and after an offline import releases the lock), all behind
the existing venv + file locks, which are unchanged. The client just
POSTs every bundle immediately and reflects the server-reported
queued/installing status; Install All fires all POSTs and lets the server
serialize them, keeping the one-shot retry-on-failure. Adds "queued" to
FeatureStatus (the bundle card already rendered that state) and surfaces
it from getFeatureStates. In-memory is deliberate: it matches the
existing contract (survives a tab close, not a server restart, which
already clears the lock on boot).

Claude-Session: https://claude.ai/code/session_019fpSXhLGLXWwfyZY2tWhLG

* fix(qa): don't log env-derived credentials in the AI-install script

CodeQL flagged clear-text logging of sensitive information: the login
status line interpolated the QA base URL and username (both read from
the process environment) into a console.log. Replaced with a static
message. QA helper only, but it's a real hygiene issue and cleared the
high-severity code-scanning alert on the PR.

Claude-Session: https://claude.ai/code/session_019fpSXhLGLXWwfyZY2tWhLG
2026-07-03 09:54:02 +08:00
SnapOtterandGitHub ef342c268b docs: clarify nvidia cuda ai support (#378) 2026-06-29 21:29:00 +08:00
SnapOtter c6319cf8a9 fix(security): harden auth and outbound fetches 2026-06-29 17:54:12 +08:00
SnapOtterandGitHub 63a03d26f2 feat: pipeline templates, analytics opt-out, 83 conversion presets, positioning + e2e modernization
Lands five integrated branches: pipeline templates (#355), analytics opt-out (#354), 83 conversion presets bringing the catalog to 240 tools (#356), self-hosted positioning (#353), and e2e modernization (#351).

Integration fixes: aligned stale web analytics tests with the opt-out/allow-list model, closed 3 CodeQL incomplete-sanitization alerts in the i18n generator, resolved settings/index/docs/format-matrix conflicts, and corrected tool counts to 240.
2026-06-28 18:57:53 +08:00
SnapOtterandGitHub 607c24895b Improve docs search UX, dash cleanup, and colored modality tool icons (#338)
* feat(docs): improve search UX

- Hide the 'Search by Pagefind' branding in the search dialog footer
- Replace the 'No results found.' message shown before any query with a
  friendly hint (detected via empty-input :placeholder-shown state)
- Tune placeholder, empty-state, and results-heading copy; show a few
  sub-section matches per result (pageResultCount)

* docs: replace double-dash em-dash substitute with single dash

Swept prose ' -- ' to ' - ' and numeric ranges (e.g. 2--20 to 2-20)
across the documentation. CLI flags and code blocks left untouched.

* fix(landing): show colored category icons on modality tool pages

The /tools/<modality>/ pages rendered bare name+description cards with no
icon or color. Port the colored, category-tinted icon card from /tools/
so modality pages match the main catalog (icon, tint, 'Learn more').
2026-06-24 12:06:31 +08:00
SnapOtter 8e530f2f1b docs: simplify analytics copy across all surfaces 2026-06-24 11:22:34 +08:00
SnapOtter 5c30167bab docs: remove IP address mentions from privacy copy 2026-06-24 11:17:58 +08:00
SnapOtterandGitHub 9378b2caab docs: update privacy and analytics disclosure (#337)
Update README, in-app privacy page, landing privacy page, and
deployment docs to reflect the new analytics model. Remove references
to opt-in consent, Settings toggle, and ANALYTICS_ENABLED env var.
Document the SNAPOTTER_ANALYTICS build arg for disabling.
2026-06-24 11:13:23 +08:00
SnapOtterandGitHub 5d36ac06d8 feat(analytics): build-time bake + telemetry depth (#336)
Bake PostHog + Sentry into the published Docker image (SNAPOTTER_ANALYTICS
build arg, codegen script). Delete entire consent system. Move event emission
to BullMQ worker. Add cross-tier identity stitching, Sentry performance
tracing on both tiers, frontend funnel events. Fix stateful regex bug.

86 files changed, 1593 insertions(+), 3747 deletions(-)
2026-06-24 11:05:39 +08:00
SnapOtterandGitHub 1fec97111b fix(docker): make storage writable under non-root/foreign UIDs (TrueNAS, OpenShift) (#299)
The entrypoint only fixed volume permissions when started as root (chown +
gosu-drop to snapotter). Launched under a non-root/foreign UID (TrueNAS app
user, Kubernetes runAsUser, OpenShift) it did no permission setup, so /data and
/tmp/workspace -- owned by uid 999 from the image -- were not writable by the
running user. Uploads and processing then failed with a cryptic EACCES
("workspace folder is not writable") and AI bundle installs failed the same way,
while health checks still reported the container healthy.

- entrypoint: source new entrypoint-lib.sh; verify writability up front when
  non-root, and as snapotter after chown when root (catches root-squashed
  mounts), failing fast with an actionable message (which dir, uid/gid, how to
  fix) instead of a late, cryptic EACCES
- Dockerfile: own /data and /tmp/workspace as snapotter:0, group-writable with
  setgid, so an arbitrary UID with the root supplementary group (OpenShift /
  Kubernetes fsGroup) can write; keep /opt/venv world-readable for the AI venv
  bootstrap under arbitrary UIDs
- api: assert storage writability at boot (lib/storage-writable.ts), failing
  fast with the same guidance even when the entrypoint is bypassed
- docs: add a Storage permissions section (named volumes, bind mounts, TrueNAS,
  Kubernetes/OpenShift) and cross-link it from the security guide

Fixes #230
2026-06-22 16:58:59 +08:00
SnapOtterandGitHub 17726ae59d docs: multi-modality rebrand, 2.0 architecture accuracy, and full OpenAPI coverage (#254)
* docs: rebrand from image-only to multi-modality across docs and metadata

SnapOtter expanded from image-only to 157 tools across 5 modalities
(image, video, audio, document/PDF, data). Update all product-level
copy, metadata, and i18n that still framed it as an image-only tool.

- README, package.json, root llms.txt: multi-modality framing, 157 tools
- OpenAPI info + tags, generated /llms.txt tagline (docs.ts)
- VitePress docs site: hero, getting-started, architecture, security,
  deployment, configuration, developer, supported-formats
- i18n: 10 product keys across all 21 locales (hero, app description,
  privacy notes, AI features, progress messages, getting-started)
- web/demo/landing meta + privacy copy, COMMUNITY_GUIDE, .env.example

Stale tool counts (53/50+/52/70+/35) corrected to 157 throughout.
Database/container deployment claims left unchanged (out of scope).

* docs: fix stale post-rebrand test assertions and README language list

- tests/e2e-docs/homepage.spec.ts: assert the current docs homepage (file toolkit, 157 tools, 5 modalities) instead of the old image-only strings
- tests/unit/api/docs-route.test.ts: sync the reproduced llms.txt tagline with docs.ts
- README.md: 21 languages with the correct list (add Swedish and Chinese Traditional, drop Czech which is not supported)

* docs: correct 2.0 architecture references (Postgres 17 + Redis 8, 3-container stack)

The docs and metadata still described the 1.x stack (SQLite, single container, p-queue). Update them to the current 2.0 reality.

- README: replace the broken single-container `docker run` quick-start with the real Docker Compose stack (app + Postgres 17 + Redis 8); fix the "no Redis, no Postgres" feature bullet
- package.json: description no longer claims a single container
- apps/docs: rewrite database.md for Postgres; configuration.md DB_PATH -> DATABASE_URL + REDIS_URL; architecture.md SQLite/p-queue/better-sqlite3 -> Postgres/BullMQ/pg and add media-engine + doc-engine; developer/security/deployment/docker-tags/getting-started/contributing compose examples now include postgres + redis; index.md + api/ai.md AI count 16 -> 19
- SECURITY.md: Drizzle (SQLite) -> (PostgreSQL)
- landing: enterprise/FeatureHighlights single-container wording; TrustSignals/ToolGrid 150+ -> 157 (dynamic); Pricing/FAQ 15 -> 19 AI tools

* docs(api): document all video, audio, document, and data tool endpoints in OpenAPI

The spec covered only image tools; the Scalar UI and the generated /llms.txt and /llms-full.txt inherited that gap. Add the 104 missing tool endpoints so the API docs match the code.

- Video: 29 endpoints (most long/async; auto-subtitles is AI)
- Audio: 17 (transcribe-audio is AI)
- Document/PDF: 36 (ocr-pdf is AI; conversions are long/async)
- Data: 10
- Image: 12 newer tools (background-replace, blur-background AI; histogram/lqip-placeholder/sprite-sheet custom responses; barcode-generate uses a JSON body)

Each schema is derived from the tool's Zod validator and executionHint (fast -> 200, long -> 202+SSE, AI adds 501 FeatureNotInstalledError, multi-file inputs as arrays), referencing the existing shared schemas. Tool path entries: 64 -> 168. Spec parses as valid YAML with no duplicate paths and only known $refs.
2026-06-16 18:04:52 +08:00
SnapOtterandGitHub 01421640b5 fix: resolve file library Open File bug, upload reliability, and SSE proxy timeouts (#203)
The Open File button in the Files section did nothing due to a race
condition where the home page reset the file store on mount before files
from handleOpenFile could render. Upload on the files page used fetch
with no timeout, progress, or retry, causing silent failures on mobile
and slow connections. SSE connections for job progress had no keepalive
pings, allowing reverse proxies to kill idle streams.
2026-06-05 19:01:40 +08:00
SnapOtter d025a569e2 docs: add SEO descriptions to all doc pages and update hero subtitle 2026-05-16 12:48:23 +08:00
SnapOtter 86a74ab706 fix: replace hardcoded version in deployment docs with placeholder 2026-05-10 20:17:40 +08:00
SnapOtter bb6d26ffc6 docs: add comprehensive format support documentation 2026-05-08 00:26:56 +08:00
SnapOtter bd84728588 docs: comprehensive API sync and documentation audit
- Rewrite OpenAPI spec to match actual code (988 lines changed):
  - Fix ToolResponse schema (add previewUrl, savedFileId)
  - Fix Error schema shape ({error, details} not {statusCode, error, message})
  - Fix POST /api/auth/register URL (was /api/auth/users)
  - Fix login/session responses (7 missing user fields + expiresAt)
  - Fix 8 endpoints returning 204 → 200 with {ok: true}
  - Fix pipeline execute field name (steps → pipeline)
  - Fix API keys response key (keys → apiKeys)
  - Fix settings response wrapper, teams UUID type
  - Rewrite 7 major tool response schemas (info, barcode-read,
    find-duplicates, compare, remove-background, upscale, ocr, blur-faces)
  - Fix files/save-result (JSON → multipart), files/upload (201 + array)
  - Fix SSE progress schema (integers not arrays)
  - Add 422/501 error responses to AI and processing tools
  - Fix settings required → optional on 29 tool endpoints
  - Add 5 missing color adjustment fields to alias endpoints
- Rewrite rest.md tool parameter descriptions (12 tools fixed)
- Add Tool Sub-Routes section to rest.md (11 endpoints)
- Fix file library, settings, pipeline, auth docs in rest.md
- Fix API key hashing description (SHA-256 → scrypt)
- Fix "GitHub Pages" → "Cloudflare Pages" in architecture + deployment docs
- Fix tool count "45+" → "47" across all doc surfaces
- Fix branding endpoint paths in rest.md (/branding/logo → /settings/logo)
2026-04-25 07:17:45 +08:00
SnapOtter 3e3eb6f180 fix: update Docker Hub image path from snapotterhq to snapotter
The Docker Hub username is `snapotter`, not `snapotterhq`. Updates all
references in README, CI workflow, and documentation.
2026-04-25 01:25:45 +08:00
SnapOtter 119037e31d fix: remove reference to local-only HARDWARE_RECOMMENDATIONS.md
The file is gitignored and not in the repo, so the link was broken.
2026-04-25 00:49:25 +08:00
SnapOtter 2edb25883e fix: replace broken local file reference with GitHub link in docs
The deployment guide referenced `docs/HARDWARE_RECOMMENDATIONS.md` as
inline code, which is inaccessible from the deployed VitePress site.
2026-04-25 00:42:47 +08:00
SnapOtter 0309e0f680 chore: deploy to Cloudflare Pages and update branding
- 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
2026-04-24 18:06:29 +08:00
ashim-hq 8aea77b4be docs: remove hardcoded pipeline/batch limits, default to unlimited
Pipeline steps and batch size are now unlimited by default. The old
"20 steps" and "200 images" figures had no basis in the actual code
(MAX_BATCH_SIZE already defaulted to 0/unlimited). Both remain
configurable via MAX_PIPELINE_STEPS and MAX_BATCH_SIZE env vars.

Also includes updated hardware requirements and sidebar nav from
prior documentation audit.
2026-04-23 20:50:38 +08:00
AshimandGitHub 4c9dc6e38e fix: Docker hardening, security, and deployment readiness for V1 (#82)
Phase 1 — Docker Artifact Optimization:
- Replace broad `COPY . .` with targeted frontend source copies (API/Python
  changes no longer bust the frontend build cache)
- Replace build-essential with gcc/g++ (leaner runtime)
- Fix LOG_LEVEL=debug → info for production
- Harden .dockerignore (exclude worktrees, IDE, CI, test artifacts)

Phase 2 — State & Persistence:
- Add PUID/PGID support in entrypoint.sh for bind mount compatibility
- Guard against PUID=0/PGID=0 to prevent accidental root execution
- Evict conflicting system users (e.g. node:1000) before UID remap

Phase 3 — Security:
- Always register @fastify/rate-limit so login brute-force protection
  works even when global rate limit is disabled (RATE_LIMIT_PER_MIN=0)
- Add trustProxy support (TRUST_PROXY env var, default true) so rate
  limiting and audit logs use real client IPs behind reverse proxies
- Strip stack traces from 500 error responses in production
- Fix FSTDEP022 deprecation: maxParamLength → routerOptions
- Add multi-file guard on single-file tool endpoint with clear error
  message pointing to the /batch endpoint

Phase 4 — Graceful Degradation:
- Add consolidated hardware detection startup banner (GPU, rate limit,
  upload limit, proxy status)
- Add ConnectionMonitor component with health polling and reconnecting
  overlay that auto-dismisses when the server comes back

Phase 5 — Deployment Docs:
- Rewrite deployment.md with copy-paste CPU and GPU compose templates
- Add hardware requirements table (minimum, recommended, heavy workloads)
- Add PUID/PGID bind mount documentation
- Add complete env var reference table
- Add reverse proxy guides for Nginx, Nginx Proxy Manager, Traefik,
  and Cloudflare Tunnels
2026-04-21 10:19:08 +08:00
ashim-hq bac3febbdb docs: update deployment instructions for GitHub Container Registry usage 2026-04-16 17:29:50 +08:00
Siddharth Kumar Sah 85b1cfc10a chore: rename Stirling-Image to ashim across entire codebase
Complete rebrand from Stirling-Image to ashim following the project
move to https://github.com/ashim-hq/ashim.

Changes across 117 files:
- Package scope: @stirling-image/* → @ashim/*
- GitHub URLs: stirling-image/stirling-image → ashim-hq/ashim
- Docker Hub: stirlingimage/stirling-image → ashimhq/ashim
- GitHub Pages: stirling-image.github.io → ashim-hq.github.io
- All branding text: "Stirling Image" → "ashim"
- Docker service/volumes/user: stirling → ashim
- Database: stirling.db → ashim.db
- localStorage keys: stirling-token → ashim-token
- Environment variables: STIRLING_GPU → ASHIM_GPU
- Python cache dirs: .cache/stirling-image → .cache/ashim
- SVG filter IDs, test prefixes, and all other references
2026-04-14 20:55:42 +08:00
Siddharth Kumar Sah 958b10cb45 refactor: remove lite variant, fix release workflow
- Remove all lite/full variant logic from frontend, API, shared constants,
  docs, and tests (single unified Docker image only)
- Replace single QEMU multi-arch Docker build with per-architecture native
  builds (amd64 + arm64) and manifest merge to fix disk space exhaustion
- Add disk cleanup step and per-platform build cache scopes
- Switch release trigger from push to workflow_dispatch
- Add GitHub issue templates and PR template
2026-04-10 17:38:54 +08:00
stirling-imageandGitHub b0083e2b08 feat: unified Docker image with GPU auto-detection (#37)
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
2026-04-10 13:21:06 +08:00
Siddharth Kumar Sah f1ae974de9 docs: add CUDA tag to README, getting-started, deployment, benchmarks
- README: add CUDA docker run example alongside full and lite
- Getting started: add GPU acceleration tip with speedup numbers
- Deployment: add CUDA row to variants table
- Docker tags: expand benchmarks with warm + cold start tables
2026-04-05 20:35:24 +08:00
Siddharth Kumar Sah be45d3ca80 docs: add lite image references across all documentation
README Quick Start now shows both :latest and :lite commands.
Getting Started adds a tip callout about the lite image.
Deployment page lists both variants with a comparison table and
updates the CI/CD description to mention both are built.
Developer guide adds the lite build command.
2026-04-05 08:01:18 +08:00
Siddharth Kumar Sah 50a52b43c1 chore: rebrand repo from siddharthksah to stirling-image org
Update all references across docs, workflows, UI components, and config
to point to the new GitHub org (stirling-image/stirling-image) and Docker
Hub account (stirlingimage/stirling-image) ahead of repo transfer.
2026-03-30 08:17:54 +08:00
Siddharth Kumar Sah 6cfa3b0c38 feat: multi-arch Docker support, security hardening, and test improvements
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.
2026-03-28 11:19:09 +08:00
Siddharth Kumar Sah 6668615750 docs: add VitePress documentation site with GitHub Pages deployment
Rewrites all documentation with accurate project details (Fastify, port
1349, single-container Docker, all 33+ tools, full database schema).
Adds getting started guide and configuration reference. Updates help and
settings dialogs to link to the docs site.
2026-03-22 21:00:37 +08:00