Configuration readiness
The production profile checks deployment settings at startup.
The server-role endpoint GET /api/admin/configuration-readiness returns those facts and checks that need runtime state.
It requires the app_admin authority.
It is available only after checks that can prevent startup pass.
Diagnostics never contain configured values.
Each fact has these fields:
- A stable
id. - The affected configuration
subject. - Applicable runtime
roles. - A
requirementand astatus. - An explanation and a documentation link.
Requirements are REQUIRED, RECOMMENDED, or OPTIONAL.
Status is one of:
| Status | Meaning |
|---|---|
SATISFIED | The applicable check passed. |
ACTION_REQUIRED | An applicable check failed. Required deployment facts with this status prevent startup. |
NOT_CONFIGURED | An optional setting is absent. |
NOT_APPLICABLE | The check does not apply to this process's runtime roles. |
The table names variables inside the application container. In the supported self-host stack,
Compose maps POSTGRES_PASSWORD to DATABASE_PASSWORD and owns the runtime-role values.
| Setting | Application environment variable |
|---|---|
spring.datasource.url | DATABASE_URL |
spring.datasource.username | DATABASE_USERNAME |
spring.datasource.password | DATABASE_PASSWORD |
hephaestus.runtime.server.enabled | HEPHAESTUS_RUNTIME_SERVER_ENABLED |
hephaestus.runtime.worker.enabled | HEPHAESTUS_RUNTIME_WORKER_ENABLED |
hephaestus.runtime.webhook.enabled | HEPHAESTUS_RUNTIME_WEBHOOK_ENABLED |
hephaestus.host-url | APPLICATION_HOST_URL |
hephaestus.security.encryption-key | HEPHAESTUS_SECURITY_ENCRYPTION_KEY |
hephaestus.security.credential-encryption-key | HEPHAESTUS_SECURITY_CREDENTIAL_ENCRYPTION_KEY |
hephaestus.webhook.secret | WEBHOOK_SECRET |
hephaestus.webhook.routing.secret | WEBHOOK_ROUTING_SECRET |
hephaestus.webhook.routing.previous-secret | WEBHOOK_ROUTING_PREVIOUS_SECRET |
hephaestus.sync.nats.enabled | NATS_ENABLED |
hephaestus.sync.nats.server | NATS_SERVER |
hephaestus.sync.nats.username | NATS_USERNAME |
hephaestus.sync.nats.password | NATS_PASSWORD |
hephaestus.auth.state-cookie-key | HEPHAESTUS_AUTH_STATE_COOKIE_KEY |
hephaestus.llm.egress.allow-loopback | HEPHAESTUS_LLM_EGRESS_ALLOW_LOOPBACK |
hephaestus.agent.image.require-digest | HEPHAESTUS_AGENT_IMAGE_REQUIRE_DIGEST |
hephaestus.agent.image.reference | HEPHAESTUS_AGENT_IMAGE_REFERENCE |
hephaestus.sandbox.docker.container-runtime | SANDBOX_DOCKER_CONTAINER_RUNTIME |
spring.mail.host | SPRING_MAIL_HOST |
hephaestus.email.from | HEPHAESTUS_EMAIL_FROM |
Runtime roles
- Enable at least one of
hephaestus.runtime.server.enabled,worker.enabled, orwebhook.enabled. - Use only
trueorfalsefor each value.
The supported split topology enables only webhook on the webhook process and only worker on a remote worker. The application process enables server, optionally with a colocated worker.
Database
The server and worker roles use PostgreSQL.
The webhook role has no database and needs none of these settings.
DATABASE_URL must be a PostgreSQL URL.
The production profile adds the jdbc: prefix.
Spring Boot and Liquibase perform separate connection and migration health checks.
This syntax check does not replace them.
- Supply a non-empty username.
- Supply a non-empty password.
Remote databases require TLS.
Prefer sslmode=verify-full, which encrypts the connection and verifies server identity.
sslmode=require encrypts without identity verification.
Production startup rejects plaintext remote connections unless HEPHAESTUS_DATABASE_ALLOW_INSECURE_REMOTE=true explicitly permits them.
Credential encryption
Do not change the general encryption key on an existing installation. Rotate the credential key with the documented online procedure.
The server and worker roles require both keys. The webhook role stores no secret and needs neither key.
- Set each encryption key to exactly 32 printable, non-space ASCII characters.
- Keep both keys with the backup set, in separate storage with separate credentials. Keep the required keys outside the cluster gives the rule.
The supported self-host setup generates the master key and copies it into an empty credential-key setting. These settings are separate even when their initial values match.
External URL
Set hephaestus.host-url to the public HTTPS origin.
The origin must have no credentials, query, fragment, or path other than /.
Webhooks
Server and webhook roles require hephaestus.webhook.secret with at least 32 printable, non-space ASCII characters.
The supported self-host setup generates an independent value.
Never reuse another application key.
The REQUIRED fact webhook.routing-secret applies to server and webhook roles on every production deployment, even without GitLab.
- Set
WEBHOOK_ROUTING_SECRETto at least 32 printable, non-space ASCII characters. - Use a value different from
WEBHOOK_SECRET. - Use the same value on both roles.
NATS
Server and webhook roles require NATS and an explicit nats:// or tls:// URI.
The URI must have a host and, optionally, a valid port.
It must have no query, fragment, or non-root path.
A worker-only process must disable NATS because its job queue uses PostgreSQL.
The supported Compose deployment requires NATS_USERNAME and NATS_PASSWORD.
Setup generates both.
Compose, not a readiness fact, requires these credentials.
Readiness checks syntax and role consistency, not connectivity or JetStream health.
Login
The server role requires a Base64-encoded 32-byte hephaestus.auth.state-cookie-key.
It also requires an enabled GitHub or GitLab sign-in provider in the database-backed provider catalogue.
Environment provider entries are seeds, not the readiness authority.
Slack and Outline are link-only providers and do not satisfy sign-in readiness.
Worker and webhook roles do not load login providers.
Compose does not forward custom auth session or user-view rate-limit variables.
To change the properties below, supply an explicit application-server environment or mounted configuration override.
A value only in the stack's .env does not change these limits.
Recent sign-in for sensitive actions
These actions require a sign-in from the last few minutes:
- Change an account's role.
- Force an account out of all its sessions.
- View as a user.
- Change a login provider.
- Register an LLM connection.
- Link an identity.
hephaestus.auth.step-up-max-age sets that window and defaults to 5m.
It must be a positive duration shorter than the absolute session lifetime.
Otherwise, the gate can never bind.
Access confirmation uses a provider already connected to the account. This is local confirmation, not multi-factor authentication. An identity provider with an existing session may complete it without another request to the user. Enforce MFA at the provider.
The server does not replay a refused action after access confirmation.
- Review the action.
- Submit it again.
Session deadlines
The access cookie lasts 24h by default (hephaestus.auth.access-ttl).
While active, the browser renews it about hourly, before expiry, or after a return to a tab whose renewal is due.
Hidden or inactive tabs do not renew it continuously.
This is a renewable credential lifetime, not a precise last-request idle timeout.
After the cookie expires, the user must sign in.
In production, tokens stay in HttpOnly, Secure, host-only, SameSite=Lax cookies.
The browser never stores them in local storage.
A session ends at hephaestus.auth.session-max-lifetime (default 7d), regardless of activity.
Access token renewal stays within that deadline, never past it.
Reads through a user view have a per-instance-admin budget.
hephaestus.auth.rate-limit.user-view.capacity per hephaestus.auth.rate-limit.user-view.period defaults to 120 per minute.
Every read about the viewed member counts, so one page usually uses several.
If the rate-limit store is unavailable, the server refuses views rather than permitting them without a budget.
Choosing session limits
Longer sessions reduce interruptions but increase exposure to stolen cookies and unattended devices. The defaults suit individual devices for routine team work. For shared computers or sensitive deployments, shorten both limits. Require MFA at the identity provider. The recent-sign-in gate above does not establish a second factor.
NIST SP 800-63B-4 §2.1.3 recommends an overall timeout of no more than 30 days at AAL1. It does not require an inactivity timeout there. OWASP's session guidance gives more conservative examples: 15–30 minutes idle and a 4–8 hour overall timeout. These are risk-based reference points, not an assurance-level certification for Hephaestus.
Explicit overrides take precedence over defaults. Renewal applies the configured access lifetime but preserves the session's original absolute deadline and sign-in time. Revocation and account suspension still deny access before either deadline.
Temporary renewal failures leave the page open. They do not bypass server authentication. A failed sign-out reports an error, which the user can retry. If the server rotates a token but its response is lost, the user may need to sign in again. Cross-tab coordination requires Web Locks support and a secure context (HTTPS in production).
LLM proxy
Worker roles must leave hephaestus.llm.egress.allow-loopback=false. Provider credentials and model
configuration are database-backed runtime configuration and are not deployment settings.
Agent image
Worker roles require digest enforcement and a SHA-256-pinned hephaestus.agent.image.reference
(readiness fact agent.image-contract). See Release image lock.
Sandbox isolation
Set SANDBOX_DOCKER_CONTAINER_RUNTIME=runsc on workers after
installing and configuring gVisor on the host. This
recommendation is non-fatal.
Docker configuration
For the single-host deployment, keep the local Unix socket default. Docker-specific settings under
hephaestus.sandbox.docker.* apply to processes with the worker role enabled.
The gateway keeps its independent hephaestus.sandbox.gateway.* settings.
Resource limits, capacity, and workspace transfer limits remain under hephaestus.sandbox.*.
Property under hephaestus.sandbox.docker | Environment variable | Default |
|---|---|---|
host | SANDBOX_DOCKER_HOST | unix:///var/run/docker.sock |
tls-verify | SANDBOX_DOCKER_TLS_VERIFY | false |
cert-path | SANDBOX_DOCKER_CERT_PATH | unset |
container-runtime | SANDBOX_DOCKER_CONTAINER_RUNTIME | Docker daemon default |
owner | SANDBOX_DOCKER_OWNER | default |
Compose forwards these variables only to application-worker.
application-server runs without the worker role.
Each worker joins a job network or its conversation host network as the container Docker identifies by its default hostname.
Keep the default hostname of each worker container.
With a custom hostname:, the worker logs that it cannot identify its container, and it starts no conversation host.
A worker outside Docker joins no job network. The server refuses its practice reviews, which run on an internal network. The worker also logs that it cannot identify its container, but it starts its conversation host (runtime roles).
Use a distinct owner for each installation that shares a Docker daemon but uses a different database.
All worker-capable roles of the same installation must use the same stable value.
It accepts 1–63 lowercase letters, digits, and hyphens, with a letter or digit first.
Container inventory, capacity accounting, and cleanup select the hephaestus.sandbox-owner label.
Job networks and conversation host networks use hephaestus-sandbox-<owner>--<UUID>.
For a conversation host, the UUID is the host ID.
This prevents accidental cross-installation cleanup.
It is not an authorization boundary against a process with Docker daemon access.
Each worker starts one conversation host, with its own internal network, attempt volumes, and container.
These resources have the label hephaestus.session-id=<host-id>.
The host container also has the label hephaestus.kind=heph-host.
These resources also record the worker container that started them, identified by its own HOSTNAME, and its start time.
Cleanup removes them only after that worker container stops or restarts.
It keeps resources whose creator it cannot establish.
After a crash of that worker, its conversation host exits when its command channel closes. Cleanup removes the stopped host container and its other resources after the worker container stops or restarts. A worker outside Docker records no creator, so the resources of its conversation host require manual removal. A new host gets a new ID, so leftover resources do not stop new conversation turns. Cleanup also keeps networks created before this record existed.
The REQUIRED fact sandbox.docker-legacy-configuration rejects old Docker settings outside hephaestus.sandbox.docker.* on workers.
Move them to the property names above.
The worker does not read the old settings.
Removing leftover sandbox networks
A sandbox network's name identifies only its job or conversation host, not whether the network is unused. A conversation host can take up to its start timeout to start (runtime roles). During that time, only the worker container may be attached. Establish the owner before you remove the network.
-
Read the recorded creator:
docker network inspect --format '{{index .Labels "hephaestus.creator-container"}} {{index .Labels "hephaestus.creator-started-at"}}' <network> -
If a creator is recorded, check it:
docker container inspect --format '{{.State.Running}} {{.State.StartedAt}}' <creator>If it runs with the recorded start time, it still owns the network. Leave that network. If the creator no longer exists, stopped, or restarted, automatic cleanup removes the network on its next pass. No manual action is necessary.
-
If the creator record is absent or unreadable, stop this installation's application processes on this Docker daemon. Include the application server and workers with this
owner. Only this pause proves the network unused: no process can start or attach a sandbox. Other installations on the daemon are unaffected. -
Only after that pause, remove the conversation host containers that the stopped workers left:
docker container ls -aq --filter label=hephaestus.kind=heph-host --filter label=hephaestus.sandbox-owner=<owner>docker container rm --force <container> -
Inspect the network again:
docker network inspect --format '{{range $id, $c := .Containers}}{{$id}} {{$c.Name}}{{println}}{{end}}' <network> -
Resolve the worker container to its full id:
docker container inspect --format '{{.Id}}' <worker-container>If an attached container has another id, a sandbox still uses the network. Leave that network.
-
If the worker container is still attached, disconnect it by its full id:
docker network disconnect <network> <worker-id> -
Remove the network:
docker network rm <network> -
List the host's attempt volumes, using the host ID at the end of the network name:
docker volume ls -q --filter label=hephaestus.session-id=<host-id> -
Remove those volumes:
docker volume rmThe command refuses a volume that a container still uses.
Before an upgrade from unscoped sandbox ownership or a change to owner:
- Drain reviews and conversation turns.
- Stop that installation's workers.
- Verify ownership of its remaining containers and networks.
- Only after verification, remove those resources.
Cleanup never adopts containers labelled hephaestus.managed=true, agent-net- networks, or resources of a previous owner.
All Docker operations of the worker use these host, TLS, and certificate settings.
Inherited DOCKER_CONTEXT, DOCKER_HOST, and Docker TLS environment variables do not override them.
A TCP connection changes how the worker reaches Docker, not where its containers run. The daemon must manage the worker container and its sandbox networks.
- Follow Docker's mutual-TLS setup.
- Set
SANDBOX_DOCKER_TLS_VERIFY=true. - With a Compose override, mount client certificates read-only into each worker-capable container.
- Set
SANDBOX_DOCKER_CERT_PATHto that container directory, which must containca.pem,cert.pem, andkey.pem.
A host path in .env does not mount it.
Never mount the Docker socket or client certificates in a sandbox.
These conditions prevent worker startup:
- Unknown keys under
hephaestus.sandbox.docker. - Blank
hostorowner. - TLS verification without a certificate path.
These checks do not verify daemon connectivity or runtime availability. See runtime roles for the gateway boundary.
The application cannot harden the Docker host from inside a container. The supported host matrix's Docker Engine version already closes CVE-2024-29018's DNS-exfiltration path. For further host protection:
-
Block the cloud metadata endpoint on the host:
iptables -I DOCKER-USER -d 169.254.0.0/16 -j DROP -
Recommended: put a proxy such as Tecnativa's
docker-socket-proxy, configured withEXEC=0, in front of the daemon socket. -
Recommended: set
userns-remap=defaultin/etc/docker/daemon.json.
Email
Email is optional.
The fact notification.email is NOT_CONFIGURED until spring.mail.host names a relay.
It is ACTION_REQUIRED if a relay is set but hephaestus.email.from is blank.
Without a sender address, every notification is withheld and recorded as NOT_CONFIGURED.
This optional readiness fact never prevents startup.
Personal settings do not expose SMTP setup warnings. Instance admins configure the capability in instance settings. See the supported no-email behavior in the setup guide. Integration settings → Email gives configuration and verification instructions. Before email activation, complete the processing review in the processor checklist.
Test-instance confirmation policy
Admin passkeys explains the explicit test-instance override for additional authentication checks. Keep its default enabled on production instances.