# Configuration readiness

The server checks its deployment settings at startup, under the production profile. `GET /api/admin/configuration-readiness` returns those facts and the checks that need runtime state. The readiness card of the instance admin overview shows them. The endpoint requires the `app_admin` authority. It is available only after the checks that can prevent startup pass. Diagnostics never contain configured values.

The chart sets each setting from your values and the release Secret ([Configuration reference](/next/admin/configuration-reference.md)).

Each fact has a stable `id`, the configuration `subject`, the runtime `roles`, a `requirement`, a `status`, an explanation and a link to its section on this page. The requirement is `REQUIRED`, `RECOMMENDED`, or `OPTIONAL`.

| Status            | Meaning                                                                                        |
| ----------------- | ---------------------------------------------------------------------------------------------- |
| `SATISFIED`       | The check passed.                                                                              |
| `ACTION_REQUIRED` | The check failed. A required deployment fact with this status prevents startup.                |
| `NOT_CONFIGURED`  | An optional setting is absent.                                                                 |
| `NOT_APPLICABLE`  | The check does not apply to this server, for example an agent check without `sandbox.enabled`. |

| Fact `id`                                                     | Requirement                     | Section                                                             |
| ------------------------------------------------------------- | ------------------------------- | ------------------------------------------------------------------- |
| `runtime.roles`                                               | Required                        | [Server role](/next/admin/configuration-readiness.md#runtime-roles) |
| `database.url`, `database.username`, `database.password`      | Required                        | [Database](#database)                                               |
| `security.credential-encryption`, `security.value-encryption` | Required                        | [Credential encryption](#credential-encryption)                     |
| `external.base-url`, `external.api-origin`                    | Required                        | [External URL](#external-url)                                       |
| `webhook.shared-secret`, `webhook.routing-secret`             | Required                        | [Webhooks](#webhooks)                                               |
| `auth.state-cookie-key`, `auth.login-provider`                | Required                        | [Login](#login)                                                     |
| `llm.proxy-egress`                                            | Required with `sandbox.enabled` | [LLM proxy](#llm-proxy)                                             |
| `agent.image-contract`                                        | Required with `sandbox.enabled` | [Agent image](#agent-image)                                         |
| `notification.email`                                          | Optional                        | [Email](#email)                                                     |

## Server role

`runtime.roles` checks that the server role is on. The chart always turns it on. `sandbox.enabled` turns on reviews, conversation turns and their sandboxes ([Reviews and conversation turns](/next/admin/runtime-roles.md#reviews-and-conversation-turns)).

## Database

`database.url` checks that `DATABASE_URL` is a PostgreSQL URL. The production profile adds the `jdbc:` prefix. `database.username` and `database.password` check that both values are not empty. Spring Boot and Liquibase check the connection and the migration separately. An `options` parameter in `DATABASE_URL` replaces the 10-second lock wait limit of the server's connections.

A remote database requires TLS, and production startup refuses a plaintext remote connection. The chart always connects with `sslmode=verify-full`, which encrypts the connection and verifies the server identity.

## Credential encryption

CAUTION: Preserve encryption keys

Do not change the general encryption key on an existing installation. Rotate the credential key with the documented online procedure.

`security.credential-encryption` checks `HEPHAESTUS_SECURITY_CREDENTIAL_ENCRYPTION_KEY`, which encrypts stored integration credentials. `security.value-encryption` checks `HEPHAESTUS_SECURITY_ENCRYPTION_KEY`, which seals JWT signing keys and other encrypted values. Each key must be 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](/next/admin/secret-custody.md#keep-the-required-keys-outside-the-cluster)).

## External URL

`external.base-url` checks `hephaestus.host-url`, and `external.api-origin` checks `hephaestus.auth.issuer`. Each must be a root HTTPS origin, with no credentials, query, fragment, or path other than `/`. Provider webhooks and the Slack callback use the issuer. The chart sets both from `host`.

## Webhooks

`webhook.shared-secret` checks `WEBHOOK_SECRET`. It needs at least 32 printable, non-space ASCII characters. Never reuse another application key.

`webhook.routing-secret` checks `WEBHOOK_ROUTING_SECRET`, also on a deployment without GitLab. GitLab group hooks and Outline subscriptions use it. It needs at least 32 printable, non-space ASCII characters, and must differ from `WEBHOOK_SECRET`.

## Login

`auth.state-cookie-key` checks `HEPHAESTUS_AUTH_STATE_COOKIE_KEY`, a Base64-encoded 32-byte key that seals the OAuth state. `auth.login-provider` checks that at least one GitHub or GitLab sign-in provider is enabled in the provider catalog of the instance. Environment provider entries only seed the catalog. Slack and Outline only link accounts, so they do not satisfy this check.

### 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.

That window is 5 minutes. The confirmation uses a provider that is already connected to the account. It is not multi-factor authentication: enforce MFA at the provider. The server does not replay a refused action, so the user submits it again after the confirmation.

### Session deadlines

The access cookie lasts 24 hours. While a tab is active, the browser renews it about every hour. After the cookie expires, the user must sign in. A session ends after 7 days, whatever the activity. Renewal never moves a session past that deadline. Revocation and account suspension deny access before either deadline.

Tokens stay in HttpOnly, Secure, host-only, SameSite=Lax cookies, never in local storage. An account can hold parallel sessions, one for each browser or installed client. An instance admin can end every session of one account with **Force sign out**, or block the account. To end every session on the instance, see [End every session on the instance](/next/admin/production-operations-runbook.md#end-every-session-on-the-instance). For shared computers or sensitive deployments, require MFA at the identity provider.

## LLM proxy

`llm.proxy-egress` checks that `HEPHAESTUS_LLM_EGRESS_ALLOW_LOOPBACK` is `false` on a server with `sandbox.enabled`. Provider credentials and models are runtime settings in the database, not deployment settings ([AI providers](/next/admin/ai-providers.md)).

## Agent image

`agent.image-contract` checks, on a server with `sandbox.enabled`, that `HEPHAESTUS_AGENT_IMAGE_REQUIRE_DIGEST` is on and that `HEPHAESTUS_AGENT_IMAGE_REFERENCE` is pinned by its SHA-256 digest. The chart sets both from the release ([Release image lock](/next/admin/release-image-lock.md)).

## Email

Email is optional. `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. Every notification is then withheld and recorded as `NOT_CONFIGURED`. This fact never prevents startup.

[Integration settings → Email](/next/admin/production-setup.md#email) gives the configuration and the test. Before you turn on email, complete the processing review in the [processor checklist](/next/admin/dsms/processor-checklist.md#follow-up-if-the-processing-surface-changes).
