Skip to main content

Configuration

Charlotte resolves its settings from four sources. When the same setting is provided by more than one source, the higher-precedence source wins:
  1. CLI arguments (highest)
  2. Environment variables
  3. Config file (JSON)
  4. Built-in defaults (lowest)

Config file

Pass a config file explicitly:
If --config is omitted, Charlotte looks for charlotte.config.json in the current working directory and loads it automatically when present. A missing default file is not an error; a missing explicit --config path is. The file is validated with zod. Unknown keys, wrong types, and invalid enum values produce a clear startup error on stderr (stdout is reserved for the MCP stdio transport) and Charlotte exits non-zero.

Schema

Every section is optional. An empty {} is valid.

HTTP mode (http)

charlotte --http [--port N] serves the MCP streamable HTTP endpoint instead of stdio. The two modes are mutually exclusive — one process serves one transport.
  • POST /mcp — the MCP endpoint. Requires Authorization: Bearer <token>; anything else is answered 401 {"error":"unauthorized"} before any browser or session activity. The server refuses to start without a token.
  • GET /healthz — unauthenticated liveness: {version, uptime_s, browser_connected}. No page data, no config echo.
The tool set is fixed at startup from http.profile (--profile overrides; --tools is ignored with a warning), because a stateless HTTP transport has no per-connection registry to mutate — over HTTP, charlotte_tools is a read-only reporter: it reports the active profile and group status, and enable/disable requests are refused with a pointer at http.profile. Default browse excludes the dev-mode, evaluate, and monitoring groups. Any path that is neither /mcp nor /healthz answers 404 {"error":"not_found"}. Keys marked Reserved above are validated and documented now so a config written today keeps working when their consumers land; they have no effect yet.

Request observation (http.debugRequests / CHARLOTTE_DEBUG_HTTP)

Either switch turns on a diagnostic mode that logs, to stderr, every inbound request — method, path with query string, and headers — plus the response status once the request finishes. Unmatched paths are logged explicitly, which is the point: it exists to capture which discovery endpoints a connector client probes before that support is designed. authorization, proxy-authorization, cookie, set-cookie, x-api-key, and x-auth-token values are never logged. They are replaced by a marker recording that the header was present and, for auth headers, its scheme (<redacted: present, scheme=Bearer>). Leave it off in normal operation: it is noisy, it writes request metadata (paths, query strings, user agents) into your logs, and nothing depends on it.

Output-size limits (limits)

These caps (issue #188) bound how much a single tool response can return so a pathological page — 100k links, an infinite-scroll feed, a giant document body — cannot blow the MCP client’s context window. All are optional; omitted keys fall through to the built-in defaults above. When a page response exceeds maxResponseBytes it degrades to a compact summary and suggests writing the full result to a file via output_file; charlotte_evaluate results are capped independently by maxEvaluateBytes. Truncated responses carry a truncation marker so agents can tell the output was clipped.

Environment variables

The Chromium sandbox (--no-sandbox)

The Chromium sandbox is the primary defense between a malicious web page and the account Charlotte runs as. Because Charlotte navigates agents to arbitrary, often untrusted, URLs, the sandbox is enabled by default. Disable it only when you must (most commonly inside containers, where the kernel sandbox cannot be set up). The opt-out is exposed three ways, in precedence order:
The provided Dockerfiles set CHARLOTTE_NO_SANDBOX=1 because the container cannot use the kernel sandbox; docker-compose.yml keeps Docker’s default seccomp filter in place (it no longer uses seccomp=unconfined) so the container is not left without any syscall filtering.