Skip to content
docs
Arxo ↗

Configuration reference

For LLMs13 sections

Give every setting of the two network executables in one place: each setting’s type, default, source (flag or environment), precedence, validation, whether a restart is needed, and whether it is secret — with links to the scenario articles that use it.

Components: law-mcp-server (binary name in law-mcp/Cargo.toml; its help text prints law-mcp) and law serve (law-bin-core/src/serve.rs, law-serve-core/). Scenario: any deployment that starts these processes. All values below are synthetic examples. Words like default, implementation-limit, reference-setting, and operator-policy-example are used with distinct meanings (see the last table); implemented / described / verified / supported follow the section vocabulary.

  • A checkout or release tree containing the binaries.
  • For law-mcp-server: a corpus profile name (one JSON file in the checkout’s profiles directory, or all).
  • For law serve: a world directory and a journal directory (see Deploy a private HTTP service).

Source rule (verified in src/main.rs, Args::parse): every setting first reads its environment variable, then a command-line flag overwrites it. Precedence is therefore flag > environment > built-in default for settings that have both; settings with only an environment source use environment > built-in default. All settings take effect only after a process restart: every change needs a restart. The four external-leg variables are read from the process environment on every call, but that is not live reload — editing the parent shell or the systemd EnvironmentFile does not change the environment of the already-running process, so a new value still needs a restart (or relaunch) to take effect. There is no configuration file and no reload signal.

SettingTypeDefaultSourceValidationRestartSecret
Root treepathauto-detected (find_root from cwd)LAW_MCP_ROOT, --rootmust hold the profiles directory for named profilesyesno
Profilestringall (empty env counts as unset)LAW_MCP_PROFILE, --profileall, or an existing profile file whose facets name each known facet exactly onceyesno
HTTP modebooloff (stdio JSON-RPC on stdin/stdout)--http only—yesno
Hoststring127.0.0.1LAW_MCP_HOST, --hostnon-loopback without a token is refused unless public (exact error names LAW_MCP_TOKEN and LAW_MCP_PUBLIC=1)yesno
Portu168722LAW_MCP_PORT, --portmust parse as u16yesno
TokenstringnoneLAW_MCP_TOKEN, --tokenexact Authorization: Bearer <token> match per request; flag wins over env; empty env counts as unset but empty --token "" is a real empty token (quirk: it also permits a public bind while demanding Authorization: Bearer with a trailing space)yesyes
PublicbooloffLAW_MCP_PUBLIC=1, --publicexplicit consent to serve without a tokenyesno
Allowed originscomma list / repeatableempty (any Origin header is refused with 403)LAW_MCP_ALLOWED_ORIGINS, --allowed-originexact string match against the Origin headeryesno
Call timeoutseconds, fractions allowednone (no limit)LAW_MCP_CALL_TIMEOUT onlynon-negative number; empty or 0 means no limityesno
Journal on/offboolonLAW_MCP_LOG=0 disables—yesno
Journal body capbytes per side65536 (64 KiB)LAW_MCP_LOG_MAX_BODY onlymust parse as usize; 0 means no capyesno
Journal socketpath/run/systemd/journal/socketLAW_MCP_LOG_SOCKET onlyif the socket is missing, records fall back to one JSON line on stderryesno
OTLP receiverHTTP(S) URLnone (disabled)LAW_MCP_OTLP_URL onlynon-empty string; spans POST as JSON to exactly this URLyesno
Answers service URLHTTPS URLnone (the 4 publication tools answer “unavailable”)LAW_ANSWERS_URL only; stdio + HTTP; read per callnon-empty; http loopback-only; credentials-in-URL rejectedyesno
Answers service tokenstringnone (same unavailability)LAW_ANSWERS_TOKEN only; stdio + HTTP; read per callrequired non-empty together with the URL; sent as Authorization: Beareryesyes
Neural service URLHTTPS URLnone (lexical-only answers)LAW_NEURAL_URL only; stdio + HTTP; read per callsame URL rules as aboveyesno
Neural service tokenstring"" (appendix inactive)LAW_NEURAL_TOKEN only; stdio + HTTP; read per callempty refused by the client (SERVICE_CONFIG → inactive appendix); sent as Authorization: Beareryesyes
Artifact dirpath$HOME (required at call time)LAW_MCP_ARTIFACT_DIR onlymissing dir and missing $HOME fail the call with ANSWER_RESOURCE_UNAVAILABLEyesno

Two unit templates (reference-settings, not code defaults) — never mix them:

  • Private (the model for every closed deployment in this section): kit/secure-deploy/law-mcp-private@.service sets LAW_MCP_PORT=%i, LAW_MCP_HOST=127.0.0.1, LAW_MCP_CALL_TIMEOUT=110, LAW_MCP_LOG=1, MemoryMax=6G, User=arxo-mcp, NoNewPrivileges=true, RestartSec=3, and reads the token from EnvironmentFile=/etc/arxo/mcp-%i.env. It contains no PUBLIC line: without a usable token the process refuses to serve instead of opening up.
  • Public (deliberate open bind only): the shipped arxo-ops/deploy/mcp/law-mcp@.service adds LAW_MCP_PUBLIC=1. Copying that line into a private instance silently converts a missing token from a startup refusal into an open evaluator — keep it out of every closed recipe.

Flags that are commands, not settings: --healthcheck (probes 127.0.0.1:<port>/healthz with a 4 s timeout, exit 0/2; honors --port), --export-neural-input PATH (writes the search index document and exits), -h/--help.

