mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
* fix(enterprise): ship enterprise pkg in prod image, full license features, tracing key fallback docker/Dockerfile: COPY packages/enterprise manifest+src into the production stage. Without it, apps/api's workspace link to @snapotter/enterprise dangles and every import() throws (silently caught), so all 19 enterprise features failed closed (enterprise.active=false) regardless of a valid license. scripts/generate-license.mjs: sync PLAN_FEATURES with packages/enterprise/src/license.ts so a --plan enterprise license unlocks all 19 features (was 8) and team unlocks 8. apps/api/src/tracing.ts: accept SNAPOTTER_LICENSE_KEY as a fallback to LICENSE_KEY so distributed_tracing activates with the same key as the rest of the app. * fix(docker): keep scripts/bake-analytics.mjs in build context .dockerignore excluded the whole scripts/ dir (PR #82, V1 hardening), but docker/Dockerfile later added 'COPY scripts/bake-analytics.mjs' for the analytics bake step. A clean production image build therefore fails with 'scripts/bake-analytics.mjs: not found'. The published image build is gated off in CI so this latent break went unnoticed. Exclude scripts/* but re-include the one file the Dockerfile needs. * fix: S3 upload stream, analytics bake reaches API, dedupe retention field, reconcile orphan jobs storage-s3.ts: wrap the upload AsyncIterable in Readable.from() so @aws-sdk/lib-storage accepts it. STORAGE_MODE=s3 file uploads failed with 'Body Data is unsupported format' for every tool because a bare async generator is not a Readable. docker/Dockerfile: COPY the builder-baked analytics baked.ts into the API runtime stage. The API re-copied the committed (off) baked.ts from the build context, so the SNAPOTTER_ANALYTICS build arg had no effect on the API -- and since the SPA reads /api/v1/config/analytics, analytics was off everywhere regardless of the arg. settings-dialog.tsx: remove the duplicate tempFileMaxAgeHours control under Data Retention; it bound the same setting key as the File Management control with a different default, so editing either silently overwrote the other. apps/api/src/index.ts: reconcile orphaned job rows (empty tool_id, never enqueued to BullMQ) at boot so they don't sit in processing/queued forever and inflate the per-user concurrent-job count and the upgrade-check in-flight gate. * fix(web): style the SSO login buttons (they referenced undefined theme tokens) The OIDC/SAML 'Sign in with <provider>' buttons used bg-secondary / text-secondary-foreground, which the web theme never defines (it has primary, background, foreground, muted, border, card, primary-subtle). Those classes resolved to nothing, so the buttons rendered as bare unstyled text on the login page. Restyle: the optional (non-enforced) buttons become white-card outline buttons with a key icon and an orange hover tint, secondary to the primary Login button; the SSO-enforced buttons become solid primary with the icon. * fix: gate S3 behind license, custom-role enterprise perms, wire retention UI, cleanup S3 is a licensed feature, but shipping packages/enterprise in every image removed the implicit gate, so STORAGE_MODE=s3 worked without a license. Enforce isFeatureEnabled('s3_storage') at boot and fail fast if unlicensed. Custom roles can now be granted security:manage / compliance:manage / webhooks:manage (roles.ts ALL_PERMISSIONS + the Roles UI) so admins can build least-privilege compliance/security roles instead of only the built-in admin role. retentionSweep now reads the jobsRetentionDays / auditRetentionDays DB settings the System Settings UI writes (env vars become the fallback default), mirroring how the temp-file sweep reads tempFileMaxAgeHours. Previously those two UI controls were no-ops. Cleanup: drop the never-set snapotter_storage_bytes gauge and the unused MAX_WORKSPACE_SIZE_GB env var; emit tool_client_error to PostHog from the web ErrorBoundary (client crashes were not reaching analytics); add the Python OpenTelemetry packages so the innermost sidecar.<script> span exports; fix the stale 'only local storage' line in the docs; delete two e2e-analytics specs that tested the removed consent UI. * fix(env): restore MAX_WORKSPACE_SIZE_GB default security-auth-hardening.test.ts asserts env.MAX_WORKSPACE_SIZE_GB defaults to 10, so the var is an intentional (tested) default, not dead code. Removing it in the cleanup commit broke that unit test. Keep the declaration.
6.4 KiB
6.4 KiB
description
| description |
|---|
| All SnapOtter environment variables with defaults. Configure auth, storage, AI models, analytics, and more. |
Configuration
All configuration is done through environment variables. Every variable has a sensible default, so SnapOtter works out of the box without setting any of them.
Environment variables
Server
| Variable | Default | Description |
|---|---|---|
PORT |
1349 |
Port the server listens on. |
RATE_LIMIT_PER_MIN |
0 (disabled) |
Maximum requests per minute per IP. Set to 0 to disable rate limiting. |
CORS_ORIGIN |
(empty) | Comma-separated allowed origins for CORS, or empty for same-origin only. |
LOG_LEVEL |
info |
Log verbosity. One of: fatal, error, warn, info, debug, trace. |
TRUST_PROXY |
true |
Trust X-Forwarded-For headers from a reverse proxy. Set to false if not behind a proxy. |
Authentication
| Variable | Default | Description |
|---|---|---|
AUTH_ENABLED |
false |
Set to true to require login. The Docker image defaults to true. |
DEFAULT_USERNAME |
admin |
Username for the initial admin account. Only used on first run. |
DEFAULT_PASSWORD |
admin |
Password for the initial admin account. Change this after first login. |
MAX_USERS |
0 (unlimited) |
Maximum number of registered user accounts. Set to 0 for unlimited. |
SESSION_DURATION_HOURS |
168 |
Login session lifetime in hours (default is 7 days). |
SKIP_MUST_CHANGE_PASSWORD |
- | Set to any non-empty value to bypass the forced password-change prompt on first login |
Storage
| Variable | Default | Description |
|---|---|---|
STORAGE_MODE |
local |
local or s3. S3/MinIO requires a license with the s3_storage feature. |
DATABASE_URL |
postgres://snapotter:snapotter@postgres:5432/snapotter |
PostgreSQL connection string. |
REDIS_URL |
redis://redis:6379 |
Redis connection string (used for BullMQ job queues). |
WORKSPACE_PATH |
./tmp/workspace |
Directory for temporary files during processing. Cleaned up automatically. |
FILES_STORAGE_PATH |
./data/files |
Directory for persistent user files (uploaded images, saved results). |
Processing limits
| Variable | Default | Description |
|---|---|---|
MAX_UPLOAD_SIZE_MB |
0 (unlimited) |
Maximum file size per upload in megabytes. Set to 0 for unlimited. |
MAX_BATCH_SIZE |
0 (unlimited) |
Maximum number of files in a single batch request. Set to 0 for unlimited. |
CONCURRENT_JOBS |
0 (auto) |
Number of batch jobs that run in parallel. Set to 0 to auto-detect based on available CPU cores. |
MAX_MEGAPIXELS |
0 (unlimited) |
Maximum image resolution allowed in megapixels. Set to 0 for unlimited. |
MAX_WORKER_THREADS |
0 (auto) |
Maximum worker threads for image processing. Set to 0 to auto-detect based on available CPU cores. |
PROCESSING_TIMEOUT_S |
0 (no limit) |
Maximum processing time per request in seconds. Set to 0 for no timeout. |
MAX_PIPELINE_STEPS |
0 (no limit) |
Maximum number of steps in a pipeline. Set to 0 for no limit. |
MAX_CANVAS_PIXELS |
0 (no limit) |
Maximum canvas size in pixels for output images. Set to 0 for no limit. |
MAX_SVG_SIZE_MB |
0 (unlimited) |
Maximum SVG file size in megabytes. Set to 0 for unlimited. |
MAX_SPLIT_GRID |
100 |
Maximum grid dimension for the image split tool. |
MAX_PDF_PAGES |
0 (unlimited) |
Maximum number of PDF pages for PDF-to-image conversion. Set to 0 for unlimited. |
Cleanup
| Variable | Default | Description |
|---|---|---|
FILE_MAX_AGE_HOURS |
72 |
How long unsaved processing results (raw uploads and tool outputs) are kept before automatic deletion. Files you explicitly save to the Files library are not affected and persist until you delete them. |
CLEANUP_INTERVAL_MINUTES |
60 |
How often the cleanup job runs. |
Appearance
| Variable | Default | Description |
|---|---|---|
DEFAULT_THEME |
light |
Default theme for new sessions. light or dark. |
DEFAULT_LOCALE |
en |
Default interface language. |
DEFAULT_TOOL_VIEW |
sidebar |
Default tool layout. sidebar or fullscreen. |
Docker permissions
| Variable | Default | Description |
|---|---|---|
PUID |
999 |
Run the container process as this UID. Set to match your host user for bind mounts (id -u). |
PGID |
999 |
Run the container process as this GID. Set to match your host group for bind mounts (id -g). |
Docker example
services:
SnapOtter:
image: snapotter/snapotter:latest
ports:
- "1349:1349"
volumes:
- SnapOtter-data:/data
- SnapOtter-workspace:/tmp/workspace
environment:
- AUTH_ENABLED=true
- DEFAULT_USERNAME=admin
- DEFAULT_PASSWORD=changeme
- DATABASE_URL=postgres://snapotter:snapotter@postgres:5432/snapotter
- REDIS_URL=redis://redis:6379
- MAX_UPLOAD_SIZE_MB=200
- CONCURRENT_JOBS=4
- FILE_MAX_AGE_HOURS=12
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
postgres:
image: postgres:17-alpine
environment:
POSTGRES_USER: snapotter
POSTGRES_PASSWORD: snapotter
POSTGRES_DB: snapotter
volumes:
- SnapOtter-pgdata:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U snapotter"]
interval: 10s
timeout: 5s
retries: 12
redis:
image: redis:8-alpine
command: ["redis-server", "--maxmemory-policy", "noeviction", "--appendonly", "yes"]
volumes:
- SnapOtter-redisdata:/data
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 12
volumes:
SnapOtter-data:
SnapOtter-workspace:
SnapOtter-pgdata:
SnapOtter-redisdata:
Volumes
The Docker Compose stack uses four volumes:
/data(app) - AI models, Python venv, and user files. Mount this to keep uploaded files and installed AI bundles across restarts./tmp/workspace(app) - Temporary storage for files being processed. This can be ephemeral, but mounting it avoids filling up the container's writable layer.SnapOtter-pgdata(postgres) - PostgreSQL data directory. This holds all relational data (users, settings, pipelines, jobs, audit log). Back up viapg_dumpor volume snapshot.SnapOtter-redisdata(redis) - Redis append-only file for durable job queues.