Skip to main content

Server configuration

okto-pulse serve is one Python process running two uvicorn listeners — the API + Web UI on 8100 and the MCP server on 8101 (80-pulse-feature-inventory.md:213–215). Configuration is read from environment variables and an optional .env file via pydantic-settings. Every field on CoreSettings accepts an env-var override using its uppercase field name (80-pulse-feature-inventory.md:178–180). This page covers the runtime knobs you actually need to touch: ports, bind hosts, CORS, logging, and worker tuning. For the full env-var reference, see Environment variables. For where Pulse stores data, see Storage paths.

Ports

Two listeners, two defaults.

Override on the command line

--api-port writes OKTO_PULSE_PORT and --mcp-port writes OKTO_PULSE_MCP_PORT into the process environment before importing okto_pulse.community.main — that module evaluates app = create_community_app() at import time and bakes the port values into /config.js for the Web UI (inventory:53–60). Setting them later, after the import, is too late.

Override via environment

Both ports must be free on the bind host before serve starts. Run okto-pulse status to confirm the current binding (inventory:63–80).

Bind addresses

Pulse separates the two listeners’ bind hosts on purpose: the API (port 8100) defaults to loopback because it serves the Web UI and admin endpoints, while the MCP server (port 8101) needs to be reachable from the agent.
Both listeners bind 127.0.0.1. Reachable only from the same machine. This is the safe default — your agent connects via http://localhost:8101/mcp?api_key=....
Never bind 0.0.0.0 on a public network without a firewall. The MCP API key is the only thing between an attacker and full board control. Use 127.0.0.1 for local work, a private network plus firewall for shared workstations, and a reverse proxy with TLS termination (Caddy, nginx, Traefik) if you must expose Pulse beyond the host.

CORS

The API enforces CORS on the Web UI and any third-party browser client. The community edition overrides the core default to * so the bundled Web UI works regardless of how you reach it (inventory:765, inventory:191). Override per environment by setting CORS_ORIGINS to a comma-separated list:
CORS is enforced by the API, not the MCP server. MCP requests are server-to-server and not subject to CORS — the API key is the access control there.

Logging

Pulse uses Python’s standard logging module. Logs stream to stdout/stderr by default.

Log level

Set via DEBUG=true or by configuring logging directly with standard Python tooling. There is no dedicated LOG_LEVEL env var; DEBUG toggles INFODEBUG for okto_pulse.* loggers.

Log destinations

By default, logs go to:
  • stdout for INFO and below
  • stderr for WARNING, ERROR, CRITICAL
  • No file logging — the process does not write a log file. Capture stdout/stderr if you need persistence.
For a containerized deployment, let the container runtime capture the streams:
For a systemd unit, journald captures both streams automatically:

MCP call tracing

Independent of the standard logger, Pulse can record every MCP tool call to a JSONL trace.
Use traces to debug tool selection or to replay sessions against the MCP test harness. See Troubleshooting for replay tooling.

Worker tuning (uvicorn)

Pulse runs with a single uvicorn worker, by default and by design. okto-pulse serve exposes --api-port, --mcp-port, and --accept-terms only — there is no --workers flag (okto-pulse/src/okto_pulse/community/cli.py:223–276 / inventory:53–60). The KG consolidation pipeline, the queue scheduler, and the embedded LadybugDB graph all assume one writer per process; spawning multiple uvicorn workers would create concurrent writers, lock contention, and split queue state.
Do not bypass the CLI to run Pulse with multiple uvicorn workers. The single-worker model is required for KG correctness — concurrent writers will hit the LadybugDB advisory lock and dead-letter queue entries. Scale by running multiple Pulse instances on different ports against different data directories instead — one Pulse process per board cluster.
What you can tune: Sources: CoreSettings fields kg_queue_max_concurrent_workers, kg_queue_min_interval_ms, kg_connection_pool_size, kg_kuzu_buffer_pool_mb (inventory:780–784).
The KG_KUZU_* and kg_kuzu_* field names are legacy — Pulse uses LadybugDB (the Kùzu-derived engine) under the hood since 2026-05-03. The variable names are kept stable for backward compatibility; the underlying engine is LadybugDB.
KG_QUEUE_* and KG_DECAY_TICK_* fields are hot-reloadable. APScheduler re-reads them with a 5-second debounce and kg_decay_tick_* can be updated through PUT /settings/runtime without a restart (inventory:790–793).

Reverse proxy notes

If you front Pulse with Caddy, nginx, or Traefik:
  • Bind Pulse to 127.0.0.1 and let the proxy terminate TLS and forward.
  • Set MCP_HOST=127.0.0.1 to keep the MCP listener loopback-only behind the proxy.
  • Forward Host, X-Forwarded-For, and X-Forwarded-Proto so the API renders absolute URLs correctly.
  • WebSockets are not used by the API or MCP — plain HTTP/1.1 is sufficient. Streamable HTTP responses (MCP SSE) require the proxy to disable response buffering on /mcp.

Verifying the running config

okto-pulse status reads the SQLite metadata and probes the configured ports:
Source: cli.py:567–614 (cmd_status) (inventory:67–78). The 216 MCP tools exposed by the running process are listed in the MCP reference.

Storage paths

Where Pulse keeps its database, attachments, and per-board knowledge graphs.

Environment variables

Full reference of every CoreSettings field and its env-var name.

Docker install

Run Pulse in a container with the right HOST and MCP_HOST defaults.

MCP setup

Connect Claude Code, Cursor, Cline, Windsurf, or Codex to your board.
Last modified on May 17, 2026