-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path.env.example
More file actions
192 lines (166 loc) · 10.2 KB
/
.env.example
File metadata and controls
192 lines (166 loc) · 10.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# FileMorph Configuration
# Copy this file to .env and adjust values for your deployment.
#
# This file is structured top-down: required for every deployment first,
# then Cloud-overlay (account / payment / email features), then Compliance-
# Edition tunables, then operational knobs. A Community-Edition self-host
# only needs the first section.
#
# ──────────────────────────────────────────────────────────────────────
# === Required for every deployment ===
# ──────────────────────────────────────────────────────────────────────
# Server bind. APP_HOST=0.0.0.0 makes the app listen on every interface
# inside the container; the reverse proxy (Caddy/nginx) is what limits
# external reach. APP_DEBUG=true enables FastAPI's interactive docs at
# /docs and verbose tracebacks — leave off in production.
APP_HOST=0.0.0.0
APP_PORT=8000
APP_DEBUG=false
# Path to the JSON file holding hashed API keys for anonymous-tier
# clients. The file is created on first key generation; bind-mount the
# parent directory into your container if you want keys to survive
# restarts.
API_KEYS_FILE=data/api_keys.json
# Maximum HTTP request body size accepted by the upload endpoints.
# 100 MB is a sane default that fits typical convert/compress workloads
# without inviting OOM on small hosts. Operators with bigger files raise
# this; tier quotas (anonymous/free/pro/business) still apply on top via
# app/core/quotas.py.
MAX_UPLOAD_SIZE_MB=100
# CORS: comma-separated list of allowed origins. Production deployments
# behind a real domain list explicit origins (e.g.
# `https://files.example.com,https://www.files.example.com`) so a
# malicious page on a different domain can't talk to your API. Avoid `*`
# — the middleware refuses to combine `*` with `allow_credentials=true`,
# and same-origin dev works fine with the localhost default below.
CORS_ORIGINS=http://localhost:8000
# Public canonical URL of the deployment. Used by the canonical/og:url
# meta tags, the sitemap, and the JSON-LD structured data. Localhost is
# fine for dev; set to your domain for production so search engines
# index the right URLs.
APP_BASE_URL=http://localhost:8000
# Optional: route heavy upload POSTs (convert/compress, single + batch)
# through a separate subdomain. Empty string = same-origin (default,
# simplest). Set this only when the main site sits behind a proxy that
# caps request bodies and uploads must bypass it via a tunnel
# subdomain. See docs/self-hosting.md for CORS implications.
API_BASE_URL=
# ──────────────────────────────────────────────────────────────────────
# === Cloud-overlay (optional — accounts, payments, transactional email) ===
# ──────────────────────────────────────────────────────────────────────
# A Community Edition deployment can leave everything below empty.
# Setting any of these activates the corresponding sub-processor (see
# docs/sub-processors.md). The application disables each feature
# automatically when its primary key/url is empty — no further toggle
# needed.
# JWT signing secret. Required for the user-account features
# (registration, login, refresh, role checks). Must be at least 32
# characters; rotate by changing this value (all sessions invalidate
# on the next request).
JWT_SECRET=dev-secret-change-me-min-32-chars-long
# PostgreSQL DSN for the Cloud overlay (users, api_keys, file_jobs,
# audit_events tables). Use the asyncpg driver. Empty string disables
# the database completely — registration/login routes return 503.
# DATABASE_URL=postgresql+asyncpg://user:pass@host:5432/filemorph
# Stripe (leave empty to disable billing). When present, the /pricing
# page shows live upgrade buttons gated behind the BGB §356 (5)
# withdrawal-waiver checkbox; without these values the page renders a
# "Coming Soon" banner.
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
STRIPE_PRO_PRICE_ID=
STRIPE_BUSINESS_PRICE_ID=
# Whether to expose /pricing as a commercial offer surface at all.
# Self-hosted Community deployments leave this off — there is no
# commercial offer to advertise. Set to `true` only on a SaaS
# deployment that has (or will soon have) Stripe configured.
PRICING_PAGE_ENABLED=false
# Transactional email (password-reset, billing receipts, account
# verification, deletion confirmation, dunning). Empty SMTP_HOST disables
# outgoing mail; the routes that need it (forgot-password, register)
# return a graceful 503. Variable names must match what `app/core/config.py`
# reads (pydantic-settings is case-insensitive but the *stem* matters).
# SMTP_HOST=smtp.zoho.eu
# SMTP_PORT=587
# SMTP_USERNAME=no-reply@example.com
# SMTP_PASSWORD=
# SMTP_FROM_EMAIL=no-reply@example.com
# SMTP_FROM_NAME=FileMorph
# SMTP_REPLY_TO=hallo@example.com
# Recipient inbox for the public /contact form (the second contact
# channel linked from the German Impressum, DDG §5). Falls back to
# SMTP_REPLY_TO → SMTP_FROM_EMAIL. Empty everywhere → the form still
# renders but submissions are logged and dropped (same no-op as
# transactional mail when SMTP_HOST is empty).
# CONTACT_FORM_RECIPIENT_EMAIL=
# Default UI / email language when the request locale can't be detected
# (anonymous endpoints, cron-driven mails like dunning). Allowed: `de`,
# `en`. Logged-in users override this via their preferred_lang
# (see PR-i18n-3).
LANG_DEFAULT=de
# RFC 9116 / security.txt contact. Surfaces in /.well-known/security.txt
# and the /security page; receives coordinated-disclosure reports.
SECURITY_CONTACT_EMAIL=security@filemorph.io
# ──────────────────────────────────────────────────────────────────────
# === Compliance-Edition tunables (audit log, retention, output integrity) ===
# ──────────────────────────────────────────────────────────────────────
# These knobs target operators in regulated environments (DACH
# Behörden, healthcare, legal). The defaults below are Cloud-edition
# safe (fire-and-forget audit, no retention beyond the request);
# Compliance customers tighten them per their privacy/audit policy.
# NEU-B.1 — Compliance-Edition tamper-evident audit log gate. Default
# off (Cloud-edition: failures are logged at WARNING and never break
# the request). Set to `true` for ISO 27001 A.12.4.1 / BORA §50 /
# BeurkG §39a compliance — the convert/compress route then refuses
# to serve a result it could not log to audit_events. Requires
# DATABASE_URL above.
AUDIT_FAIL_CLOSED=false
# NEU-B.2 — Retention policy in hours. Cloud edition is zero-retention
# by design (every conversion flushes its temp dir on completion or
# failure; no S3/R2 storage layer is active). This knob is informational
# for self-hosters running a future storage-key-backed pipeline
# (FileJob.expires_at). Compliance-edition operators with an
# eDiscovery / GoBD retention requirement set this to the value their
# privacy policy declares; Cloud / Community keep it at 0.
RETENTION_HOURS=0
# ──────────────────────────────────────────────────────────────────────
# === Operational knobs (sweep cadence, concurrency, metrics) ===
# ──────────────────────────────────────────────────────────────────────
# S10-lite analytics — per-day counters for page views, conversions,
# registrations, and failures. Visible at /cockpit (admin-only).
# Counters are aggregates, not personal data — no cookie banner needed.
# Default on; set to `false` to leave the daily_metrics table empty
# (the cockpit Analytics tab then shows an empty-state notice).
METRICS_ENABLED=true
# Background sweep that removes orphaned `fm_*` temp dirs left behind
# by crashes mid-conversion. The request path always cleans its own
# temp dir in a `finally` block, so this only catches crash-recovery
# cases. Set to 0 to disable the periodic sweep (the startup sweep
# still runs once on boot regardless).
TEMP_SWEEP_INTERVAL_MINUTES=60
# How old (minutes) an `fm_*` temp dir must be before the sweep
# removes it. Larger than the longest plausible single conversion;
# smaller windows risk pulling out a still-running job's tempdir.
# Operators with very long batch pipelines raise this to match.
TEMP_SWEEP_MAX_AGE_MINUTES=10
# P3-4 — Pillow decompression-bomb cap (in megapixels). Pillow ships
# with a ~89 MP warning threshold; FileMorph promotes that warning to
# a hard error at startup (see app/core/image_hardening.py). Above
# the configured value, /convert and /compress return HTTP 400 with
# `X-FileMorph-Error-Code: decompression_bomb`. Raise this only if
# you have an explicit large-image use case (GIS, scans, microscopy).
# Garbage / out-of-range values fall back to the default rather than
# refusing to boot. Valid range: 1 .. 10 000.
FILEMORPH_IMAGE_MAX_MEGAPIXELS=89
# NEU-D.1 — global concurrency cap across all callers. Default 4 is
# sized for a 4 GB host with the existing per-tier output caps; raise
# to roughly CPU-count on a bigger box. Past the cap, requests get
# 503 + Retry-After.
MAX_GLOBAL_CONCURRENCY=4
# How long a request waits for a free slot before giving up. Small
# enough that callers fail fast under saturation, big enough to
# absorb sub-second jitter when two requests race for the same slot.
CONCURRENCY_ACQUIRE_TIMEOUT_SECONDS=0.5
# Value sent in the Retry-After response header when a slot is
# denied. Should match the typical drain time of a saturated pool.
CONCURRENCY_RETRY_AFTER_SECONDS=5