HTTP Server Configuration
Configure HTTP/HTTPS listening, named sockets, and TLS certificates. The unified HTTP server handles all HTTP-based channels.
Related:
- REST Channel — REST API binds to the unified HTTP server
- GraphQL Channel — GraphQL API binds to the unified HTTP server
- MCP Channel — MCP (Model Context Protocol) uses the GraphQL transport
- Monitoring — Monitoring endpoints can be isolated on a dedicated socket
- Upgrading IdentityScribe — Migration notes for socket/endpoints changes
Overview
Section titled “Overview”IdentityScribe uses a unified HTTP server for all HTTP traffic:
- REST API (
/api/*) - GraphQL API (
/graphql) - MCP (
/mcp) — Model Context Protocol, uses GraphQL transport - Monitoring endpoints (
/metrics,/observe/*,/livez,/readyz, etc.)
All channels bind to the same server via named sockets. By default, everything uses the @default socket on port 8080.
Socket model
Section titled “Socket model”flowchart TB
subgraph Config["http { } Configuration"]
Default["@default socket<br/>port: 8080, host: auto"]
Named["http.sockets.* (opt-in)"]
end
subgraph Channels["Channels"]
REST["channels.rest"]
GQL["channels.graphql"]
MCP["channels.mcp"]
MON["monitoring.*"]
end
Default --> REST
Default --> GQL
Default --> MCP
Default --> MON
Named -.->|"socket = name"| REST
Named -.->|"socket = name"| GQL
Named -.->|"socket = name"| MCP
Named -.->|"socket = name"| MON
@default Socket
Section titled “@default Socket”The primary socket is configured at http {} root level:
http { # Port for all HTTP traffic (default: 8080) port = 8080 port = ${?SCRIBE_HTTP_PORT}
# Host binding # "auto" resolves based on app.mode: # - dev/test/local: localhost (safe default) # - production: 0.0.0.0 (all interfaces) host = auto host = ${?SCRIBE_HTTP_HOST}}Named sockets (optional)
Section titled “Named sockets (optional)”Define additional sockets under http.sockets.* for network separation:
http.sockets.internal { port = 9001 port = ${?SCRIBE_INTERNAL_PORT} host = "localhost"}Named sockets inherit all settings from http {} automatically. Only override what differs.
Channel socket binding
Section titled “Channel socket binding”Channels reference sockets by name. If not specified, they use @default.
REST API
Section titled “REST API”channels.rest { enabled = true
# Socket for REST API (default: @default) socket = ${?SCRIBE_REST_SOCKET}}Monitoring endpoints
Section titled “Monitoring endpoints”Monitoring supports per-endpoint socket configuration:
monitoring { # Default socket for all monitoring (falls back to @default) socket = ${?SCRIBE_MONITORING_SOCKET}
# Per-endpoint overrides (each falls back to monitoring.socket) prometheus.socket = ${?SCRIBE_PROMETHEUS_SOCKET} observe.socket = ${?SCRIBE_OBSERVE_SOCKET} health.socket = ${?SCRIBE_HEALTH_SOCKET}}Multi-Socket Binding
Section titled “Multi-Socket Binding”Channels can bind to multiple sockets using array syntax:
# HOCON arraymonitoring.socket = ["@default", "internal"]
# Environment variable (comma-separated)SCRIBE_MONITORING_SOCKET="@default,internal"Common patterns
Section titled “Common patterns”Single socket (default)
Section titled “Single socket (default)”Everything on one port — simplest setup:
http { port = 8080 host = "0.0.0.0"}
# All channels use @default automaticallycurl http://localhost:8080/api/entries/usercurl http://localhost:8080/metricscurl http://localhost:8080/readyzMonitoring separation
Section titled “Monitoring separation”Keep REST public, monitoring internal:
http { port = 8080 host = "0.0.0.0"
sockets.internal { port = 9001 host = "localhost" }}
monitoring.socket = "internal"# Public REST APIcurl http://public-ip:8080/api/entries/user
# Internal monitoring (localhost only)curl http://localhost:9001/metricscurl http://localhost:9001/readyzNote: The bundled monitoring stack (
monitoring/docker,monitoring/helm) uses this pattern with monitoring on port 9001.
Development: Dual-Socket Monitoring
Section titled “Development: Dual-Socket Monitoring”For development, you may want monitoring endpoints available on both the default and internal sockets to avoid confusion when accidentally using the wrong port:
http { port = 8080 sockets.monitoring { port = 9001 }}
monitoring { socket = "monitoring"
# Expose on BOTH sockets for dev convenience observe.socket = ["@default", "monitoring"] health.socket = ["@default", "monitoring"] prometheus.socket = ["@default", "monitoring"]}This configuration:
- Keeps the production separation (
monitoring.socket = "monitoring") - Overrides individual endpoints to be available on both sockets
- Prevents 404 confusion when hitting the wrong port during development
The config/dev-local.conf uses this pattern.
TLS Termination
Section titled “TLS Termination”Enable HTTPS on the default socket:
http { port = 443 ssl { enabled = true cert = "/path/to/server.pem" key = "/path/to/server.key" }}mTLS for HTTP APIs
Section titled “mTLS for HTTP APIs”Require client certificates for API access (applies to REST, GraphQL, and MCP):
http { port = 8443 ssl { enabled = true cert = "/path/to/server.pem" key = "/path/to/server.key" ca = "/path/to/client-ca.pem" client-auth = "REQUIRED" }}Configuration inheritance
Section titled “Configuration inheritance”Settings marked as inheritable flow from http {} to named sockets:
| Setting Group | Inherits to Named Sockets |
|---|---|
concurrency-limit | Yes |
content-encoding | Yes |
protocols | Yes |
shutdown | Yes |
idle-connection-period/timeout | Yes |
request-limits | Yes |
connection-limits | Yes |
cors | Yes |
ssl | Yes |
requested-uri-discovery | Yes |
Override specific settings in a named socket:
http { concurrency-limit.max-limit = 200
sockets.internal { # Inherits concurrency-limit from http {} port = 9001 # Override only content-encoding content-encoding.gzip.enabled = false }}Environment variables
Section titled “Environment variables”Core settings
Section titled “Core settings”| Variable | Config Path | Default | Description |
|---|---|---|---|
SCRIBE_HTTP_PORT | http.port | 8080 | HTTP server port |
SCRIBE_HTTP_HOST | http.host | auto | Bind address (auto, localhost, 0.0.0.0) |
SCRIBE_HTTP2_ENABLED | http.protocols.http-2.enabled | false | Enable HTTP/2 |
SCRIBE_HTTP_GZIP_ENABLED | http.content-encoding.gzip.enabled | true | Enable gzip compression |
Socket references
Section titled “Socket references”| Variable | Config Path | Default | Description |
|---|---|---|---|
SCRIBE_REST_SOCKET | channels.rest.socket | @default | Socket for REST API |
SCRIBE_MONITORING_SOCKET | monitoring.socket | @default | Default socket for monitoring |
SCRIBE_PROMETHEUS_SOCKET | monitoring.prometheus.socket | (inherits) | Socket for /metrics |
SCRIBE_OBSERVE_SOCKET | monitoring.observe.socket | (inherits) | Socket for /observe/* |
SCRIBE_HEALTH_SOCKET | monitoring.health.socket | (inherits) | Socket for health probes |
TLS Settings
Section titled “TLS Settings”| Variable | Config Path | Default | Description |
|---|---|---|---|
SCRIBE_HTTP_SSL_ENABLED | http.ssl.enabled | false | Enable HTTPS |
SCRIBE_HTTP_SSL_CERT | http.ssl.cert | — | Server certificate (PEM) |
SCRIBE_HTTP_SSL_KEY | http.ssl.key | — | Private key (PEM) |
SCRIBE_HTTP_SSL_CA | http.ssl.ca | — | CA cert for client verification |
SCRIBE_HTTP_SSL_CLIENT_AUTH | http.ssl.client-auth | NONE | NONE, OPTIONAL, REQUIRED |
Limits
Section titled “Limits”| Variable | Config Path | Default | Description |
|---|---|---|---|
SCRIBE_HTTP_MAX_PAYLOAD_SIZE | http.request-limits.max-payload-size | unlimited | Max request body size |
SCRIBE_HTTP_MAX_TCP_CONNECTIONS | http.connection-limits.max-tcp-connections | unlimited | Max TCP connections |
SCRIBE_HTTP_CONCURRENCY_MAX | http.concurrency-limit.max-limit | auto | AIMD max permits |
Migration from Previous Config
Section titled “Migration from Previous Config”If upgrading from a version that used separate monitoring configuration:
| Old Config | New Config |
|---|---|
monitoring.hostname | http.host or http.sockets.<name>.host |
monitoring.port | http.port or http.sockets.<name>.port |
channels.rest.listen[].port | http.port or define named socket |
channels.rest.listen[].host | http.host or define named socket |
SCRIBE_MONITORING_PORT | SCRIBE_HTTP_PORT or define named socket |
SCRIBE_MONITORING_HOSTNAME | SCRIBE_HTTP_HOST or define named socket |
Migration example
Section titled “Migration example”Before (old config):
monitoring { hostname = "localhost" port = 9001}
channels.rest { listen = [{ port = 8080, host = "0.0.0.0" }]}After (new config):
http { port = 8080 host = "0.0.0.0"
sockets.internal { port = 9001 host = "localhost" }}
monitoring.socket = "internal"# REST uses @default automaticallyOr for simpler deployments where everything shares one port:
http { port = 8080 host = "0.0.0.0"}# Everything uses @default — no named sockets neededRelated documentation
Section titled “Related documentation”- Configuration Guide — General configuration reference
- Deployment Guide — Installation and deployment
- Monitoring Guide — Operational monitoring workflows
- REST Channel Guide — REST API configuration