Skip to content

Latest commit

 

History

History
630 lines (559 loc) · 51.3 KB

supported-inputs.md

File metadata and controls

630 lines (559 loc) · 51.3 KB

Supported configuration and module formats

This is the shared compatibility policy for inputs a user deliberately writes or supplies: brain configuration, documented environment settings, per-module JSON settings and canonical module content. Field references remain with their owners. Their documented meaning, validation, precedence and migration rules are part of the integration contract, even when a field is described in a linked module reference rather than repeated there.

The decision records the maintainer's boundary. This reference follows repository source; consult the installed package's changelog for release availability.

Compatibility and ownership

A supported input includes its accepted representation, units, interpretation, validation and failure behavior, precedence over other sources, omission/default behavior and promised preservation during edits or migration. A loader accepting arbitrary data does not make every such value a supported format. Undocumented implementation details are internal; an existing documented promise cannot be withdrawn merely by relabeling it internal.

Apply the existing contract versioning rules: additions ship in a minor; removing, renaming, retyping or changing the meaning of a supported input is a break. Before 1.0 a break requires a prior maintainer ruling on its issue, the breaking label and a minor changeset naming the break; from 1.0 it requires a major. Contract changes use CONTRACT: and update the integration contract in the same commit. An explicit migration explains what changes, preserves unrelated source and data, and documents any refusal or manual recovery path. Opening an editor or loading a configuration is not permission to migrate it.

Some omitted values deliberately select a curated policy, such as the jobs default-enabled boards or images' documented model selection. Such policies may evolve under their explicit owner policy; they do not promise one permanent catalog. This is not a blanket exemption for default changes. Assess each change against its documented promise, including validation of explicit selections, opt-in behavior and any affected migration. An explicit user value must retain its documented precedence.

Module authors own their domain schemas and canonical field references and link them to this policy. They classify a proposed change before implementation. The separately governed experimental module-authoring API does not make a user's supported files experimental merely because a module reads them. Likewise the published ENV_VARS descriptor API and the meanings of environment inputs are separate compatibility questions; export curation does not waive input promises.

Governance policy

context/policies/governance.md is versioned host input, excluded from the content index and read-only to workers. Its UTF-8 YAML frontmatter must have schema_version: 1; its Markdown body has no policy meaning. The data-only reader accepts at most 1 MiB, refuses linked ancestors, symlinks, hardlinks and nonregular files, and does not evaluate brain configuration. The entire file's exact bytes, including prose and line endings, determine its lowercase SHA-256 content hash. A prose edit changes identity without changing policy semantics.

All objects reject unknown keys. Omitted fields resolve to the defaults below; explicit fields replace their corresponding default, while nested budget/profile objects merge per field. No value grants authority to autonomous turns.

