-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy path.env.example
More file actions
393 lines (324 loc) · 19.4 KB
/
Copy path.env.example
File metadata and controls
393 lines (324 loc) · 19.4 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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
# =============================================================================
# PRODUCTION ENVIRONMENT VARIABLES
# =============================================================================
#
# Copy this file to .env and fill in the values for your production deployment.
#
# Usage:
# docker compose -f compose.proxy.yaml -f compose.core.yaml -f compose.app.yaml up -d
#
# =============================================================================
# -----------------------------------------------------------------------------
# GENERAL
# -----------------------------------------------------------------------------
# Hostname for the application (e.g., hephaestus.cit.tum.de)
APP_HOSTNAME=
# Release to deploy, or the full commit SHA for a deploy that tracks main. Every image in the stack
# is pulled at this tag, and the agent sandbox image is resolved from it too, so the whole
# deployment is one matched build. Never a tag that moves between builds — `latest`, or a partial
# version like `0.74` that every patch release retags — because it pairs a server with an agent image
# nobody built it against, and the boot is refused. See ADR 0031.
IMAGE_TAG=0.74.0
# Tomcat RemoteIpValve internal-proxies REGEX (NOT CIDR) matching ONLY the ingress (Traefik) address.
# REQUIRED in prod: forward-headers-strategy=native is active and ProxyTrustGuard fails the boot if
# blank (Boot's default trusts all of RFC-1918, letting a client spoof X-Forwarded-For and defeat the
# pre-auth IP rate limit). Find Traefik's address with `docker network inspect shared-network` and
# match its /24, e.g. 172\.18\.0\.\d+. The bridge subnet is assigned dynamically unless pinned, so
# re-verify after recreation (or pin a subnet / static Traefik IP to keep this value stable).
HEPHAESTUS_TRUSTED_PROXIES=
# -----------------------------------------------------------------------------
# AUTHENTICATION (Hephaestus-native — no Keycloak; ADR 0017)
# -----------------------------------------------------------------------------
# Base64-encoded 32-byte AES-256 key sealing the short-lived OAuth state cookies.
# REQUIRED in deployed environments. Generate with: openssl rand -base64 32
HEPHAESTUS_AUTH_STATE_COOKIE_KEY=
# GitHub OAuth App (https://github.com/settings/developers).
# Callback: https://<hostname>/api/login/oauth2/code/github
# Named GH_OAUTH_* (not GITHUB_OAUTH_*) because GitHub Actions reserves the GITHUB_ prefix for
# secret/variable names; compose maps these onto the GITHUB_OAUTH_* the server actually reads.
GH_OAUTH_CLIENT_ID=
GH_OAUTH_CLIENT_SECRET=
# GitLab OAuth application (gitlab.com or self-hosted; scope: read_user — OAuth2 flow, not OIDC).
# Callback: https://<hostname>/api/login/oauth2/code/gitlab. Leave the client id blank to disable.
GITLAB_OAUTH_CLIENT_ID=
GITLAB_OAUTH_CLIENT_SECRET=
# Instance the login federates to (defaults to https://gitlab.com; e.g. https://gitlab.lrz.de) and
# the login-button label (defaults to GitLab).
GITLAB_OAUTH_BASE_URL=https://gitlab.com
GITLAB_OAUTH_DISPLAY_NAME=GitLab
# First instance super-admin (lockout-safe; promoted on login). Comma-separated <provider>:@<username>
# or <provider>:<subject>, e.g. github:@octocat,gitlab:@m.mustermann. See docs/runbooks/auth-cutover.md.
# HEPHAESTUS_AUTH_BOOTSTRAP_ADMINS=
# Optional one-time break-glass token enabling POST /auth/bootstrap-admin while no admin exists.
# HEPHAESTUS_AUTH_BOOTSTRAP_TOKEN=
# Default GitLab instance for SCM integration / workspace creation (not auth).
# GITLAB_DEFAULT_SERVER_URL=https://gitlab.lrz.de
# -----------------------------------------------------------------------------
# GITHUB INTEGRATION
# -----------------------------------------------------------------------------
# GitHub App credentials
# Create at: https://github.com/settings/apps
GH_APP_ID=
GH_APP_PRIVATE_KEY=
# Alternative: path to private key file
# GH_APP_PRIVATE_KEY_LOCATION=/path/to/private-key.pem
# Install URL shown in the workspace creation wizard
GH_APP_INSTALLATION_URL=
# GitHub personal access token (for API operations)
GH_AUTH_TOKEN=
# Webhook secret for GitHub and GitLab webhooks (min 32 chars; generate with: `openssl rand -hex 32`)
WEBHOOK_SECRET=
# AES-256-GCM key for credentials encrypted at rest in the `connection` table.
# EXACTLY 32 bytes — i.e. 32 ASCII characters. A non-ASCII character costs more than one byte,
# so a 32-character passphrase with an umlaut in it is rejected. Required in prod (the app fails
# fast without it, and the Liquibase backfill re-encrypts existing credentials on first boot).
# Keep it stable — rotating it
# requires re-encrypting every connection row. Generate: `openssl rand -base64 24 | cut -c1-32`
HEPHAESTUS_SECURITY_ENCRYPTION_KEY=
HEPHAESTUS_SECURITY_CREDENTIAL_ENCRYPTION_KEY=
# -----------------------------------------------------------------------------
# NATS - Message Queue
# -----------------------------------------------------------------------------
NATS_ENABLED=true
# Required. Generate independent random values; do not reuse an application key. Keep a letter in
# each: the broker's config file reads an all-digit credential as a number and refuses to start.
NATS_USERNAME=
NATS_PASSWORD=
NATS_DURABLE_CONSUMER_NAME=hephaestus-consumer
# Container resource ceilings
#APPLICATION_SERVER_CPUS=4.0
#APPLICATION_SERVER_PIDS_LIMIT=512
#APPLICATION_WORKER_CPUS=4.0
#APPLICATION_WORKER_PIDS_LIMIT=512
#WEBHOOK_SERVER_CPUS=2.0
#WEBHOOK_SERVER_PIDS_LIMIT=256
# How long a durable consumer may go with nothing CONNECTED to it before JetStream deletes it — not
# how long it goes without traffic. Set 0s on a deployment that may be offline longer than this and
# must resume exactly where it left off; between 0s and 1h is rejected at startup.
HEPHAESTUS_INTEGRATION_CONSUMER_INACTIVE_THRESHOLD=30d
# What the broker may hold for ALL webhook streams together, in bytes. One value sets both the
# broker's own JetStream budget and the bound the server checks its stream limits against. Keep it
# below the free space on the nats-data volume. Replaces NATS_JS_MAX_FILE, which was a size string
# and is no longer read — see MIGRATION.md.
NATS_JS_MAX_FILE_BYTES=17179869184
# Largest single message the broker will carry. Must be at least what the receiver accepts
# (hephaestus.webhook.http.max-payload-bytes, 25 MiB) — anything above it is verified, admitted and
# then lost at publish. NATS' own default is 1MB, which is far below that.
NATS_MAX_PAYLOAD_BYTES=26214400
# How long a webhook stream keeps a delivery, and the disk ceiling under that. MAX_AGE is the
# ceiling; MAX_BYTES is the floor under it, and which of the two binds is a function of your volume.
# Read your own answer off `webhook.stream.oldest.message.age` rather than inferring it. The four
# streams' ceilings must total under NATS_JS_MAX_FILE_BYTES or the receiver refuses to start.
# https://docs.hephaestus.build/admin/webhook-ingestion-operations
HEPHAESTUS_WEBHOOK_STREAM_MAX_AGE=180d
HEPHAESTUS_WEBHOOK_STREAM_MAX_BYTES=1GB
HEPHAESTUS_WEBHOOK_STREAM_MAX_BYTES_GITHUB=10GB
# Startup keeps an existing stream's limits in step with configuration, but withholds any change
# that would delete messages the stream already holds and logs what it would cost. Set true for one
# deliberate start-up to apply it — for example when first bounding a stream that has already grown
# past the new limit.
HEPHAESTUS_WEBHOOK_STREAM_ALLOW_DESTRUCTIVE_LIMIT_UPDATES=false
# How long one stream limit change may take. A bound that sheds messages deletes them before the
# broker answers, so raise this on a stream too large to shed inside five minutes.
HEPHAESTUS_WEBHOOK_STREAM_LIMIT_UPDATE_TIMEOUT=5m
# -----------------------------------------------------------------------------
# MONITORING & SYNC
# -----------------------------------------------------------------------------
# Timeframe in days for monitoring data
MONITORING_TIMEFRAME=14
# Run sync on application startup
MONITORING_RUN_ON_STARTUP=false
# Cron expression for sync schedule. Unset, the application syncs hourly; the value below moves that
# to 2 AM daily.
MONITORING_SYNC_CRON=0 0 2 * * *
# Cooldown between syncs in minutes
MONITORING_SYNC_COOLDOWN_IN_MINUTES=5
# Backfill settings
MONITORING_BACKFILL_ENABLED=true
MONITORING_BACKFILL_BATCH_SIZE=50
MONITORING_BACKFILL_RATE_LIMIT_THRESHOLD=500
MONITORING_BACKFILL_INTERVAL_SECONDS=60
# -----------------------------------------------------------------------------
# LEADERBOARD NOTIFICATIONS
# -----------------------------------------------------------------------------
# Global kill-switch for the Slack leaderboard digest. The target channel/team are
# configured per-workspace on the Slack Connection, not here.
LEADERBOARD_NOTIFICATION_ENABLED=false
# Schedule: day is an integer 1-7 (ISO-8601, Mon=1 … Sun=7); time is HH[:MM].
LEADERBOARD_SCHEDULE_DAY=2
LEADERBOARD_SCHEDULE_TIME=09:00
# -----------------------------------------------------------------------------
# SLACK INTEGRATION
#
# Per-workspace OAuth (admin connects from Settings > Integrations); bot tokens
# are encrypted at rest in the connection table. Events/interactivity are
# verified on webhook-server with the app signing secret.
# -----------------------------------------------------------------------------
HEPHAESTUS_INTEGRATION_SLACK_ENABLED=false
HEPHAESTUS_INTEGRATION_SLACK_CLIENT_ID=
HEPHAESTUS_INTEGRATION_SLACK_CLIENT_SECRET=
HEPHAESTUS_INTEGRATION_SLACK_REDIRECT_URI=
HEPHAESTUS_INTEGRATION_SLACK_SIGNING_SECRET=
# -----------------------------------------------------------------------------
# OUTLINE INTEGRATION
#
# Per-workspace connection (admin pastes the wiki URL + an API key in Settings >
# Integrations); the key is encrypted at rest in the connection table. Outline's
# webhook secret is per-subscription — generated when Hephaestus registers the
# subscription and stored on that same connection row — so there is NO global
# signing secret env var here (unlike Slack).
#
# The ENABLED flag gates every Outline bean (matchIfMissing=false). Compose sets
# it on application-server, application-worker (which builds the agent context and
# would otherwise ship it without wiki documents) and webhook-server (which would
# otherwise answer 501 to every Outline delivery). One value drives all three.
#
# The OAUTH block below is separate and optional: it seeds a LINK-ONLY login
# provider that lets a member attach their Outline identity to their Hephaestus
# account (there is no "sign in with Outline"). Register the OAuth app in Outline
# under Settings > Applications with the exact redirect URI
# https://<host>/api/login/oauth2/code/outline and scope `read`.
# A blank OUTLINE_OAUTH_CLIENT_ID leaves the provider unseeded.
#
# OUTLINE_OAUTH_BASE_URL is REQUIRED whenever OUTLINE_OAUTH_CLIENT_ID is set: it is
# the wiki's own origin (Outline is self-hosted, so there is no default like
# github.com/slack.com to fall back on). Blank or invalid, the seed is SKIPPED with
# an ERROR in the log — the boot succeeds and the account-linking button is
# simply never there, which is easy to miss.
# -----------------------------------------------------------------------------
HEPHAESTUS_INTEGRATION_OUTLINE_ENABLED=false
# Comma-separated, operator-approved HTTPS origins. Empty blocks Outline use.
HEPHAESTUS_INTEGRATION_OUTLINE_ALLOWED_ORIGINS=
# e.g. https://wiki.example.com — required if OUTLINE_OAUTH_CLIENT_ID is set.
OUTLINE_OAUTH_BASE_URL=
OUTLINE_OAUTH_DISPLAY_NAME=Outline
OUTLINE_OAUTH_CLIENT_ID=
OUTLINE_OAUTH_CLIENT_SECRET=
# -----------------------------------------------------------------------------
# OBSERVABILITY
# -----------------------------------------------------------------------------
# Sentry error tracking
SENTRY_DSN=
SENTRY_ENVIRONMENT=production
# Enable TanStack Devtools in the webapp (keep false in production)
TANSTACK_DEVTOOLS_ENABLED=false
# -----------------------------------------------------------------------------
# LEGAL
# -----------------------------------------------------------------------------
# Selects the bundled legal profile served at /imprint and /privacy.
# tumaet — legal identity of TU München / AET (used by the canonical
# upstream deployment at hephaestus.aet.cit.tum.de).
# (empty)— no profile selected; the built-in safety-fallback is shown
# instead and makes clear that the operator still owes a
# deployment-specific imprint (§ 5 DDG) and privacy statement
# (Art. 13 GDPR).
# To ship custom legal text, prefer mounting deployment-specific Markdown
# files into the webapp container at /usr/share/nginx/html/legal-overrides/.
# Valid values: lowercase alphanumeric, `-`, `_` (max 32 chars). Invalid
# values log a WARN at container startup and fall through to the disclaimer.
# See docs/admin/legal-pages.mdx for the full configuration guide.
LEGAL_PROFILE=
# -----------------------------------------------------------------------------
# GITLAB INTEGRATION
# -----------------------------------------------------------------------------
# Enable GitLab beans (sync, webhooks, workspace creation)
GITLAB_ENABLED=false
# Enable GitLab workspace creation in the UI.
# This only works if GITLAB_ENABLED=true and a GitLab login provider is configured.
GITLAB_WORKSPACE_CREATION=false
# -----------------------------------------------------------------------------
# AGENT SANDBOX (Practice Review)
# -----------------------------------------------------------------------------
# Agent sandbox image. Left unset it follows IMAGE_TAG above on a deploy tracking main, and the
# release's cosign-verified digest on a release deploy — so setting it is an override of both, for
# when neither names the image you mean. A digest is immutable and therefore reproducible; never a
# release channel, which names whatever released most recently. See docs/admin/agent-image-digests.md.
# HEPHAESTUS_AGENT_IMAGE_REFERENCE=ghcr.io/hephaestus-build/agent-pi@sha256:<digest>
# A deploy that tracks main rather than a release sets BOTH of these: there is no signed pin asset
# for a main commit, so the fetcher must be told to skip it and the digest requirement must come off.
# The derived reference is still a matched pair, just a tag rather than a digest.
# HEPHAESTUS_RELEASE_PIN_SKIP=true
# HEPHAESTUS_AGENT_IMAGE_REQUIRE_DIGEST=false
# The sandbox is activated by the worker role (hephaestus.runtime.worker.enabled, default true
# in the monolith), not a toggle. Practice review additionally needs AGENT_ENABLED and
# GIT_CHECKOUT_ENABLED enabled together. The agent job queue runs on PostgreSQL, not NATS —
# NATS_ENABLED below is unrelated to practice review; it only gates webhook-driven sync ingest.
# Supplementary group the application server and worker join so they can reach the Docker socket
# the agent sandbox runs containers through. The image runs unprivileged and the socket is
# root:docker 0660, so without this every sandbox start fails with "permission denied" and no
# practice review can run. There is no portable default — read the host's group id with
# `getent group docker | cut -d: -f3`.
# DOCKER_GROUP_ID=999
# Docker daemon endpoint (default: local socket)
# SANDBOX_DOCKER_HOST=unix:///var/run/docker.sock
# OCI runtime: leave empty for runc (default), set to "runsc" for gVisor
# SANDBOX_CONTAINER_RUNTIME=
# Max concurrent agent containers
# SANDBOX_MAX_CONCURRENT=5
# Resource limits per agent container
# SANDBOX_MEMORY_BYTES=4294967296
# SANDBOX_CPUS=2.0
# Enable the agent job queue (required for practice review). The queue runs on PostgreSQL;
# NATS is only needed for webhook/sync ingest, not for this.
AGENT_ENABLED=false
# Which sources practice review may read, and for which purpose, is fixed by the reviewed decisions
# shipped in the artifact-source contract, not by this file. A source you have not connected is
# simply never captured. See docs/contributor/artifact-source-contract.mdx.
HEPHAESTUS_FABRIC_GC_RETENTION_DAYS=30
# Poll interval and claim batch size for the PostgreSQL-backed job queue (defaults shown).
# AGENT_POLL_INTERVAL=1s
# AGENT_CLAIM_BATCH_SIZE=5
# AGENT_MAX_RETRIES=5
GIT_CHECKOUT_ENABLED=false
# Enable practice review for all users.
# In compose this drives both the feature flag and the actual review gate.
PRACTICE_REVIEW_FOR_ALL=false
# Practice review behavior overrides (defaults shown). Auto-trigger and draft handling are not here:
# auto-trigger is a per-workspace setting in the admin UI, and whether a draft occasions a review is
# stated by each practice's own occasions.
# PRACTICE_REVIEW_DELIVER_TO_MERGED=false
# PRACTICE_REVIEW_COOLDOWN_MINUTES=15
# How many reviews one person may ask for by hand in an hour.
# PRACTICE_REVIEW_MAX_REQUESTS_PER_REQUESTER_PER_HOUR=5
# Whether a review says on the work that it is running, and whether it reacts to the comment that
# asked for it. Both are visible to everyone who can see the work.
# PRACTICE_REVIEW_PROGRESS_FOOTER=false
# PRACTICE_REVIEW_REACTION_SUPPRESSION=false
# Spend guardrails for reviewing past work. A campaign is priced and confirmed before it runs; these
# bound the largest one an administrator can confirm, whatever the estimate says.
# PRACTICE_REVIEW_BACKFILL_MAX_WINDOW=400d
# PRACTICE_REVIEW_BACKFILL_MAX_ARTIFACTS=5000
# PRACTICE_REVIEW_BACKFILL_BATCH_SIZE=25
# PRACTICE_REVIEW_BACKFILL_COST_HISTORY_WINDOW=90d
# A review opportunity is recorded the moment it arises and settled when the review finishes. These
# bound how long one may sit unsettled before it is retried, and then given up on and recorded as
# lapsed rather than left open.
# SIGNAL_LEDGER_PENDING_RETRY_AFTER=1h
# SIGNAL_LEDGER_PENDING_LAPSE_AFTER=7d
# SIGNAL_LEDGER_SWEEP_BATCH_SIZE=200
# OpenAI-compatible LLM providers (including self-hosted gateways) are not set via env vars. An
# instance admin registers them at runtime
# under Instance admin → AI models, with per-model pricing, and can share individual models with
# workspaces. Workspaces may instead connect their own provider ("bring your own AI provider") under
# their workspace settings. The LLM proxy — the only path a sandboxed agent has to a provider key —
# runs automatically wherever agent jobs execute; there is no separate enable flag for it.
# =============================================================================
# Worker pod (split-pod deployment only — monolith defaults work without these)
# =============================================================================
# The application-worker compose service hosts the practice + mentor sandbox
# runtime out-of-process. It dials the application-server hub over WSS and
# exchanges a one-time registration token for a 1-hour JWT.
# Shared bootstrap secret. Workers exchange this for a worker JWT at startup.
# Must be ≥32 chars. Rotate by setting a new value and restarting both pods.
# HEPHAESTUS_WORKER_REGISTRATION_TOKEN=
# Note: the hub's JWT signing key is a keyring (hephaestus.worker.hub.token.keys[*].private-key +
# active-kid), not a single env var. With none configured the hub uses an ephemeral key regenerated
# on restart (fine for a single replica). A stable multi-replica key has no compose passthrough yet.
# No LLM env vars are needed on the worker pod: the LLM proxy runs in-process here (it follows
# job-execution capability, i.e. wherever agent.enabled + runtime.worker are enabled) and reads
# providers from the same instance/workspace catalog as the app pod — nothing to pass through.
# Capacity overrides — leave unset to auto-size from Runtime.availableProcessors().
# HEPHAESTUS_WORKER_REVIEW_MAX=auto
# HEPHAESTUS_WORKER_MENTOR_MAX=auto
# Drain timeout on SIGTERM. timeout=0 ≡ immediate-cancel. Default 5m.
# HEPHAESTUS_WORKER_DRAIN_TIMEOUT=5m