-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path.env.example
More file actions
170 lines (134 loc) · 7.59 KB
/
Copy path.env.example
File metadata and controls
170 lines (134 loc) · 7.59 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
# Deep Reader — environment configuration template.
# Copy to .env and fill in the values before starting the server.
# .env is gitignored; never commit real credentials.
# ---------------------------------------------------------------------------
# HTTP server
# ---------------------------------------------------------------------------
# Port the Go server listens on inside the container.
HTTP_PORT=8080
# ---------------------------------------------------------------------------
# Auth
# ---------------------------------------------------------------------------
# There is no auth token to configure. The single built-in account (username +
# password) is created on first run: open the app and you will be redirected to
# /setup. Credentials are stored in the database (the password as a bcrypt hash).
# All devices sign in with the same account.
# Brute-force protection. The login endpoint is throttled by an in-memory,
# per-IP lockout: after LOGIN_MAX_ATTEMPTS consecutive failures from one client
# IP, that IP is locked out for LOGIN_LOCKOUT_DURATION (the API replies 429 with
# a Retry-After header). A successful login clears the failure streak. State is
# in-memory and resets on restart — fine for a single-user, self-hosted service.
# Consecutive failed logins (per IP) before lockout. Set to 0 to disable.
LOGIN_MAX_ATTEMPTS=5
# Rolling window over which failed logins are counted (Go duration string).
LOGIN_ATTEMPT_WINDOW=15m
# How long an IP stays locked out after reaching LOGIN_MAX_ATTEMPTS.
LOGIN_LOCKOUT_DURATION=15m
# Trust the reverse proxy's X-Forwarded-For header so the per-IP lockout sees
# the real client IP rather than the proxy's. This deployment runs behind a
# host TLS proxy that forwards to 127.0.0.1:8080, so it is enabled by default.
# WARNING: only enable this when actually behind a trusted proxy — if the app is
# directly reachable, a client could spoof X-Forwarded-For to evade the lockout.
TRUST_PROXY=true
# Optional allowlist of trusted proxy IPs/CIDRs (comma-separated). Leave empty
# to trust loopback/private/link-local peers, which covers the documented
# reverse-proxy-on-loopback / Docker setup. Set it explicitly (e.g. the proxy's
# address) to harden when the app is also reachable from other networks.
TRUSTED_PROXIES=
# ---------------------------------------------------------------------------
# Database
# ---------------------------------------------------------------------------
# Absolute path to the SQLite file. Must be on a persistent volume.
DATABASE_PATH=/data/deep-reader.db
# ---------------------------------------------------------------------------
# Public pages
# ---------------------------------------------------------------------------
#
# Publishing an article writes a standalone HTML page (Open Graph metadata plus
# the translated text) to disk and serves it at /p/<token>. The link is
# unguessable and expires after the lifetime configured in Settings > Public
# Pages; expired pages answer 404 and are swept hourly.
# Where the generated pages are stored. Must be on the same persistent volume
# as the database. Defaults to a "public-pages" directory next to DATABASE_PATH.
PUBLIC_PAGES_DIR=/data/public-pages
# Externally reachable origin used to build share links and og:url, e.g.
# https://reader.example. Leave empty to derive it from the request that
# publishes the article, which is correct behind the documented reverse proxy.
PUBLIC_BASE_URL=
# ---------------------------------------------------------------------------
# LLM provider (OpenAI-compatible API)
# ---------------------------------------------------------------------------
#
# The connection — base URL, API key and model — is configured in the UI under
# Settings > LLM: add one or more provider profiles (OpenAI, OpenRouter, local
# Ollama, …) and pick the active one. These are stored on the backend, not in
# env. The knobs below are deployment-level operational limits and stay here.
# Maximum number of concurrent LLM requests (worker pool size).
LLM_MAX_CONCURRENT=2
# Timeout for a single LLM API call (Go duration string, e.g. 60s, 2m).
LLM_REQUEST_TIMEOUT=90s
# Number of retry attempts before marking an article as "failed".
LLM_MAX_RETRIES=1
# Target token-window size for the step-wise enrichment. The article's tokens
# are split into windows of roughly this size (snapped to sentence boundaries)
# and each window is translated by its own bounded LLM call, so a single
# completion is never long enough to be truncated. Lower it if you still see
# truncated/invalid JSON on long articles; raise it to use fewer LLM calls.
LLM_CHUNK_TOKENS=500
# ---------------------------------------------------------------------------
# Ingestion
# ---------------------------------------------------------------------------
# Timeout for fetching and extracting a remote article URL (Go duration string).
# Used by the built-in readability extractor (and the markdown.new fallback).
READABILITY_TIMEOUT=15s
# Version string for the enrichment prompt/schema. Bump this value to trigger
# re-enrichment of existing articles on the next /api/articles/:id/reenrich call.
ENRICHMENT_VERSION=1
# ---------------------------------------------------------------------------
# markdown.new content extraction
# ---------------------------------------------------------------------------
#
# markdown.new (https://markdown.new) converts a URL into clean Markdown that is
# better suited for LLM enrichment than raw HTML. When enabled it is the PRIMARY
# extractor; the built-in readability extractor is the fallback used when
# markdown.new fails OR when the daily request-unit budget is exhausted (so
# adding articles never hard-fails — it just degrades to local extraction).
# Enable markdown.new as the primary extractor: true | false
MARKDOWN_ENABLED=true
# Base URL of the markdown.new service (override for a self-hosted instance).
MARKDOWN_BASE_URL=https://markdown.new
# Timeout for a single conversion (Go duration string). JS-heavy pages rendered
# in a headless browser add latency, so this is generous.
MARKDOWN_TIMEOUT=45s
# Daily request-unit budget (resets at UTC midnight). The free plan grants 500
# units/day per IP. Set to 0 for unlimited (e.g. a self-hosted instance).
MARKDOWN_DAILY_LIMIT=500
# Request units one article conversion costs against MARKDOWN_DAILY_LIMIT. The
# free plan bills a crawl at 50 units, so the conservative default of 50 yields
# ~10 conversions/day. Lower it (e.g. 1) if your plan bills simple conversions
# as a single request.
MARKDOWN_COST_PER_ARTICLE=50
# ---------------------------------------------------------------------------
# Logging
# ---------------------------------------------------------------------------
# Log level: debug | info | warn | error
LOG_LEVEL=info
# Log format: json | text
LOG_FORMAT=json
# ---------------------------------------------------------------------------
# Error tracking (Sentry) — optional
# ---------------------------------------------------------------------------
#
# Reports errors and panics only (no performance tracing). Leave a DSN empty to
# disable that side entirely. Backend and frontend are usually separate Sentry
# projects, hence two DSNs.
# Backend (Go) DSN. Empty disables backend error reporting.
SENTRY_DSN=
# Frontend (browser) DSN. Delivered to the client at runtime via GET /api/config
# (the static PWA is built once and embedded, so the DSN cannot be baked at build
# time). Browser DSNs are public by design — this is NOT a secret. Empty disables
# frontend error reporting.
SENTRY_FRONTEND_DSN=
# Environment tag applied to both backend and frontend events (e.g. production,
# staging). Empty leaves it unset.
SENTRY_ENVIRONMENT=