Configuration
Every setting this server accepts, in one place. There was no such place: the
guide explained a dozen of them where they came up and the rest existed only in
internal/config/config.go, so the answer to “what can I set” was “read the
source”.
A test keeps this honest. TestEveryConfigurationKeyIsDocumented reads the
Config struct and every COGITORIUM_* this server looks up, and fails if one
of them is missing from this file. A setting added without a line here does not
merge.
Where it comes from
Later wins:
- Defaults — what you get having written nothing.
- The file —
--config <path>, else$COGITORIUM_CONFIG, else<data-dir>/config.yamlif it exists. - The environment —
COGITORIUM_*. - Flags —
--listen,--data,--log-level,--config.
A file is YAML and every key below is top-level:
listen: 0.0.0.0:8688
data_dir: /data
sandbox: docker
terminal: false
Booleans read from the environment are strict: only 1 and true (any case)
count as on. COGITORIUM_EGRESS=yes is off, and it overrides a config file
that said on — a value that looks affirmative and is not would be the worst way
to find out what is enabled.
Where it listens, and what it owns
| Key | Environment | Default | What it does |
|---|---|---|---|
listen |
COGITORIUM_LISTEN |
127.0.0.1:8688 |
The HTTP address. Loopback means one machine; anything else requires a token for every request. |
data_dir |
COGITORIUM_DATA_DIR |
~/.cogitorium |
The SQLite database and everything the server owns on disk. |
public_url |
— | empty | How this install is reachable from outside, so a run’s files can be linked in a callback. Empty leaves the links out. |
callback_hosts |
— | empty | Hosts a callback may be sent to. Empty means none, which is what makes the callback path unreachable rather than merely unused. |
contextd_path |
COGITORIUM_CONTEXTD |
contextd |
The Contextverse binary, resolved from PATH by default. |
log_level |
COGITORIUM_LOG_LEVEL |
info |
debug, info, warn or error. |
log_format |
COGITORIUM_LOG_FORMAT |
text |
text or json. |
metrics_listen |
COGITORIUM_METRICS_LISTEN |
empty (off) | The Prometheus endpoint’s own address. It is unauthenticated, which is why it is a separate listener and why it has to be asked for. |
gear_proxy_listen |
COGITORIUM_GEAR_PROXY_LISTEN |
127.0.0.1:0 — a loopback port the kernel picks |
Where the gear network gate listens; only a gear granted the network is given the credential for it. That is enough on Docker Desktop, where a container reaches a host loopback service through host.docker.internal. On Linux, where that name is the bridge gateway rather than the host’s loopback, an operator with granted gears names the gateway address here. |
update_check |
COGITORIUM_UPDATE_CHECK |
ask |
ask, on or off. ask is what keeps a fresh install from talking to GitHub before anybody agreed to it. |
Credentials
Nothing here belongs in a config file that ends up in a repository. The three that have no YAML key at all are env-only for exactly that reason.
| Key | Environment | Default | What it does |
|---|---|---|---|
| — | COGITORIUM_ADMIN_TOKEN |
generated | Seeds the first admin’s token instead of printing a generated one. At least 24 characters. |
| — | COGITORIUM_ADMIN_PASSWORD |
none | Seeds the first admin’s password, for a deployment nobody is standing in front of. |
| — | COGITORIUM_SECRET_KEY |
none | Encrypts this install’s own secret store. Without it, storing a secret is refused rather than done badly. |
variables_dir |
COGITORIUM_VARIABLES_DIR |
empty | A directory of files read as named values — how a Kubernetes ConfigMap is mounted. |
secrets_dir |
COGITORIUM_SECRETS_DIR |
empty | The same for a mounted Secret. |
orchestrator_secrets |
COGITORIUM_ORCHESTRATOR_SECRETS |
on | off stops the orchestrator reading and writing named values. Written here it is absolute: no screen can lift it. |
Models
Seeded, never enforced. A provider is created only when this install has no provider by that name; nothing is updated or removed afterwards, so an address changed on the Models screen stays changed. See the starter.
| Key | Environment | Default | What it does |
|---|---|---|---|
providers |
— | empty | Providers and their models, known on first start so a deployment comes up ready rather than empty. |
orchestrator_model |
— | empty | The model a new workspace’s orchestrator thinks with, as <provider>/<model>. Only set when nobody has chosen one. |
providers:
- name: local
kind: openai-compatible # or: anthropic
base_url: http://ollama:11434/v1
key_env: SOME_ENV_VAR # the variable holding the key, never the key
models:
- name: qwen2.5:7b # what the provider calls it
label: local # what you read on a screen
orchestrator_model: local/qwen2.5:7b
Doors a prepared workspace arrived with
A workspace bundle can carry its inlets — the address, and behind it every task with its schema, its instruction and what success is — so two installs restored from one document are the same install. It never carries the key, which is why a restored door is inert: it exists, its shape is right, and it refuses every delivery.
That is the right rule and it left one manual step in a deployment that is
otherwise one command. inlet_keys closes it without weakening the rule,
because the key does not come from the document — it comes from the environment,
where this deployment already keeps its secrets and where the caller’s own copy
comes from too:
| Key | Environment | Default | What it does |
|---|---|---|---|
inlet_keys |
— | empty | Doors to open at start, each naming the variable holding its key. Applied on every start, not only the first: a key is one half of a pair, and an install that had drifted from the value the caller holds would refuse deliveries with nothing to read. |
inlet_keys:
- address: echopage # the `echopage` in POST /i/echopage/extract
key_env: ECHOPAGE_INLET_KEY
A door named here that this install does not have yet is ordinary — on a first start the workspace has not been imported — and the next start finds it. An empty variable leaves the door shut and says so loudly, because the failure it would otherwise cause is a 401 at somebody else’s integration hours later.
The same thing through the API, for a deployment that would rather script it:
POST /api/v1/inlets/{id}/key with {"key": "…"} adopts a key you already
hold instead of generating one. A supplied key must be at least 32 characters;
the response never echoes it back.
Running agent-authored code
| Key | Environment | Default | What it does |
|---|---|---|---|
sandbox |
COGITORIUM_SANDBOX |
auto |
docker, kubernetes, subprocess, or auto — which uses Docker when it answers and says plainly when it does not. It never picks kubernetes, which is a deployment rather than a guess. subprocess does not isolate anything: a gear runs with this server’s file access, including the database and the provider keys in it. |
sandbox_image |
COGITORIUM_SANDBOX_IMAGE |
python:3.12-alpine |
The image gears run in. |
sandbox_runtime |
COGITORIUM_SANDBOX_RUNTIME |
empty | An OCI runtime the daemon has been configured with — runsc for gVisor, kata-runtime for Kata. Cogitorium selects one and refuses at startup if the daemon lacks it; the isolation belongs to the runtime, not to this product. |
sandbox_pool |
COGITORIUM_SANDBOX_POOL |
0 (off) |
Warm containers per image. The one setting that trades isolation for latency: a pooled container has a history. Runs given named values or the network are never pooled. |
browser_image |
COGITORIUM_BROWSER_IMAGE |
pinned Playwright image | The container a gear granted the browser environment runs in. Large, and never pulled at startup. |
egress |
COGITORIUM_EGRESS |
false |
Whether agents may reach the internet at all. Off on a fresh install. |
egress_key |
COGITORIUM_EGRESS_KEY |
empty | The credential for the outward gate. Never baked into defaults. |
mcp_clients |
COGITORIUM_MCP_CLIENTS |
false |
Lets an operator install external MCP servers. An approved one runs on the host as this server’s user, outside the sandbox — policy, not isolation. |
On Kubernetes
Only the claim has to be supplied: it names the volume the data directory is on, and a gear Job mounts that same claim at its own subPath.
| Key | Environment | Default | What it does |
|---|---|---|---|
kube_namespace |
COGITORIUM_KUBE_NAMESPACE |
the pod’s own | Where gear Jobs are created. |
kube_claim |
COGITORIUM_KUBE_CLAIM |
empty | The claim the data directory is on. |
kube_node |
COGITORIUM_KUBE_NODE |
downward API | A ReadWriteOnce volume attaches to one node, so a Job scheduled elsewhere waits forever. |
kube_cpu |
COGITORIUM_KUBE_CPU |
cluster’s own | Bounds one gear Job. |
kube_memory |
COGITORIUM_KUBE_MEMORY |
cluster’s own | The same. |
The terminal
| Key | Environment | Default | What it does |
|---|---|---|---|
terminal |
COGITORIUM_TERMINAL |
on | A shell in the UI. |
It is on, and it is a shell on the machine this server runs on, as the account it runs as — the same reach you already have by sitting at that machine, and the terminal an editor would have given you. The exception is a workspace terminal on an install with more than one account: a member is not the operator, so there they get the sandbox instead, and where there is no sandbox to put them in they are refused rather than handed the machine.
terminal: false refuses it entirely, and on a shared install it should. The
setting distinguishes “written false” from “not written”, so writing it once
holds across restarts and across changes to the default.
Work
| Key | Environment | Default | What it does |
|---|---|---|---|
queue_workers |
— | 4 |
How many queued deliveries run at once. |
queue_max_per_workspace |
— | 50 |
A burst, not a backlog: large enough that an ordinary spike waits rather than being refused. |
budget_run_tokens |
— | 0 (no ceiling) |
A token ceiling for one run. |
Plugins
| Key | Environment | Default | What it does |
|---|---|---|---|
plugins |
— | empty | Per-plugin settings, keyed by plugin id. Deliberately untyped: the host has no idea what any plugin’s settings mean and should not pretend to. |
plugins:
release-radar:
repository: orkcom-tech/cogitorium
Read-only from a plugin’s side. What a plugin wants to remember goes in its own storage — a plugin writing its own configuration would be a plugin granting itself something.
Also
COGITORIUM_CONFIG names the config file itself, and is the second place
--config looks.