Production Setup
This guide covers the minimum configuration required to deploy Hephaestus in production. It assumes you are familiar with Docker Compose or Kubernetes and have access to the protected secrets managed by TUM.
Platform overview
The production stack consists of:
- Application server (Spring Boot, runs the Pi mentor agent in-process)
- Webhook server (same Spring Boot artifact,
webhookprofile → NATS JetStream) - React webapp (served via nginx)
- PostgreSQL 16
- Hephaestus-native auth (Spring Security
oauth2Loginfederating to GitHub + optional GitLab — gitlab.com or self-hosted — issuing cookie-session JWTs — ADR 0017)
Environment variables
Set the following secrets before starting the stack:
| Variable | Purpose |
|---|---|
HEPHAESTUS_SECURITY_ENCRYPTION_KEY | Required. AES-256 key (EXACTLY 32 chars) for credentials encrypted at rest in the connection table. Must be set before the first deploy of the unified integration framework — the Liquibase backfill re-encrypts existing credentials on boot and fails fast (or HALTs the migration) without it. Keep it stable; rotating requires re-encrypting all rows. Generate: openssl rand -base64 24 | cut -c1-32. |
WEBHOOK_SECRET | HMAC secret for GitHub and GitLab webhooks (prefer SHA-256; e.g., openssl rand -hex 32) |
GITHUB_OAUTH_CLIENT_ID | GitHub OAuth app client ID (callback https://<host>/api/login/oauth2/code/github) |
GITHUB_OAUTH_CLIENT_SECRET | GitHub OAuth app client secret |
HEPHAESTUS_AUTH_ISSUER | Public issuer origin, e.g. https://<host> (sets the JWT iss + /.well-known issuer) |
HEPHAESTUS_AUTH_STATE_COOKIE_KEY | Base64 32-byte AES key sealing the OAuth state cookies (openssl rand -base64 32) |
HEPHAESTUS_INTEGRATION_SLACK_ENABLED | Optional: enables the Slack OAuth admin surface (per-workspace connect) |
HEPHAESTUS_INTEGRATION_SLACK_CLIENT_ID | Optional: Slack app client ID used by the OAuth flow |
HEPHAESTUS_INTEGRATION_SLACK_CLIENT_SECRET | Optional: Slack app client secret used by the OAuth flow |
HEPHAESTUS_INTEGRATION_SLACK_REDIRECT_URI | Optional: Slack OAuth callback URL (typically https://<host>/oauth/callback/slack) |
HEPHAESTUS_INTEGRATION_OAUTH_SUCCESS_REDIRECT | Optional: post-callback landing URL on success. Defaults to /integrations?status=success. Set the absolute URL when the SPA host differs from the API host. |
HEPHAESTUS_INTEGRATION_OAUTH_FAILURE_REDIRECT | Optional: post-callback landing URL on failure. Defaults to the success base + ?status=error. |
LEGAL_PROFILE | Selects a bundled imprint/privacy profile. Use tumaet only for the canonical AET deployment; self-hosters leave empty and mount /legal-overrides/. See Legal Pages. |
Per-workspace bot tokens are issued via OAuth and encrypted at rest in the connection table; there is no longer a global bot token.
GitHub integration
Hephaestus can connect to GitHub using either a Personal Access Token (PAT) or a GitHub App:
- Personal Access Token: Simpler to set up. Configure tokens through the workspace UI after deployment. No additional environment variables required.
- GitHub App (optional): Provides more granular permissions and better rate limits. If using a GitHub App, set:
GH_APP_ID: Your GitHub App IDGH_APP_PRIVATE_KEY: Your GitHub App private key (PEM format)GH_APP_PRIVATE_KEY_LOCATION: Alternative path to private key file (optional)GH_APP_INSTALLATION_URL: Install URL shown in the workspace creation wizard
The application works with either approach. If GitHub App credentials are not provided (defaults to GH_APP_ID=0), workspaces will use Personal Access Tokens instead.
GitLab rollout bundle
GitLab login, GitLab workspaces, webhook auto-registration, and practice review are not independent toggles. Treat the following as a rollout bundle and enable them together when rolling out GitLab practice review:
| Variable | Purpose |
|---|---|
GITLAB_OAUTH_CLIENT_ID | GitLab OAuth application client ID (callback https://<host>/api/login/oauth2/code/gitlab) |
GITLAB_OAUTH_CLIENT_SECRET | GitLab OAuth application client secret |
GITLAB_OAUTH_BASE_URL | GitLab instance the login button federates to. Defaults to https://gitlab.com; set to your self-hosted instance (e.g. https://gitlab.lrz.de) |
GITLAB_OAUTH_DISPLAY_NAME | Login-button label (defaults to GitLab; e.g. gitlab.lrz.de) |
GITLAB_DEFAULT_SERVER_URL | Default GitLab instance for workspace creation / SCM sync (not auth). Falls back to GITLAB_OAUTH_BASE_URL |
GITLAB_ENABLED | Enables server-side GitLab beans in the application server |
GITLAB_WORKSPACE_CREATION | Enables GitLab workspace creation in the UI and API |
WEBHOOK_SECRET | Shared secret used for GitLab webhook verification and auto-registration |
WEBHOOK_EXTERNAL_URL | Public webhook base URL registered on GitLab |
The login provider is instance-agnostic: gitlab.com works out of the box, and any self-hosted GitLab works by pointing GITLAB_OAUTH_BASE_URL at it. Create the GitLab OAuth application on that instance with these settings:
- Redirect URI:
https://<hostname>/api/login/oauth2/code/gitlab· Scope:read_user - Confidential client: enabled
This env-seeds one default GitLab login provider (registration id gitlab). Additional GitLab instances are added at runtime by an instance admin under Instance admin → Login providers — no redeploy.
Practice review rollout bundle
Practice review is not enabled by a single flag. The following settings must be aligned:
| Variable | Purpose |
|---|---|
PRACTICE_REVIEW_FOR_ALL | Enables the feature flag and the detection gate for all users |
PRACTICE_REVIEW_SKIP_DRAFTS | Skips draft PRs and draft merge requests |
PRACTICE_REVIEW_DELIVER_TO_MERGED | Allows delivery after merge |
PRACTICE_REVIEW_COOLDOWN_MINUTES | Minimum delay between repeated reviews of the same PR/MR |
PRACTICE_REVIEW_APP_BASE_URL | Footer link in review comments |
SANDBOX_ENABLED | Enables the Docker sandbox used by the coding agent |
AGENT_NATS_ENABLED | Enables the agent job queue and executor |
GIT_CHECKOUT_ENABLED | Enables local repo checkout and bind-mount into agent containers |
NATS_ENABLED | Enables webhook-driven sync consumption |
NATS_DURABLE_CONSUMER_NAME | Durable consumer name for sync processing |
SANDBOX_ENABLED, AGENT_NATS_ENABLED, GIT_CHECKOUT_ENABLED, and NATS_ENABLED must be true together for practice review. Anything else is a half-configured deployment.
Rollout tiers
Use the same variables differently across environments:
| Environment | Intended scope |
|---|---|
| Preview | Limited by default. Keep GitLab login, GitLab workspaces, sandbox, git checkout, and agent job execution disabled unless preview is explicitly being used as rollout validation. |
| Staging | Uses the production compose files, but should still be a controlled rollout. Start by enabling GitLab login and GitLab workspace creation first. Only enable SANDBOX_ENABLED, AGENT_NATS_ENABLED, GIT_CHECKOUT_ENABLED, and NATS_ENABLED when staging is intentionally validating practice review execution. |
| Production | Full rollout only after staging has validated the exact same bundle. |
Staging and production both use the production compose files. The difference should come from the .env values, not from a different compose topology.
AI model configuration
The mentor (Pi agent run in-process) and the sandboxed practice-review proxy both flow through the application server's LLM proxy routes. There is no separate intelligence service to configure.
For Azure-backed practice review in the application server:
| Variable | Purpose |
|---|---|
LLM_PROXY_AZURE_OPENAI_URL | Azure OpenAI resource URL used by the dedicated Azure proxy route |
LLM_PROXY_AZURE_OPENAI_AUTH_HEADER | Usually api-key |
LLM_PROXY_AZURE_OPENAI_USE_BEARER | Usually false for Azure OpenAI |
The generic LLM_PROXY_OPENAI_* settings only affect the OpenAI-compatible proxy route. The sandboxed Pi agent uses the dedicated Azure route when configured for Azure OpenAI.
Webhook secret: Set WEBHOOK_SECRET to the secret configured on your GitHub and GitLab webhooks. Prefer SHA-256 (X-Hub-Signature-256) and generate with openssl rand -hex 32 (base64 also works).
Data sync and backfill
The sync scheduler fetches recent GitHub activity (issues, PRs, reviews) for monitored repositories. Backfill optionally syncs historical data in batches to avoid rate limit exhaustion.
| Variable | Default | Purpose |
|---|---|---|
MONITORING_RUN_ON_STARTUP | true | Run initial sync when application starts |
MONITORING_TIMEFRAME | 7 | Days of recent activity to sync each cycle |
MONITORING_SYNC_CRON | 0 0 * * * * | Cron schedule for sync (hourly by default) |
MONITORING_SYNC_COOLDOWN_IN_MINUTES | 60 | Minimum gap between syncs per repository |
MONITORING_BACKFILL_ENABLED | true | Enable historical data backfill |
MONITORING_BACKFILL_BATCH_SIZE | 50 | Issues/PRs per backfill batch |
MONITORING_BACKFILL_RATE_LIMIT_THRESHOLD | 500 | Skip backfill if rate limit below this |
MONITORING_BACKFILL_INTERVAL_SECONDS | 60 | Interval between backfill batches |
Backfill behavior: After recent sync completes, backfill works backwards from the highest issue number, syncing in small batches. It pauses when rate limits drop below the threshold and resumes on the next cycle. Progress is checkpointed per repository.
Deployment steps
- Provision infrastructure: Ensure PostgreSQL, Redis (if used), and storage volumes are ready.
- Configure authentication:
- Copy
server/.env.exampletoserver/.env, then populateGITHUB_OAUTH_CLIENT_ID,GITHUB_OAUTH_CLIENT_SECRET,HEPHAESTUS_AUTH_ISSUER, andHEPHAESTUS_AUTH_STATE_COOKIE_KEY. The GitHub OAuth app's callback must behttps://<host>/api/login/oauth2/code/github. - Confirm the GitHub identity provider requests the
user:emailscopes and disable username/password login flows to keep authentication SSO-only. - If rolling out GitLab login, set
GITLAB_OAUTH_CLIENT_ID/SECRET(callback.../api/login/oauth2/code/gitlab), plusGITLAB_OAUTH_BASE_URL(defaults tohttps://gitlab.com; point it at a self-hosted instance such ashttps://gitlab.lrz.de) and optionallyGITLAB_OAUTH_DISPLAY_NAME. This env-seeds the default GitLab login provider on first boot. Additional GitLab instances are added at runtime by an instance admin under Instance admin → Login providers (one OAuth app per instance) — no redeploy. The screen shows the exact redirect URI to register on the upstream GitLab OAuth app.
- Copy
- Bootstrap secrets: Load environment variables into your secret manager or
.envfiles consumed by Docker/Kubernetes. - Deploy services: Use the provided Compose files (
docker/compose.app.yaml,docker/compose.core.yaml,docker/compose.proxy.yaml) or your Kubernetes manifests. - Run database migrations: The application server runs Liquibase migrations on startup; monitor logs to confirm success.
- Verify integrations:
- Designate the first super-admin before first boot via the allowlist
HEPHAESTUS_AUTH_BOOTSTRAP_ADMINS=<provider>:@<username>(e.g.github:@octocat), or use the one-time break-glassHEPHAESTUS_AUTH_BOOTSTRAP_TOKEN— both are idempotent and lockout-safe (see thedocs/runbooks/auth-cutover.mdrunbook → First instance admin). They promote on login; subsequent admins are managed from/admin/users. Hand-editingaccount.app_role = 'APP_ADMIN'in SQL is a last-resort fallback only. - Verify that the super admin user can access workspace admin endpoints for workspaces where they have membership (they are automatically elevated to workspace ADMIN level for those workspaces).
- Trigger a test webhook from GitHub or GitLab to validate the ingest pipeline.
- Verify that
GITLAB_ENABLED,GITLAB_WORKSPACE_CREATION,SANDBOX_ENABLED,AGENT_NATS_ENABLED,GIT_CHECKOUT_ENABLED, andNATS_ENABLEDare all present in the deployed environment before testing GitLab practice review. - Open
/imprintand/privacy. If the red "not configured" banner is shown, configure a legal profile or overrides before opening the deployment to end users — serving the built-in disclaimer is a § 5 DDG / Art. 13 GDPR violation.
- Designate the first super-admin before first boot via the allowlist
Global admin privileges
Accounts with app_role = 'APP_ADMIN' carry the admin authority in their JWT. This grants them:
- Automatic workspace ADMIN privileges for workspaces where they have membership (they are auto-elevated to workspace ADMIN level)
- Ability to manage workspace settings, members, and repositories in their member workspaces
- Cannot perform OWNER-only operations (e.g., workspace ownership transfer) unless explicitly granted the OWNER role in that workspace
- Must have workspace membership (any role: OWNER, ADMIN, or MEMBER) to access the workspace
This allows platform administrators to troubleshoot and manage workspaces where they are members, with automatic admin privileges, without needing explicit ADMIN role assignment in the database.
Operational tips
- Monitor services with the central Prometheus/Loki stack; ensure trace IDs appear in logs.
- Schedule regular backups for PostgreSQL (it now holds auth state — accounts, identity links, sessions — in addition to application data).
- Review weekly leaderboard Slack posts to ensure the automation is active.
- Preview deployments do not currently represent the full GitLab practice-review rollout unless they are explicitly wired with the same GitLab IdP, sandbox, git checkout, and agent NATS settings.
Support
Contact the Hephaestus core team if you need to rotate secrets or migrate infrastructure. Document any deviations from this checklist in the deployment runbook.