Implementation-limits (constants, not settings): request body cap MAX_BODY = 1 MiB (src/http.rs); no request batching; no SSE (GET /mcp → 405); no sessions (DELETE → 405).

Source rule (verified in serve.rs parse + service.rs Config::default): flags only — law serve reads no environment variable for its own configuration. All flags are parsed once at startup: every change needs a process restart.

SettingFlagTypeDefaultValidationRestartSecret
World--world (required)dir or pinned profile.jsonnonedir, or a file named profile.json under deps/profiles/<id>/<version>/ matching the lock; a .arxo bundle is refused — restore the world dir from the bundle firstyesno
Journal--journal / --no-journaldir / boolnone — one of the two is required--no-journal is development only (unrecorded decisions never replay)yesno
Watch world--watch-worldbooloffwatches the --world symlink target, warms the new world; the journal stays shared, new records carry the new pinyesno
Host--hoststring127.0.0.1non-loopback without a token is refused unless --publicyesno
Port--portu168480must parseyesno
Token--token / --token-file (mutually exclusive)string / pathnoneexact Authorization: Bearer <token> match; empty file token refusedyesyes
Public--publicbooloffexplicit consent to bind without a token on any address; requires the process-worker path (the CLI always sets it to its own executable, serve.rs)yesno
Workers--workersusize2at least 1yesno
Queue--queueusize64at least 1yesno
Max body--max-bodybytes1048576 (1 MiB)must parseyesno
Max assertions--max-assertionsusize10000must parseyesno
Max response--max-responsebytes16777216 (16 MiB)must parseyesno
Call timeout--call-timeout-msms30000 (30 s)positive numberyesno
Journald socket--log-socketpathnone (JSON to stderr)call fields to native journald when setyesno
OTLP receiver--otlp-urlHTTP URLnone (disabled)must start with http:// or https://yesno

Implementation-limits (no flag): socket I/O timeout 10 s (io_timeout), journal segment cap 64 MiB (max_segment), Idempotency-Key 1–255 printable ASCII characters without spaces.

The ./law shell script reads one variable: LAW_PYTHON — path to a Python ≥ 3.12 with jsonschema and fastjsonschema for law dev subcommands. It is launcher-only: neither Rust executable reads it.

  1. Pick the scenario article first (Deploy a private HTTP service for law serve; HTTP, TLS and reverse proxies for the MCP edge): copy its exact command, not the tables above.
  2. Set secrets through the file/env channel the scenario names (--token-file, LAW_MCP_TOKEN via a systemd EnvironmentFile), never on a shared command line.
  3. Start the process and read the stderr launch line: law-mcp-server prints address, profile, journal channel, call limit, and OTLP receiver; that line is the effective configuration.
  4. Change any setting by editing the unit/flags and restarting the process.
  • law-mcp-server --http --port 8722 on loopback without a token starts and answers GET /healthz with {"status":"ok",...} (HTTP 200).
  • law serve --world <dir> --journal <dir> starts after all workers warm up; GET /readyz returns 200.
  • A non-loopback bind without a token exits before listening with the refusal quoted above (exit code 2).
Terminal
law-mcp-server --healthcheck --port 8722; echo "exit=$?"
curl -s http://127.0.0.1:8722/healthz
curl -s http://127.0.0.1:8480/readyz -o /dev/null -w "%{http_code}\n"

Success: exit 0, a body containing "status":"ok", and 200 for /readyz. These commands are created-examples (paths and ports are synthetic); the flags, routes, and fields they use exist in code.

SymptomCauseFix
bind on "0.0.0.0" without LAW_MCP_TOKEN is refusednon-loopback HTTP without token/publicbind loopback, or set a token, or set LAW_MCP_PUBLIC=1 deliberately
address "0.0.0.0" without a token is refusedsame for law serve--token/--token-file, or deliberate --public
LAW_MCP_PORT: ... / --port: ...unparsable portpass a 0–65535 integer
facets.allow contains duplicates / each known facet must be named exactly onceprofile names a facet twice or misses onefix the profile file (see Tool profiles)
OTLP URL: expected http:// or https://bad law serve --otlp-urlpass a full HTTP(S) URL
--token and --token-file cannot be combinedboth passedkeep one
direct launch requires worker_exelibrary misuse, not CLIstart via the law serve command
  • Changing any setting without restarting the process is not supported. The four external-leg variables are re-read per call from the process environment, but a changed shell or EnvironmentFile value still reaches the process only through a restart (or relaunch); everything else binds at startup.
  • There is no configuration file format: any .toml/.yaml snippet for these executables is an operator-policy-example, not a server-read file.
  • The verified constants with no setting are the 1 MiB body cap, the 10 s I/O timeout, and the 64 MiB segment: tuning them requires a code change. Anything else absent from these tables is either a constant the author did not verify (treat as unsupported until checked) or a new setting — not a hidden option you can guess.
WordMeaningExample
Defaultvalue the code uses when the operator sets nothingport 8722
Implementation-limitconstant behavior with no setting1 MiB body cap
Reference-settingvalue from a shipped deploy file, still overrideableLAW_MCP_CALL_TIMEOUT=110 in law-mcp-private@.service
Operator-policy-examplethis documentation’s suggestion, not read by coderun as a dedicated OS user
Created-examplesynthetic value (path, token, host) safe to copy structurallyLAW_MCP_TOKEN=REDACTED_EXAMPLE_TOKEN

Documentation for Arxo. Writings — blog.arxo.io.

Anonymous visit counts on stats.arxo.io, no cookies.