API Access & Networking
IdentityScribe runs a single HTTP server for all traffic — REST, GraphQL, MCP, monitoring, and the Operator UI — but the data APIs are opt-in. On a fresh install the REST and GraphQL channels are off, and interactive API documentation is hidden because the server starts in production mode. This page is the single place to turn endpoints on, understand why one might return 404, choose which port to expose, and lock it all down.
Work through it top to bottom for first-run setup, or jump to Troubleshooting if something is already not responding.
Six states decide whether an endpoint answers
Section titled “Six states decide whether an endpoint answers”A request succeeds only when all of the following line up. Each is a separate setting, so an endpoint can be “almost on” and still return 404 or 401.
| State | Controlled by | Default | When it is off |
|---|---|---|---|
| Channel enabled — data routes served | channels.rest.enabled, channels.graphql.enabled | off | /api/entries/... or /graphql return 404 |
| Schema exposed — spec files served | channels.rest.openapi.enabled, channels.graphql.introspection.enabled | auto (off in production) | /api.json, /api.yaml, /graphql.sdl return 404 |
| Docs UI exposed — browsable docs served | channels.rest.ui.enabled, channels.graphql.ui.enabled | auto (off in production) | /api and GraphiQL return 404 |
| Authenticated — caller is allowed | auth.enabled and access rules | off | Requests without valid credentials get 401/403 |
| Reachable socket — listening on your port | channels.rest.socket, site.socket, monitoring.observe.socket | @default (8080) | A request to the wrong port is refused or 404 |
| Directory populated — entries exist | transcribe config and initial sync | n/a | Searches succeed but return no rows until sync converges |
Enable REST data routes
Section titled “Enable REST data routes”The REST channel is disabled by default. Turn it on to serve /api/entries/...:
channels.rest.enabled = trueOr with an environment variable:
SCRIBE_REST_ENABLED=trueVerify the data routes answer:
set -o pipefail
# Channel status — expect rest.enabled: truecurl -fsS -H "Authorization: Bearer <token>" http://localhost:8080/observe/channels | jq '.channels.rest'
# Search entries (replace "users" with one of your entry types)curl -fsS http://localhost:8080/api/entries/users | jq '.edges | length'Data routes work on their own — they do not require API documentation or schema endpoints to be exposed. For the full query surface (filters, sorting, pagination, exports), see the REST Channel reference.
Production API documentation
Section titled “Production API documentation”Interactive documentation and schema files are documentation, not data. They are controlled separately from the channel and default to auto, which means off in production.
| Endpoint | Purpose | Setting |
|---|---|---|
/api | Browsable, interactive REST documentation | channels.rest.ui.enabled |
/api.json, /api.yaml, /openapi.json, /openapi.yaml | OpenAPI specification files | channels.rest.openapi.enabled |
Because the default run mode is production, /api returns 404 on a fresh install even after you enable the REST channel. This is expected and safe — it keeps your schema private. Your data routes under /api/entries/... keep working, and the Operator UI still lists entry types and lets you search without any public documentation enabled.
You have three options:
# 1. Evaluation: run in development mode so auto endpoints turn on# ./identity-scribe-* --mode dev
# 2. Expose documentation explicitly, even in production (publishes your schema)channels.rest.ui.enabled = truechannels.rest.openapi.enabled = true
# 3. Keep documentation off (recommended for production) and rely on# the data routes plus the Operator UIEnable GraphQL
Section titled “Enable GraphQL”GraphQL is a separate channel with its own three states. Enable the query endpoint:
channels.graphql.enabled = trueSCRIBE_GRAPHQL_ENABLED=true| Endpoint | Purpose | Setting | Default |
|---|---|---|---|
/graphql (POST) | Run queries | channels.graphql.enabled | off |
/graphql (GET, browser) | GraphiQL explorer | channels.graphql.ui.enabled | auto |
/graphql.sdl, /graphql.json | Schema and introspection | channels.graphql.introspection.enabled | auto |
As with REST, the GraphiQL explorer and schema introspection are auto, so they are off in production. Queries against /graphql keep working regardless. See the GraphQL Channel reference for query syntax.
Socket model
Section titled “Socket model”Channels share one server, but you control what listens where using named sockets.
How HTTP sockets bind to channels
socket =By default, everything uses the @default socket on port 8080.
@default socket
Section titled “@default socket”The primary socket is configured at the http {} root:
http { port = 8080 port = ${?SCRIBE_HTTP_PORT}
# "auto" → localhost in dev, 0.0.0.0 in production host = auto host = ${?SCRIBE_HTTP_HOST}}Named sockets
Section titled “Named sockets”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 {}. Only override what differs.
Channel socket binding
Section titled “Channel socket binding”Channels reference sockets by name. Unspecified channels use @default.
channels.rest { enabled = true socket = ${?SCRIBE_REST_SOCKET} # default: @default}
monitoring { socket = ${?SCRIBE_MONITORING_SOCKET} # default for all monitoring prometheus.socket = ${?SCRIBE_PROMETHEUS_SOCKET} # per-endpoint override observe.socket = ${?SCRIBE_OBSERVE_SOCKET} health.socket = ${?SCRIBE_HEALTH_SOCKET}}Channels can bind to multiple sockets:
monitoring.socket = ["@default", "internal"]Common patterns
Section titled “Common patterns”Single socket (default)
Section titled “Single socket (default)”Everything on one port:
http { port = 8080 host = "0.0.0.0"}Monitoring separation
Section titled “Monitoring separation”Public APIs on one port, monitoring on another:
http { port = 8080 host = "0.0.0.0"
sockets.internal { port = 9001 host = "localhost" }}
monitoring.socket = "internal"The bundled monitoring stack (monitoring/docker, monitoring/helm) uses this pattern.
Dev: dual-socket monitoring
Section titled “Dev: dual-socket monitoring”During development, expose monitoring on both sockets to avoid 404 confusion:
http { port = 8080 sockets.monitoring { port = 9001 }}
monitoring { socket = "monitoring" observe.socket = ["@default", "monitoring"] health.socket = ["@default", "monitoring"] prometheus.socket = ["@default", "monitoring"]}The config/dev-local.conf uses this pattern.
Operator UI, Observe, and sockets
Section titled “Operator UI, Observe, and sockets”Add authentication
Section titled “Add authentication”Application API authentication is off by default, while Observe endpoints require authentication by default because they expose operational data. Before exposing the server, turn application API authentication on too:
SCRIBE_AUTH_ENABLED=trueSCRIBE_AUTH_ISSUER=https://auth.example.com/realms/mycompanyWith authentication on, requests without valid credentials receive 401, and requests that are authenticated but not permitted receive 403. Method selection (bearer tokens, ROPC, LDAP bind, or local accounts) and access rules are covered in the Authentication guide, and the go-live steps in the Production Checklist.
A complete first-run config
Section titled “A complete first-run config”This minimal config serves REST and GraphQL data routes, keeps the Operator UI working, and stays on the default socket. It boots in production mode with documentation hidden; add --mode dev while evaluating to browse /api and GraphiQL.
database.url = "jdbc:postgresql://localhost:5432/scribe"database.user = "scribe"database.password = ${DB_PASSWORD}
ldap.url = "ldap://your-ldap-server:389"ldap.bind-dn = "cn=reader,dc=example,dc=com"ldap.bind-password = ${LDAP_BIND_PASSWORD}
transcribes { users { ldap { base = "ou=users,dc=example,dc=com" filter = "(objectClass=person)" scope = sub attributes = "cn, sn, givenName, mail, telephoneNumber" } }}
# Serve the HTTP API channels (both are off by default)channels.rest.enabled = truechannels.graphql.enabled = trueThe Start Here walkthrough uses this shape end to end. For every available key, see the Configuration Reference.
http { port = 443 ssl { enabled = true cert = "/path/to/server.pem" key = "/path/to/server.key" }}Or via environment variables:
SCRIBE_HTTP_SSL_ENABLED=trueSCRIBE_HTTP_SSL_CERT=/path/to/server.pemSCRIBE_HTTP_SSL_KEY=/path/to/server.keyRequire client certificates for API access:
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" }}client-auth options: NONE (default), OPTIONAL, REQUIRED.
Troubleshooting
Section titled “Troubleshooting”/api returns 404
Section titled “/api returns 404”Most often this is expected: interactive documentation is auto, and the default run mode is production, so /api is hidden.
- Confirm the REST channel itself is on and serving data — this is the part that matters:
A
Terminal window set -o pipefailcurl -fsS -H "Authorization: Bearer <token>" http://localhost:8080/observe/channels | jq '.channels.rest'curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8080/api/entries/users200on the data route means REST is healthy and/apibeing404is just hidden documentation. - If you want the documentation page, run with
--mode dev, or setchannels.rest.ui.enabled = true(see Production API documentation). - If the data route is also
404, the channel is disabled — setchannels.rest.enabled = true.
The UI shows no entries
Section titled “The UI shows no entries”The Operator UI does not need public API documentation. Check, in order:
- REST disabled — request
curl -fsS -H "Authorization: Bearer <token>" http://localhost:8080/observe/channelsand confirm thatchannels.rest.enabledistrue. - Wrong socket — the UI, REST, and Observe must share a socket. Compare the socket the UI loaded from against the REST and Observe sockets (see A request hits the wrong socket).
- Not authenticated — if authentication is on, an unauthenticated UI session shows an access error rather than entries. Sign in or supply credentials.
- Directory still syncing or empty —
curl -fsS -H "Authorization: Bearer <token>" http://localhost:8080/observe/convergenceshows whether initial sync has finished. A correctly configured but freshly started instance can legitimately have zero entries until it converges.
Requests return 401 or 403
Section titled “Requests return 401 or 403”Authentication is on and the request is missing or failing credentials (401), or is authenticated but denied by an access rule (403). Supply a valid token or fix the rule — see Authentication — Access rules. Observe endpoints also have monitoring.observe.auth.enabled, which defaults to on. To confirm whether application API auth is enabled, check your auth.enabled setting and the Production Checklist.
A request hits the wrong socket
Section titled “A request hits the wrong socket”Every endpoint listens on a named socket; the default is @default on port 8080. A request to a port where the channel is not bound is refused or returns 404.
set -o pipefail# Which sockets exist, and which channel is on which socket?curl -fsS -H "Authorization: Bearer <token>" http://localhost:8080/observe/channels | jq '{sockets, channels}'Match the port you are calling against the socket the channel reports. If you split traffic across named sockets, keep the Operator UI, REST, and Observe aligned as shown in Site UI socket topology.
The local docs or home page does not render
Section titled “The local docs or home page does not render”Related
Section titled “Related”- Start Here — Full install-to-first-query walkthrough
- REST Channel — Query syntax, filters, exports
- GraphQL Channel — Schema generation and queries
- Authentication — Bearer tokens, ROPC, LDAP bind, local accounts, access rules
- Production Checklist — Schema exposure, TLS hardening, network isolation
- HTTP configuration reference — All socket settings, env vars, inheritance rules
- SSL configuration reference — Cipher suites, protocols, certificate options
- Endpoints Reference — Health probes and the Observe API