-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path.env.example
More file actions
217 lines (181 loc) · 10.3 KB
/
.env.example
File metadata and controls
217 lines (181 loc) · 10.3 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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# GitHub Store backend — environment template.
# Copy to /opt/github-store-backend/.env on the VPS, fill in real values, and
# `chmod 600`. Never commit this file with real secrets. Placeholders in this
# template are not safe to deploy as-is.
# =====================================================================
# Required (backend refuses to start under APP_ENV=production without these)
# =====================================================================
# Marks this as a production deploy. Drives Sentry environment tag and
# enables the strict env-var validation in Application.kt.
# Read by: Application.kt, InternalRoutes.kt
APP_ENV=production
# JDBC URL for Postgres. In Docker Compose this points at the postgres
# service hostname; for laptop SSH-tunnel admin it's localhost:5432.
# Read by: DatabaseFactory.kt
DATABASE_URL=jdbc:postgresql://postgres:5432/githubstore
# Postgres role the app connects as. Matches POSTGRES_USER in compose.
# Read by: DatabaseFactory.kt
DATABASE_USER=githubstore
# Postgres password. ROTATE BEFORE DEPLOY. Generate via:
# openssl rand -base64 32
# Read by: DatabaseFactory.kt (also injected into postgres container as
# POSTGRES_PASSWORD via docker-compose.prod.yml)
POSTGRES_PASSWORD=<rotate-before-deploy-openssl-rand-base64-32>
# Meilisearch URL. In Docker Compose this is the service hostname.
# Read by: MeilisearchClient.kt
MEILI_URL=http://meilisearch:7700
# Meilisearch master key. ROTATE BEFORE DEPLOY. Generate via:
# openssl rand -hex 32
# Read by: MeilisearchClient.kt (also injected into meilisearch container)
MEILI_MASTER_KEY=<rotate-before-deploy-openssl-rand-hex-32>
# OAuth App client_id for /v1/auth/device/* proxy and the web-flow exchange.
# Must equal the value the KMP client ships in BuildKonfig — both ends of
# the primary→fallback auth flow have to use the same OAuth App so
# device_codes interchange.
# Source: github.com/settings/applications/<app-id>
# Read by: GitHubDeviceClient.kt, OAuthExchangeService (via AppModule)
# (Renamed from GITHUB_OAUTH_CLIENT_ID — old name no longer read.)
OAUTH_CLIENT_ID=<github-oauth-app-client-id>
# OAuth App client_secret for the web-flow code exchange. NEVER commit a
# real value. Distinct from OAUTH_CLIENT_ID — the secret is only ever used
# server-side, and only by the backend (never by the client app or the
# website JS). Source: same GitHub OAuth App settings page.
# Read by: OAuthExchangeService (via AppModule)
OAUTH_CLIENT_SECRET=<github-oauth-app-client-secret>
# Callback URL registered with the OAuth App. Must EXACTLY match what's in
# github.com/settings/applications/<app-id> → "Authorization callback URL".
# Used as redirect_uri on the token exchange; GitHub rejects with
# redirect_uri_mismatch if the values diverge.
# Read by: OAuthExchangeService (via AppModule)
OAUTH_WEB_CALLBACK_URL=https://github-store.org/auth/callback
# Shared secret between this backend and the website (github-store.org).
# Website sends `X-Oauth-Service-Token: <this value>` on every call to
# /v1/oauth/state and /v1/oauth/exchange. Both endpoints refuse the request
# (401 service_auth_required) if missing or wrong. Rotate via:
# openssl rand -base64 48
# Set the SAME value on the website server.
# Read by: OAuthServiceAuth (via AppModule)
OAUTH_SERVICE_TOKEN=<rotate-before-deploy-openssl-rand-base64-48>
# Comma-separated allowlist of Host headers the OAuth S2S endpoints accept.
# Defence-in-depth on top of OAUTH_SERVICE_TOKEN — even if the secret leaks,
# the request still has to land on the canonical vhost. Production must set
# this; an empty value combined with APP_ENV=production refuses every S2S
# OAuth request. If you also serve OAuth via api-direct.github-store.org as
# a fallback vhost, append it: "api.github-store.org,api-direct.github-store.org".
# Read by: OAuthServiceAuth (via AppModule)
OAUTH_SERVICE_ALLOWED_HOSTS=api.github-store.org
# Trusted forge hosts that /v1/external-match is allowed to fan out to
# when the request carries sources containing forge short-names (e.g.
# "codeberg", "gitea"). Comma-separated. Backend NEVER calls hosts outside
# this set even if the request body asks — SSRF + DoS hardening. Unset →
# the canonical default of codeberg.org,gitea.com,git.disroot.org applies.
# Read by: ForgejoSearchClient (via AppModule)
FORGEJO_TRUSTED_HOSTS=codeberg.org,gitea.com,git.disroot.org
# Comma-separated list of project-hosted F-Droid `index-v2.json` URLs that
# ForgejoFdroidSeedWorker pulls daily to seed signing fingerprints for
# forge-distributed APKs. Each `sourceCode` URL in the index is matched
# against FORGEJO_TRUSTED_HOSTS — only packages on trusted hosts land in
# the signing_fingerprint table. Empty → falls back to the canonical
# default (Gadgetbridge's Codeberg pages repo). Add new entries as new
# project-hosted F-Droid repos come online.
# Read by: ForgejoFdroidSeedWorker (via AppModule)
FORGEJO_FDROID_INDEX_URLS=https://freeyourgadget.codeberg.page/fdroid/repo/index-v2.json
# Optional kill switch for the OAuthCleanupWorker. When set to "true", the
# 5-minute DELETE-of-expired-rows loop sleeps each cycle. Request-time
# `expires_at > NOW()` filters keep correctness intact; the only cost is
# unbounded growth of the oauth_ephemeral table while disabled. Use only
# when troubleshooting DB lock contention against /exchange or /handoff.
# Default: unset = worker runs normally.
# Read by: OAuthCleanupWorker
# OAUTH_CLEANUP_DISABLED=true
# =====================================================================
# GitHub token rotation pool (required in production for passthrough)
# =====================================================================
# Backend's quiet-window fallback PAT. Used alone during the daily
# fetcher's quiet hours so the fetcher gets the full pool to itself.
# In compose this is wired to GH_TOKEN_TOPICS by default — pick whichever
# pool token you want as the quiet-window fallback. ROTATE BEFORE DEPLOY.
# Source: github.com/settings/tokens (fine-grained, public_repo read-only)
# Read by: GitHubSearchClient.kt
GITHUB_TOKEN=<github-pat-rotate-before-deploy>
# Rotation pool. Outside the quiet window the backend round-robins across
# these so user traffic doesn't concentrate on any one PAT. Each must be
# a distinct PAT so quotas accumulate. ROTATE BEFORE DEPLOY.
# Read by: GitHubSearchClient.kt
GH_TOKEN_TRENDING=<github-pat-rotate-before-deploy>
GH_TOKEN_NEW_RELEASES=<github-pat-rotate-before-deploy>
GH_TOKEN_MOST_POPULAR=<github-pat-rotate-before-deploy>
GH_TOKEN_TOPICS=<github-pat-rotate-before-deploy>
# =====================================================================
# Optional
# =====================================================================
# HTTP listen port inside the app container. Caddy reverse-proxies to it.
# Read by: Application.kt
PORT=8080
# Hikari max pool size. Default 20 keeps headroom for warm Meili-served
# traffic during cold-query bursts. Read by: DatabaseFactory.kt
DATABASE_POOL_SIZE=20
# Gates /v1/internal/metrics and the /v1/internal/dashboard basic-auth
# password. Under APP_ENV=production internal routes won't even register
# if this is unset. Generate via: openssl rand -hex 32
# Read by: Plugins.kt, InternalRoutes.kt
ADMIN_TOKEN=<rotate-before-deploy-openssl-rand-hex-32>
# Sentry DSN for error reporting. If unset, Sentry init is a no-op
# (fine for local dev). Source: sentry.io project settings.
# Read by: Application.kt
SENTRY_DSN=<sentry-dsn-or-leave-blank>
# =====================================================================
# Workers / token rotation tuning
# =====================================================================
# Quiet-window UTC hours during which the rotation pool is bypassed and
# only GITHUB_TOKEN is used, so the Python fetcher (running its full job
# in this window) gets the whole pool to itself. Default 1–4.
# Move these if the fetcher cron schedule moves.
# Read by: GitHubSearchClient.kt
TOKEN_QUIET_START_UTC=1
TOKEN_QUIET_END_UTC=4
# =====================================================================
# Badges
# =====================================================================
# Static count rendered by the global "users" badge. Bump manually as
# the install base grows. Unset → badge renders with a "—" dash and the
# degraded flag set.
# Read by: BadgeService.kt
BADGE_USER_COUNT=90000
# =====================================================================
# Telemetry / privacy
# =====================================================================
# Pepper mixed into the SHA-256 of device_ids before they hit the events
# table. Treat as a secret. Generate via: openssl rand -hex 32
# Regenerating it invalidates all existing hashed device_ids in the
# events table — acceptable cost, the data is purely analytical.
# Required under APP_ENV=production; the app refuses to start without it.
# Read by: util/PrivacyHash.kt
DEVICE_ID_PEPPER=<generate-via-openssl-rand-hex-32>
# =====================================================================
# Kill switches (set to "true" to engage; default off)
# =====================================================================
# Cuts every outbound GitHub-API call: /search passthrough, /search/explore,
# the badge release fallback, the repo refresh worker. Cached results still
# serve. Use when a token leaks or the rotation pool is being scraped.
# Read by: util/FeatureFlags.kt
DISABLE_LIVE_GITHUB_PASSTHROUGH=
# Narrower switch — only stops the badge service from doing the live GitHub
# fallback for missing release tags. Implied by the broader switch above.
# Read by: util/FeatureFlags.kt
DISABLE_BADGE_FETCH=
# =====================================================================
# Backups (consumed by ops/backup_postgres.sh, not the app)
# =====================================================================
# rclone remote target for off-host backup copies. Format:
# <remote-name>:<bucket-or-path>/
# Example: storage_box:github-store-backups/
# Unset → backup script runs in local-only mode with a warning.
RCLONE_REMOTE=<rclone-remote-or-leave-blank>
# GPG symmetric passphrase used to encrypt dumps before upload. Generate
# via: openssl rand -base64 48. Store in your password manager — losing
# it makes the off-host copies unrecoverable.
BACKUP_GPG_PASSPHRASE=<generate-via-openssl-rand-base64-48-or-leave-blank>
# Optional healthcheck.io ping URL hit on successful backup. Unset →
# script does not ping.
HEALTHCHECK_URL=<healthcheck-io-ping-url-or-leave-blank>