Files
SnapOtter/.env.example
T
SnapOtterandGitHub d10d0f544f fix: release QA hardening across processing, media, security, and CI gates (#649)
A release-readiness QA pass over the whole product. The commits split into
defects a user would hit and gates that were reporting green while measuring
nothing.

## Fixes that change behaviour

Rate limiting was bypassable on every install: TRUST_PROXY defaulted to true, so
request.ip came from a client-set header and a forged X-Forwarded-For got past
the login limiter. The default is now a private-network trust list.

A transient Postgres outage stranded in-flight jobs, leaving finished output on
disk with no row pointing at it. A reconciler now resolves those rows and adopts
the bytes rather than dropping the work.

A Redis connection that moved to a new address wedged every read-blocked
consumer, so completions stopped signalling while health still answered 200.
Socket timeouts plus subscriber pings recover it.

Installing more than one AI bundle left the shared venv multi-versioned and
silently broke three tools. The installer now reconciles distributions to one
version each.

Converting an image to JXL at quality 1 through 4 returned a 500, because
libjxl 0.7 rejects the distance those values compute. The quality is floored at
what the encoder honours. A missing ffmpeg was also reported to the user as a
corrupt upload; it now says the engine is unavailable.

RAW uploads reached an unpatched LibRaw on arm64, so it is built from source at
0.22.2, and the release scan was split so it can fail on an unfixed critical
instead of hiding it behind ignore-unfixed.

## Gates that could not fail

Two mutation lanes ran zero mutants because Stryker crawled the gitignored docs
build; coverage discarded its whole report on any failing test; the lint gate
skipped root tests, scripts, and two workspaces; and several generated matrices
counted a host missing ffmpeg as a passing tool. Each now measures what it
claims.

Full evidence and the outstanding release items are tracked locally and are not
part of this branch.
2026-07-27 15:37:30 +08:00

127 lines
5.1 KiB
Bash

# Server
PORT=1349
AUTH_ENABLED=true
DEFAULT_USERNAME=admin
DEFAULT_PASSWORD=admin
STORAGE_MODE=local
# Cleanup
FILE_MAX_AGE_HOURS=72
CLEANUP_INTERVAL_MINUTES=60
# Upload & Batch (0 = unlimited)
MAX_UPLOAD_SIZE_MB=0
MAX_BATCH_SIZE=0
CONCURRENT_JOBS=0
MAX_MEGAPIXELS=0
# Max single-file AI jobs one user may have in flight (AI pool is concurrency 1; 0 = unlimited)
MAX_AI_JOBS_PER_USER=5
# Optional per-process memory cap (MB) for the native media/doc engines (0 = disabled; container limit is the primary backstop)
SUBPROCESS_MEMORY_LIMIT_MB=0
# Max frames processed by animated background removal (0 = unlimited)
GIF_BG_MAX_FRAMES=150
# Rate limiting (0 = disabled)
RATE_LIMIT_PER_MIN=0
# Users (0 = unlimited)
MAX_USERS=0
# Processing (0 = auto/unlimited)
MAX_WORKER_THREADS=0
PROCESSING_TIMEOUT_S=0
MAX_PIPELINE_STEPS=0
MAX_CANVAS_PIXELS=0
# SVG has no "auto" sizing: 0 here disables the pre-parse size cap entirely.
# Ship the code default (50 MB) so copying this file does not remove the guard.
MAX_SVG_SIZE_MB=50
MAX_LOGO_SIZE_KB=2048
MAX_SPLIT_GRID=100
MAX_PDF_PAGES=0
MAX_VIDEO_DURATION_S=0
MAX_AUDIO_DURATION_S=0
MAX_VIDEO_BITRATE_KBPS=0
LIBREOFFICE_TIMEOUT_S=120
# Engine binary overrides (default: $PATH lookup)
# FFMPEG_PATH=
# FFPROBE_PATH=
# QPDF_PATH=
# SOFFICE_PATH=
# PDFCPU_PATH=
# SNAPOTTER_HW_ACCEL= # nvenc|vaapi: hardware encoder family (default software)
# AI tools fetch missing model files automatically (public model weights only,
# never user data). Set 0 for airgapped deployments to guarantee zero outbound
# fetches; missing models then produce actionable errors instead of downloads.
SNAPOTTER_ALLOW_MODEL_DOWNLOAD=1
SESSION_DURATION_HOURS=168
LOGIN_ATTEMPT_LIMIT=10
# Which peers may set the client IP via X-Forwarded-For. request.ip is the key
# for the rate limiter, the login brute-force limiter and the enterprise IP
# allowlist, so this decides whether those controls key on something a client
# can choose. The default believes only peers on a private network, which
# covers a reverse proxy on a Docker network or a LAN while ignoring a forged
# header from a public client. Use `true` only when a proxy you control sits in
# front on a public address, `false` when nothing proxies this instance, or a
# comma-separated CIDR list to name the proxies exactly.
TRUST_PROXY=loopback,linklocal,uniquelocal
# Set to true in CI/dev to skip the forced password-change on the default admin
# SKIP_MUST_CHANGE_PASSWORD=false
# DB_PATH removed in 2.0 -- see DATABASE_URL below
WORKSPACE_PATH=./tmp/workspace
FILES_STORAGE_PATH=./data/files
DEFAULT_THEME=light
DEFAULT_LOCALE=en
APP_NAME=snapotter
# --- 2.0 foundation ---
# Postgres connection (required; this default matches docker-compose.dev.yml)
# Start the dev stack first: docker compose -f docker-compose.dev.yml up -d
DATABASE_URL=postgres://snapotter:snapotter@localhost:5432/snapotter
# Redis connection (required; this default matches docker-compose.dev.yml)
REDIS_URL=redis://localhost:6379
# Startup grace: wait this long (ms) for Postgres/Redis to accept connections
# before failing. Lets a dependency that is still booting recover instead of
# crash-looping. 0 = try once, fail fast.
DB_STARTUP_TIMEOUT_MS=30000
# Job spine tuning
SYNC_WAIT_MS=8000 # sync-response window (ms) before returning 202
JOBS_RETENTION_DAYS=30 # completed-job metadata TTL (days)
AUDIT_RETENTION_DAYS=0 # audit-log TTL (0 = keep forever)
LOG_DIR=./data/logs # rotating log ring for support bundles
# JOB_TIMEOUT_FAST_S=120 # timeout for fast pools (image, docs), seconds
# JOB_TIMEOUT_LONG_S=7200 # timeout for long pools (ai, media), seconds
# SCRATCH_PATH= # worker scratch dir (default: OS tmpdir/snapotter-scratch)
# Prometheus metrics: GET /api/v1/metrics requires an authenticated admin
# (scrapers need a session cookie or API key with system:health permission).
# --- Analytics ---
# Basic analytics are included by default. SnapOtter works normally without them.
# To disable: docker compose build --build-arg SNAPOTTER_ANALYTICS=off
# Runtime kill switch: set to 0 to disable ALL telemetry (Sentry + PostHog)
# for this instance without rebuilding. The in-app admin toggle does the same
# from Settings; this env var also covers boot-time crashes and CI fleets.
# ANALYTICS_ENABLED=false is honored as an alias (0/off also work).
# SNAPOTTER_TELEMETRY=1
# Label this instance's error reports (shows as the Sentry environment).
# SNAPOTTER_ENV=production
# One-time SQLite import on first boot (1.x upgrade path). Leave unset normally.
# SQLITE_MIGRATE_PATH=/data/snapotter.db
# --- OpenTelemetry Distributed Tracing (enterprise only) ---
# Requires a valid enterprise license with distributed_tracing feature.
# Set OTEL_EXPORTER_OTLP_ENDPOINT to enable. All other vars are optional.
# Docs: https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/
# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
# OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
# OTEL_EXPORTER_OTLP_HEADERS=
# OTEL_SERVICE_NAME=snapotter-api
# OTEL_TRACES_SAMPLER=parentbased_always_on
# OTEL_TRACES_EXPORTER=otlp