Family Accepted fields Meaning and defaults
profiles interactive, voice: governed or strict Both default to governed. A host's explicit strict override remains stronger. True voice uses voice; dictation uses interactive.
protect Array of canonical brain-relative paths Adds protection. No absolute, home, drive, backslash, empty/dot/parent segment or control-character forms. Default [].
release Array of exact built-in tier B paths Default []; tier A and .env* cannot be released. See the protected tiers.
budgets turn, session, day, each with optional warn_usd, block_usd, warn_tokens, block_tokens USD amounts are finite/nonnegative; tokens are nonnegative safe integers. Turn warn/block $100/$200 with 512,000,000 fallback tokens; session/day $2,000/$4,000 with 8,000,000,000 fallback tokens. Day is UTC calendar day; session is retained lifetime.
scope max_files_changed, max_minutes, max_tool_calls, max_bytes_written Nonnegative safe integers, except finite minutes. Defaults: 1,000 paths, 120 minutes, 500 calls, 4 GiB. Path/byte defaults are provisional approved values, not measured distributions.
commands deny, confirm: arrays of { pattern: string, reason?: string } Additions to existing permissions, default []. Regex sources preserve their literal anchors and compile case-insensitively under confirmation rules. Mixed lists retain valid entries in order and report skipped sources; nonempty entirely invalid lists refuse.
egress mode: record/allowlist; allow, deny: string arrays; max_bytes_per_turn: nonnegative safe integer record, empty lists and no byte cap by default. This reader does not establish network enforcement.
outward allow: array of { command: string, remotes: string[] }; hosts: string array Default empty additions to the existing outward-action rules and built-in destination list. Parsing grants no action.
taint egress: { allow: string[] } Default empty additions; session-derived hosts/inference relay and taint enforcement remain their owning guardrail's responsibility. Validated provider-relay requests are outside taint narrowing and create no taint (#3246); this list never needs a provider host for them.
providers Array of { paths?: string[], types?: string[], allow: string[] } Default []. At least one selector is present. Paths use canonical brain-relative Bun glob patterns; types are exact runtime taxonomy names. Either selector matches; every matching rule must allow the actual recipient. Unknown recipients cannot authorize matching content.
apify enabled: boolean; discovery, actors: deny, ask or allow; actor_grants: array of { account_id, actor_id, build_number, profile_id, schema_sha256, pricing_sha256, origins, expires_at, max_run_usd } (at most 64); limits: { run_usd, turn_usd, session_usd, day_usd, max_runs_per_turn, max_requests_per_turn, max_concurrent_runs, max_runtime_seconds, max_result_items, max_result_bytes, max_retention_seconds } Default { enabled: false, discovery: ask, actors: ask, actor_grants: [], limits: null } (additive, #3159, #3160 and #3161). Only enabled: true lets the server use its host-only APIFY_TOKEN; until then it contacts no Apify endpoint, whatever credential exists. An invalid policy file disables Apify rather than keeping an earlier enablement. discovery governs catalog searches and Actor inspection, which send the query or the chosen Actor's identifier to Apify; it never permits a run. actors: ask reviews every run, once; deny refuses every run; allow skips the review only for a run an unexpired grant matches exactly: the bound account, the immutable Actor id (not its name), an exact build_number such as 1.4.2 (not a tag), the qualified profile_id, the lowercase hex SHA-256 of the build's canonical input schema and of the Actor's price list, every https target origin the input names, and a run cap no larger than max_run_usd (positive, at most 1,000,000). Any other run is reviewed. expires_at is an ISO 8601 instant. An approved run never adds a grant; grants change only through a policy edit. limits is the only spending entitlement: without it no run is admitted. With it, every field is required. Amounts are USD from 0 to 1,000,000. Counts are whole numbers up to 1,000,000. max_runtime_seconds is at most 7 days, max_result_items at most 1,000,000, max_result_bytes at most 1 GiB and max_retention_seconds at most 30 days. Zero refuses what it bounds. run_usd bounds a run's whole reservation: its cap plus its reserved lifecycle requests and remote storage. turn_usd, session_usd and day_usd count pending reservations as spent and apply alongside, never above, budgets. day_usd and max_concurrent_runs are account-wide across brains. max_requests_per_turn counts every Apify request in a turn. Results stay readable for max_retention_seconds after a run ends, which never shortens Apify's own storage retention. See the Apify contract.

The budget and scope rulings supply these defaults. The reader selects profiles and protected paths; budget, scope, command, egress, outward, taint and provider enforcement belongs to the corresponding guardrails and is not implied by a valid check.

The same captured protection governs authoritative agent Markdown applications, planner-derived capture/archive destinations and submitted PNG masks. Protected targets, directory descendants and ancestor replacement refuse as policy_denied without partial effects; permitted reads and existing approvals remain intact. A mask's scratch cleanup also retains protected files, directories and ancestors. Neither a later session nor a new policy load can replace an already admitted application's protection (#1743). Conversation inbox staging (inbox_add) and its compensation, and an autonomous turn's exactly approved application, refuse protected targets the same way, with the protection of the policy that turn was admitted under; an approved autonomous write to a protected path no longer applies (#2876). A protected path that is itself a link, or a .env* name that is one, also protects where it leads, and a mask's scratch prune retains those places. A target reached through a link keeps the application route's alias_denied refusal; inbox staging, which follows links, refuses it as protected.

At every interactive/voice admission, the host reads the policy as data and captures an immutable effective policy/hash for that turn. Invalid edits record their validation notice and retain the process's last valid policy/hash; before any valid file, they use built-in defaults with a null effective hash. A missing optional file selects defaults without an error. A running turn retains its captured identity; a later load cannot mutate it. Policy edits remain outside turns or use the separately approved proposal route, never a worker write.

Literal recursive folder selectors such as me/** also retain folder topology for newly admitted hosted writers. The prefix must contain no glob operators; this step does not interpret exact document types or arbitrary glob selectors. A shared writer inherits the retained folder roots of active peers, even if a later policy removes them. A governed worker read-write binds each existing, unaliased root and every ancestor before its ordinary protected mounts. Files inside and permitted siblings stay writable; root and ancestor renames fail. Initial inspection of an absent, linked, special or unverifiable folder selects strict fallback without creating a placeholder. A changed inspected identity refuses launch. Folder mount qualification stays private to the resolver and binds the frozen layout to its exact brain identity; copied or caller-constructed layouts cannot authorize retained-folder mounts. The live governed launcher also checks each retained root/ancestor mount point and its exact inode in the authenticated worker namespace before opening the payload gate. A changed or unobservable view refuses launch. This requires host access to the held worker namespace; the current separate-UID broker only attests runtime-state/temp roots, so it cannot qualify retained provider folders. Such launches refuse until that concrete boundary is qualified; ordinary separate-UID launches without retained folders retain their existing behavior. Native output publication refuses staged removal/replacement of admitted roots or ancestors through strict-area, maintenance and local sync.

Before admitting a governed live writer with retained folders, the gate refuses an active peer with an unaccounted plan or a plan missing a required root pin. A peer's descendant root also retains its ancestors; retaining an ancestor alone does not retain each child inside it. An unknown peer-plan checker refuses too. A peer missing a root waits rather than refusing, for up to 30 seconds: every writer admitted after the turn retained its folders inherits them, so only an older one can lack them, such as a writer admitted before the first turn under the current policy translated a type selector. The server records each translation in its UI database, so writers in the cron runner and after a restart plan those folders too. A peer still active at that bound refuses. This check does not stop an older writer and does not block nested shared maintenance, explicitly strict turns or strict inspection fallback.

The gate stores planned root sets, not proof of mounted namespace identities; legacy rows remain unaccounted. These primitives do not activate inference-worker routing, masks, filtered indexes or server-tool refusals. They do not establish that a pre-existing writer or a concurrent policy change is safe for a protected reader. A future privacy admission must qualify all those boundaries and refuse unsupported unsafe selectors. Arbitrary type/glob requirements remain open; folder retention cannot authorize protected data for an unknown recipient.

Hosted turns (additive in 0.44.0, #1668). A provider rule denies every interactive and voice turn whose recipient it does not allow, every turn without a proven recipient, and every autonomous turn. Each literal folder/** root of a denying rule present in the turn's view reads as an empty read-only directory, a selected folder inside another selected folder is hidden with it (#2718), .git is hidden, and .brain-index/ is a turn-private, writable index. It is built before the turn by the brain's own CLI (brain index, keyword-only) in a worker that sees the same masks, so it never held a masked document. A hosted turn reuses an earlier turn's build when it provably equals a fresh one (#2833): the server keeps one copy per policy, mask set and recipient, compares the turn's checkpoint with the one that copy was built from, and adds new Markdown and asset files outside the masks with the CLI's incremental index. Any other change, including a document edited, removed or moved into a masked folder, builds the index from nothing again, as does the first such turn after the server starts. After each turn's index refresh the server brings that copy up to the live brain off the turn path, adding new notes or rebuilding it after a document changed, so the next turn usually reuses it as it is. Changes inside .brain/ and .brain-ui/ do not, because the build sees those folders as empty; a brain.config.ts that imports from them does not load in that build. The real index is refreshed from Markdown after the turn as usual. Host bridge tools that run on the server, outside the worker's mounts, refuse every path at or below a masked root with the named refusal provider_masked and one provider_mask_refusal governance event per call: brain_read_base, the application tools, share completion, image masks, inbox_add and policy proposals (#2690). query_activity, inbox_list and inbox_get read the server's own records, which keep text from earlier runs; such a turn is refused them whole with the same provider_masked refusal and no event (#2698). An autonomous (restricted) turn applies the same masks and private index to its envelope and reads the same last-valid policy. It translates selectors as below and refuses the rest the same way: an unsupported path selector before any worker starts, an untranslatable type once the brain's taxonomy has been read, a hardlink out of a masked folder as for hosted turns. Its bridge refuses masked paths the same way (#2691). It also starts only when host provenance shows its origin inbox thread was written without the masked folders, and its inbox_list and inbox_get revalidate that; a legacy thread, an item a conversation added without a born-masked lineage that hid those folders (#2816), or a thread changed under a new policy is refused with provider_masked and the queued work stays stored (#2741). In every denied turn the committed generated-text caches read as empty: .brain-cache/ is an empty read-only folder (#2725), and any root-level .context-cache.jsonl or .asset-cache.jsonl an earlier release left reads as empty (#2692). The same holds in every turn allowed only some provider folders; a turn allowed every one sees .brain-cache/ (below).

Allowed recipients (additive in 0.44.0, #2695). A turn's recipient is the provider that receives its inference, named only from the exact origin its host inference relay forwards to. That relay runs for governed and strict Claude and pi turns on managed native storage; any other turn has no proven recipient. These origins name a recipient:

Origin Recipient
https://api.anthropic.com anthropic
https://api.openai.com, https://chatgpt.com (pi's openai-codex) openai
https://generativelanguage.googleapis.com google
https://api.x.ai xai
https://api.mistral.ai mistral
https://api.deepseek.com deepseek
https://api.groq.com groq
https://api.cerebras.ai cerebras
https://api.fireworks.ai fireworks
https://api.together.ai together
https://api.moonshot.ai, https://api.moonshot.cn, https://api.kimi.com moonshotai
https://api.minimax.io, https://api.minimaxi.com minimax
https://api.z.ai, https://open.bigmodel.cn zai
An origin the server's BRAIN_UI_PROVIDER_RECIPIENT_ORIGINS names That name

Any other origin is an unknown recipient, which no rule allows. That includes a custom baseUrl or ANTHROPIC_BASE_URL, an origin listed only so a credential may reach it (BRAIN_UI_CLAUDE_OAUTH_ORIGINS, BRAIN_UI_PI_CREDENTIAL_ORIGINS), routers such as OpenRouter, and clouds that host several vendors' models, such as Bedrock or Vertex. A model name, vendor label or cost route never names a recipient. An operator who runs a trusted gateway names it with BRAIN_UI_PROVIDER_RECIPIENT_ORIGINS (name=origin[,origin];name=origin, for example anthropic=https://gateway.ithaca.invalid). A built-in origin cannot be given another name, and a malformed entry refuses the server's boot.

Matching rules intersect their allowlists. A turn allowed every provider folder sees them all, .git, the real .brain-index/ and .brain-cache/; root-level caches from an earlier release still read as empty. A turn allowed some of them sees those, and the rest are masked as above; its private index is built from that partial view, so it lacks only the masked folders' documents. A folder that a denying rule selects inside a folder the recipient may see masks that outer folder whole: only outermost provider folders are kept in place against other writers (#2718), so a mask nested inside a visible folder would not survive a peer replacing it. The turn's relay then refuses to reach any origin but the one its recipient was named from.

A conversation keeps what its turns read: provider history is never redacted. So the server records which recipient each turn showed which provider folders. A later turn of the same conversation whose recipient the current policy does not allow those folders is refused before it starts, with ProviderRecipientRefusedError naming the folders and the earlier recipient. That covers a profile pin that fell back to another profile, a changed route and a pi model change. A handoff carries its source's record into the new conversation, and a handoff summary is not drafted for a conversation that showed provider folders; write it by hand instead. A new conversation starts without a record. The record covers folders under a provider rule when the turn ran: content a turn read before a rule selected its folder is not tracked.

The rule covers the inference recipient only. What a turn's tools send over the network, including the brain CLI's own provider calls such as search embeddings, is egress and follows the egress policy (egress, outward), not providers. A turn allowed every provider folder still carries the provider masks, empty. So whatever is hidden from every turn under a provider rule, such as the generated-text caches (#2692), stays hidden from it too, until those caches move into a masked directory (#2725).

Which selectors hosted turns enforce (additive in 0.44.0, #2694). A selector is enforced only when it maps exactly onto whole folders; the turn then masks those folders as above. Every other selector refuses turn admission with ProviderPolicyRefusedError, which names it and says why, so nothing it selects is left silently readable.

Selector Hosted turns
paths: [folder/**] or [folder/**/*] with a literal folder Masks the folder. The two spellings match the same files.
Any other path pattern: me/*.md, **/oath/**, {a,b}/**, a single path such as me/identity.md, or a .git or .brain-index folder Refused.
types: [type] whose folders map exactly Masks the type's folders.
Any other types entry Refused, with the reason.

A type's folders are its effective match prefixes, or [dir] without match, as brain config check --json reports them. They map exactly when all of these hold at admission:

  • The type is defined, it is not the inbox type (which also takes every path outside the other types' prefixes), and it has at least one prefix.
  • Each prefix is a literal folder a turn can hide: no glob characters, and not .git or .brain-index or inside them.
  • No other type's prefix equals or lies inside one of them, so path inference gives no other type within them.
  • No Markdown file outside the turn's masked folders declares the type in frontmatter. The check reads every .md file the turn could see, hidden and excluded folders and node_modules included; only .git and .brain-index/ are skipped. A .md symbolic link counts as the file it resolves to, unless that file is in a masked folder. A link to a folder outside the brain is not followed and refuses the type as unverifiable. A file whose frontmatter does not parse declares no type, as for the indexer.

For example, the core identity type (dir: me) masks me/ as long as every type: identity document lives there. A note rule refuses, because note is the inbox type; so does an index rule, because index has no folder. Files of other types that sit inside a masked folder are hidden with it.

The taxonomy comes from brain.config.ts, which the host never runs: the brain's own CLI reports it from a credential-free worker that sees the literal-folder masks, and anything that worker writes is discarded. Assets (images, PDFs) take their type from their path under that configuration, so the reported folders decide which assets are hidden. Markdown documents stay covered by the frontmatter check whatever the taxonomy reports. Admission therefore runs that worker and reads every Markdown file outside the masked folders, on top of the private index build. When admission takes a newer checkpoint of the brain before the turn starts, it judges the types again on that checkpoint.

Protected taxonomy code (breaking in 0.44.0, #2699). Because the reported folders decide which assets are hidden, a type selector translates only when no governed turn could have changed the code the taxonomy runs. The taxonomy worker sees only the policy's protected paths: the built-in tiers (brain.config.ts, brain.config.json, package.json, bun.lock, node_modules, bunfig.toml, context/policies and the rest), minus what the policy releases, plus its protect list. The rest of the brain, .git included, is absent from that worker, and so are .brain/ and .brain-ui/, which the server writes for turns, even when protected. Every type selector of the policy refuses with ProviderPolicyRefusedError, naming the reason, when:

  • the policy releases brain.config.ts, brain.config.json, package.json, bun.lock or node_modules;
  • the CLI that reports the taxonomy is a brain file outside the protected paths, such as the brain's own CLI fallback script, which a brain without a packaged CLI (node_modules/.bin/brain) runs;
  • the configuration does not load from the protected paths alone, for example because it imports a helper file, loads a local module (./<folder> in modules), reads a tsconfig.json path alias or reads any other file outside them. The refusal quotes the configuration's own error, which names the file;
  • the taxonomy it reports differs from the one read with the rest of the brain visible too, because the configuration reads or probes a file outside the protected paths. Both reads run on the same checkpoint, and only the protected one is used, so the masks never depend on a file a turn can change even when code such a file holds makes the two reads agree;
  • a protected name, such as brain.config.ts beside a brain.config.json, was created by a governed turn that is still running, or has ended since admission began, and has not been reconciled. A protected name that does not exist is not mounted, so a turn can create it; its end moves the new file out of the brain;
  • the configured brain path is not its canonical path (it is reached through a symbolic link);
  • a protected path, or a folder on the way to it, is a symbolic link, is a hardlinked file, or is neither a file nor a folder in the turn's view.

To keep translating, add each such file or folder to the policy's protect list, for example protect: [modules, lib/odysseus-types.ts]. A governed turn then cannot change it either. A configuration that imports only installed packages, as the template's does, needs nothing. A brain that has only the fallback script still refuses after protecting it, because that script cannot yet start in the taxonomy worker; install the packaged CLI instead. Protection is judged against the policy in force at admission: a file that an earlier policy left unprotected keeps whatever a turn wrote to it then, so review it before protecting it. Code outside the brain, which no turn can write, still loads. Before 0.44.0 such configurations translated, trusting code a governed turn could rewrite; the maintainer approved this restriction on #2699.

Hardlinks out of masked folders (additive in 0.44.0, #2700). A folder mask hides names, not the file itself. Admission therefore refuses with ProviderPolicyRefusedError when a regular file outside the masked folders is a hardlink to a file inside one (same device and inode), whatever it is called, including names that are not valid UTF-8. The refusal names up to three such files, each with the masked file it shares, and counts the rest. Remove those hardlinks and retry. This applies to literal folders and to translated type folders alike. Symbolic links are not hardlinks and are not followed; the turn resolves them where the target is masked. A copy is a different file and is not detected.

The check reads metadata only, never contents. It looks at every file in the masked folders, and walks the rest of the brain only if a file there has more names than the masked folders hold. Hidden folders and node_modules count; only the root .git and .brain-index/ are skipped. It checks the checkpoint the turn's private index is built from and, for a governed turn, the live brain as well.

These checks apply to the brain as admission sees it. A document that the turn or a peer creates or retypes to a selected type outside its folders during the turn stays readable to that turn; the next admission refuses until it is moved into the type's folders or retyped. The same holds for a hardlink a peer creates in the live brain after the check, during admission or the turn.

Core indexing applies these selectors to embedding eligibility without removing keyword documents, chunks or links. A changed restriction reconciles existing vectors even without --embeddings; force rebuilds, cache reuse and asynchronous vector writes retain the same check. With provider rules present, hardlinked, symlinked, escaped or unreadable source paths cannot borrow an unrestricted spelling to authorize provider work. Keyword indexing remains available for these sources; coverage uses the eligible denominator. The concrete built-in Gemini embedding factory recognizes recipient google only when its actual installed SDK client resolves to the HTTPS Google Generative Language endpoint. Provider/model labels, custom implementations, Vertex/platform overrides and other endpoints do not establish that identity. A constructed client retains its resolved route when the ambient environment later changes; a new client is checked independently.

An embedding recipient does not authorize a separate completion or vision provider. Matching documents use local summary/context fallback, and matching assets retain keyword placeholders or cached descriptions rather than sending bytes to an unproven enrichment route. An invalid policy leaves keyword indexing available, disables embedding/enrichment requests and reconciles all vectors as ineligible, with a named diagnostic. This does not redact text already copied into unrelated documents or establish worker, history or server-tool isolation. Policy check warns that affected turns lose Git history; the warning is not proof that worker masks or a relay are active.

brain policy check --json is read-only, does not execute brain.config.ts, and reports the documented envelope. It checks the current file independently of any server's last-valid cache.

Brain configuration

brain.config.ts default-exports its config object; defineConfig supplies typing, while the loader validates at runtime. brain.config.json uses the same schema and is read only when the TypeScript file is absent. The files are not merged. TypeScript can carry provider/runner implementations; JSON cannot carry functions. A missing file returns no user config, and {} is valid. An invalid selected file fails rather than falling back to another file or to defaults. Imports are process-cached: start a fresh process to reload TypeScript edits.

All top-level families are listed below. Their nested field types, constraints, effective defaults and examples live in configuration.md. Omission preserves source omission; core effective defaults are applied by the consumer rather than written into the user's file.

Family Supported fields and meaning Omission and validation
profile name, cliTitle: cosmetic CLI identity. Optional strings; strict object.
taxonomy types, dirAnchors, canonical, canonicalPolicy, propagation, facts, assetTitleRules, classifierHints, defaultStaleness, tags. Core defaults, then module contributions, then user overrides; field-specific merge rules in concepts. Type names are validated strings, not a fixed union.
exclude dirs, files, segments: indexing exclusions. Adds to core/module exclusions; exact file paths cannot end in /. See exclusions.
geo Canonical service, eligibility, endpoint, cache and resource configuration. Absent leaves new services disabled; strict shared schema and contained relative cacheDir. See geo configuration.
embeddings provider, model, apiKeyEnv, dimensions. Omitted block resolves the Gemini built-in; missing key leaves vector search unavailable. A present block requires provider; dimensions are positive integers.
completions provider, fallback, apiKeyEnv, fallbackApiKeyEnv. Omitted block resolves the Gemini Flash built-in; a present block requires provider. Fallback is used when primary throws. Explicit key-variable names are nonempty strings.
agentRunner Built-in name or passed-in runner. Claude by default; registry resolution rejects unknown names.
reranker enabled, provider, model, apiKeyEnv, exclude, timeoutMs, depth, skipMargin. Model judgment is off unless enabled: true; credentials and mode overrides cannot enable it. Positive integer timeout, depth at least two, nonnegative margin. See activation.
graph root: document from which graph distances are measured. Root defaults to AGENTS.md, then CLAUDE.md; contained relative path. This does not enable a search-ranking boost.
skills emitters: extra skill emitters. Claude always runs; extras are optional string names.
stats coverageFloor, brokenLinkCeiling: health warning ratios. Defaults 0.9 and 0.05; values in 0–1.
media maxTrackedBytes, track, ignore: sync/doctor media policy. Defaults 5 MiB and empty lists; positive integer byte limit and nonempty glob strings.
hooks embedOnCommit: background paid enrichment opt-in. Off by default; boolean only.
search language: full-text tokenizer and stopwords. english by default, or none; changing it rebuilds FTS without re-embedding.
instructions maxTokens: doctor instruction-weight warning budget. 8000 estimated tokens; positive integer.
sync pull, judge: pull strategy and bounded judgments. See sync defaults; enums rebase/merge and jev/off. Missing judgment credentials use conservative decisions.
modules Package name or ./ local module path → domain config, with core-owned enabled. Config order is load order; keys must be package specifiers or contained relative paths. Domain validation belongs to each manifest. enabled defaults true and must be boolean.

Core objects reject unknown fields. Path constraints apply where the schema uses the repository-relative path validator: no absolute/home/backslash forms, .. segments or control characters. Do not generalize this to every string field: for example travelParty[].requirementsDoc is presently an optional string. Taxonomy constraints and downstream checks remain as documented in their owners.

Root selection is explicit argument → BRAIN_ROOT → nearest ancestor containing a config file → nearest ancestor containing .git → current directory. Environment does not generically overwrite brain-config keys: only the documented overrides do, such as CLI --rerank → BRAIN_RERANK_MODE → configured reranker mode, all subject to the persistent activation gate.

Environment inputs

BRAIN_UI_WORKER_IDENTITY_HELPER is an optional trusted host input owned by the UI SDK. It names one absolute, root-owned executable implementing the separate worker identity protocol. It is not a repository setting, command prefix or per-turn environment grant. Omission retains shared UID with masks and an explicit exposure disclosure; configured helpers must pass the real identity/sentinel/positive-control probe.

The package-owned references below contain the literal-variable inventory, meaning, omission behavior and conditional requirements. The inventory includes platform inputs and test controls so they can be classified rather than silently promoted into new product promises. Packages with no environment reader add no separate environment format. These references are generated from each package's src/config/env.ts; the resolver and actual consumer determine parsing and effective validation. Documented product controls below are supported inputs. Platform location inputs, build metadata and the internal NODE_ENV test control are classified separately in the precedence boundaries below; the table does not promote ambient SDK variables to supported inputs.

Owner reference Declared literal names
core BRAIN_ROOT, BRAIN_STORAGE_GIT_ROOTS, BRAIN_RERANK_MODE, XDG_BIN_HOME, NO_COLOR, BRAIN_CHROME_NO_SANDBOX, BRAIN_UI_CHROME_NO_SANDBOX, CLAUDE_CODE_PATH, GEMINI_API_KEY, TYPESAFE_API_KEY, ANTHROPIC_API_KEY, APIFY_TOKEN, BRAIN_APIFY_LEDGER
module-images OPENAI_API_KEY, GEMINI_API_KEY, OPENAI_BASE_URL, GEMINI_BASE_URL
module-jobs CHROME_CDP_URL
scrape SCRAPE_USER_AGENT, SCRAPE_CHROME_URL, SCRAPE_CHROME_PATH, SCRAPE_CHROME_NO_SANDBOX, SCRAPE_RESPECT_ROBOTS
ui-backend-claude BRAIN_UI_EXEC_WRAPPER, BRAIN_UI_EXEC_KILLER, BRAIN_UI_SUBPROCESS_ENV_EXTRA, ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN, ANTHROPIC_BASE_URL, BRAIN_UI_REVERSE_GEOCODE, NOMINATIM_PUBLIC_SERVICE_ELIGIBLE, NOMINATIM_URL, NOMINATIM_USER_AGENT
ui-backend-pi BRAIN_UI_EXEC_WRAPPER, BRAIN_UI_EXEC_KILLER, BRAIN_UI_SUBPROCESS_ENV_EXTRA, GEMINI_API_KEY, BRAIN_UI_REVERSE_GEOCODE, NOMINATIM_PUBLIC_SERVICE_ELIGIBLE, NOMINATIM_URL, NOMINATIM_USER_AGENT
ui-render-puppeteer PUPPETEER_EXECUTABLE_PATH, BRAIN_UI_CHROME_PATH
ui-server BRAIN_UI_AUTONOMOUS_SPEND_USD_PER_DAY, BRAIN_UI_AUTONOMOUS_TURNS_PER_DAY, BRAIN_UI_AUTONOMOUS_EMERGENCY_SPEND_USD, BRAIN_UI_AUTONOMOUS_EMERGENCY_TURNS, BRAIN_UI_AUTONOMOUS_TIMEZONE, BRAIN_UI_AUTONOMOUS_UNPRICED_USD_PER_TOKEN, BRAIN_UI_ASK_USER_FORM_MAX_DEPTH, BRAIN_UI_ASK_USER_FORM_MAX_NODES, BRAIN_UI_ASK_USER_FORM_MAX_OPTIONS, BRAIN_UI_EXEC_KILLER, BRAIN_UI_EXEC_WRAPPER, BRAIN_PATH, HOME, BRAIN_UI_CRON_HYGIENE, BRAIN_UI_SUBPROCESS_ENV_EXTRA, PI_CODING_AGENT_DIR, XDG_CONFIG_HOME, BRAIN_UI_SKILLS_GITHUB_TOKEN, GITHUB_TOKEN, DB_PATH, HOST, BRAIN_UI_INBOX_POKE_TOKEN_FILE, MAX_AUTONOMOUS_RUNS, BRAIN_UI_AUTONOMOUS_YIELD_AFTER_MS, BRAIN_UI_CONFIRM_BASH, BRAIN_UI_WS_MAX_CONNECTIONS, BRAIN_UI_WS_RATE, BRAIN_UI_WS_BURST, BRAIN_UI_LOG_LEVEL, SOURCE_COMMIT, ALLOWED_ORIGINS, MAX_CONCURRENT_SESSIONS, BRAIN_UI_WORKER_STATE_BYTES, BRAIN_UI_WORKER_STATE_BUDGET_BYTES, BRAIN_UI_TURN_TEMP_BYTES, BRAIN_UI_TURN_TEMP_BUDGET_BYTES, BRAIN_UI_MAINTENANCE_TEMP_BYTES, BRAIN_UI_MAINTENANCE_TEMP_BUDGET_BYTES, BRAIN_UI_TURN_TIMEOUT_MS, AUTH_MODE, BRAIN_UI_PASSWORD_HASH, COOKIE_SECRET, TRUST_PROXY, TRUST_PROXY_HOPS, PROXY_AUTH_HEADER, BRAIN_UI_DANGEROUSLY_DISABLE_AUTH, BRAIN_UI_ALLOW_PASSWORD, WEBAUTHN_RP_NAME, WEBAUTHN_USER_NAME, WEBAUTHN_USER_ID, WEBAUTHN_RP_ID, WEBAUTHN_ORIGINS, BRAIN_UI_ALLOW_LOOPBACK_ORIGIN, AGENT_BACKEND, TYPESAFE_API_KEY, CLAUDE_CODE_PATH, BRAIN_UI_CLAUDE_DEFAULT_MODEL, BRAIN_UI_CLAUDE_DEFAULT_THINKING_LEVEL, BRAIN_UI_CLAUDE_PROFILES, BRAIN_UI_PI_PROFILES, CLAUDE_CODE_OAUTH_TOKEN, BRAIN_UI_CLAUDE_TOKEN_MINTED_AT, ANTHROPIC_API_KEY, BRAIN_UI_MODEL_DISCOVERY, BRAIN_UI_MODEL_TTL_HOURS, BRAIN_UI_PRICING_DISCOVERY, BRAIN_UI_PRICING_TTL_HOURS, NODE_ENV, APIFY_TOKEN, BRAIN_APIFY_LEDGER, DEEPGRAM_API_KEY, VOICE_PROVIDER, VOICE_KEYTERM_LIMIT, VOICE_CACHE_DIR, BRAIN_UI_COASTLINE, OVERPASS_URL, OVERPASS_USER_AGENT, COASTLINE_CACHE_DIR, BRAIN_GEO_CONFIG_JSON

Static secrets and names selected by config are read when needed. Server factory configuration is resolved when the app is created; it is not a promise of live reload. Presence checks do not prove provider access, quota or request success. Token-valued flags use trimmed, case-insensitive 1/true/on/yes and 0/false/off/no; unknown or empty tokens use the declared fallback. NO_COLOR uses nonempty presence instead: NO_COLOR=0 still suppresses color. The installed shell hooks' BRAIN_SKIP_HOOKS bypass is specifically =1.

Validation is input-specific, not a universal reject-on-invalid rule. The server's autonomous budgets, form limits and canonical geo JSON reject invalid inputs; some numeric settings and log severity use documented safe fallbacks. Profile JSON and confirmation patterns have their own validation at backend initialization. Read the owning server reference, hosting configuration and budget reference before relying on a fallback. Authentication defaults and conditional secret requirements remain governed by the hosting/auth validation; optional descriptor entries do not authorize an invalid authentication setup.

Important precedence boundaries:

  • SCRAPE_CHROME_URL wins over jobs' retained CHROME_CDP_URL alias. With no endpoint the shared scraper can launch its own browser; the alias is not a promise that every scrape connects to a fixed local endpoint.
  • Renderer executable selection checks PUPPETEER_EXECUTABLE_PATH, then BRAIN_UI_CHROME_PATH, then known system paths. Core accepts either no-sandbox spelling through its token parser; the Puppeteer adapter receives that option.
  • Inference profiles' declared credentials/base URLs override inherited defaults; profiles without their own credential retain the documented Claude subscription routing and API-key clearing. Model-discovery authentication is a separate use.
  • Pi settings location is PI_CODING_AGENT_DIR → XDG_CONFIG_HOME/pi → HOME/.pi. Specialized skill-install token precedes GITHUB_TOKEN.
  • APIFY_TOKEN is read only by the server and only used while the governance policy sets apify.enabled: true (#3159). No subprocess, worker or BRAIN_UI_SUBPROCESS_ENV_EXTRA entry can receive it; the server binds the account the fixed Apify API reports, and a token for a different account needs an owner's confirmation before use. There is no fallback to another credential. Since #3162 a standalone brain apify or brain mcp process reads its own APIFY_TOKEN the same way, and BRAIN_APIFY_LEDGER names the shared Apify ledger: the server opens it (default DB_PATH.apify-ledger.sqlite), and a standalone process without it refuses every Apify operation. Neither variable reaches any worker, so a shell brain apify inside a chat turn refuses.
  • BRAIN_GEO_CONFIG_JSON supplies canonical geo settings over legacy Overpass configuration; BRAIN_UI_COASTLINE=false still prevents those requests.
  • HOME and XDG variables are platform location inputs. SOURCE_COMMIT is build-provided status metadata. NODE_ENV=test controls discovery defaults, not security. Inherited PATH, SDK/platform variables and arbitrary extra variable contents are not all brain-kit configuration APIs.

Dynamically named families

Literal tables cannot enumerate these supported name sources and transport boundaries. This inventory follows the current readers and the package-owned dynamic-family metadata. Supported name sources remain distinct from internal transport snapshots.

Owner and name source Meaning and boundary
Core embeddings.apiKeyEnv, completions.apiKeyEnv, completions.fallbackApiKeyEnv, reranker.apiKeyEnv Select built-in provider credential names at call time; omission uses that provider's default name. Primary and fallback are independent.
Images provider catalog apiKeyEnv Provider-declared credential lookup; current OpenAI/Gemini names are in the literal table. This does not introduce a provider-registration seam.
Claude inference-profile authTokenEnv / apiKeyEnv Query-time credential and availability lookup; values are not config-file secrets. The profile authentication rules still apply.
UI server and pi WEB_SEARCH_PROVIDERS[].envVar Catalog-selected key-presence checks for environment-configured web-search providers. Presence is returned, not secret values or copied settings.
Pi brain-config embeddings.apiKeyEnv Call-time key-presence check for the selected embedding provider.
Core inheritedEnv(overrides) Call-time ambient snapshot with explicit overrides last; an internal subprocess transport, not a promise for every ambient variable.
UI server subprocessEnv(audience, extra, extraNames) Audience allowlist plus admitted operator/declared names; explicit overrides last. BRAIN_UI_SUBPROCESS_ENV_EXTRA is a supported comma-separated admission control, trimmed/validated and itself withheld.
Claude/pi filtered subprocess environments Existing agent allowlist and operator extras; Claude also admits selected-profile credential names and applies its profile overrides. Forwarding does not make unknown variable meanings part of this contract.

The Google SDK's ambient GOOGLE_API_KEY is not a brain-kit credential-selection input. The Gemini providers pass their selected key explicitly. No supported promise requires mutating GOOGLE_API_KEY around asynchronous requests.

Module settings and lifecycle

For lowercase module manifest names, settings/<name>.json is a canonical JSON object. Its own keys merge recursively over the brain-config domain block; arrays, scalars and null replace. The same manifest schema then validates and applies defaults. Absent JSON contributes {}. Core-owned enabled is forbidden there; enable/disable is a separate writer. Dormant modules retain domain loading and validation. Invalid hand-edited JSON fails; it does not erase an override or silently fall back. Settings paths cannot alias another path through symlinks.

The module settings contract defines the shipped snapshot, provenance, opaque source revision, CLI and HTTP writer. Load, validation, CLI saves and UI saves use the same domain schema. Untouched optional fields stay absent; read-only/unsupported JSON values remain preserved. TypeScript keeps logic, and ordinary settings saves do not rewrite it. Inherited values require an explicit override; resetting removes the saved key.

Writes validate before replacing source, serialize revision checks and commit, and reject stale revisions. A changed save commits only its owned JSON once; structurally unchanged saves retain bytes and make no commit. Source subtrees that did not change retain their representation. Failures restore prior target files/index entries and preserve unrelated staged work. These are preservation promises, not a requirement to maintain a particular internal lock or hash layout.

Migration is an explicit reviewed transaction. Jobs previews the complete legacy scoring source, proves equivalent parsed scoring and atomically moves known keys to JSON, retaining casing, accepted source representations, unknown scoring keys, unrelated frontmatter and prose. Legacy frontmatter remains a fallback until migrated. JSON scoring wins when supplied; defaults are not materialized just because an editor opens. Travel's source-only ownership migration remains a different command: it refuses existing speaking/travel settings files, and its manifest does not declare a shared settings migration planner. Such files require reviewed manual migration, not an automatic invocation of a nonexistent planner.

First-party module inventory

All five domain schemas are strict. Each reference below owns its field meanings, validation, defaults and canonical formats under this shared policy. The shared writer applies even when a module has no custom editor metadata.

Module Supported domain configuration Canonical user content and owner reference
Speaking travelParty array of {name, role?, requirementsDoc?}, default []; deprecated but retained during the explicit travel transition. talk and conference Markdown, existing hubs and links; speaking reference, common frontmatter and directory-anchor rules. User prose is canonical.
Travel travelParty with the same member representation and default []. travel, trip, place Markdown; journey/trip-owned visit IDs, place references, route/track and photo assets. Canonical formats specify validation, relative references and unknown values. Dates/coordinates may remain unknown; titles/dates are not visit identity.
Finance clientsDir default clients, numeric feeTolerance default 30, currency default USD, positive integer termsDays default 30. ledger.md frontmatter: client identity, invoices, payments and allocations are authoritative. Statuses, balances and owned generated tables are derived; surrounding prose is preserved.
Jobs Required criteria; optional source-form scoring, dbPath, positive currency rates; opportunitiesDir, boards, queries, enrichment.concurrency and enrichment.maxDetailPages with schema defaults. Scoring/settings, criteria format, opportunity status.md pipeline fields and registry spec. JSON scoring or legacy criteria frontmatter is canonical input. jobs.db also holds manual review decisions and notes.
Images imagesDir default assets/images; optional disabledModels and ordered preferredModels string arrays. Images reference: generated image files kept at user-selected paths are content; catalog/routing details follow explicit selection policy. No additional canonical Markdown or JSON content schema is declared.

Settings live at the manifest-name path, independent of a package key alias. Core frontmatter, wiki links, registry declarations and generated-region markers retain their file-layer contracts. Module-specific Markdown fields augment those rules; they do not replace them.

Storage that is not a user input format

brain.db is a disposable content index; rebuilding from canonical files may replace it. Its existing separately documented query/direct-SQL compatibility promises still bind their consumers. Committed context/asset sidecars and stats history retain their existing file-layer contracts even though they are machine-managed. Derived does not automatically mean uncontracted.

Internal cache layouts, transient scratch output and internal storage schemas are not supported hand-authored input formats. Do not infer that an unclassified module file can be deleted or regenerated. Jobs' database contains review_status, reviewed_at and review_notes written by manual review: scraping/indexing cannot reconstruct that state. UI operational storage likewise has its own backup and recovery requirements. Those schemas can remain internal while their state must be preserved. Canonical module Markdown, JSON settings and user-kept assets are never disposable merely because they are outside the core content index.

Diagram specs

The enabled diagrams module accepts a versioned YAML diagram fence or standalone .diagram.yaml document. The v1 spec and check contract owns its strict fields, family grammar, budgets, geometry validation and parse bounds.