How env validation works
The runtime composes one big Zod schema at boot: the Tier-0 base schema (always required) plus every loaded plugin’sconfigSchema. process.env is validated against the merged schema. Missing required vars fail boot with [boot-error] Plugin '<name>' env validation failed for '<field>'.
Disabling a plugin (via features or autoDetect) removes its env requirements automatically.
Tier-0 (core)
Always required by the runtime. Source:packages/oracle-runtime/src/config/base-env-schema.ts.
Runtime
Matrix
Storage
Blocksync / chain
Auth / UCAN
Always present. Both arez.coerce.number() — set them as plain integer strings in .env.
LLM
Cross-field check (
validateLlmProviderKey): the API key for the selected LLM_PROVIDER must be present — OPEN_ROUTER_API_KEY when LLM_PROVIDER=openrouter (the default), NEBIUS_API_KEY when LLM_PROVIDER=nebius. A missing key fails boot with a named-field error (e.g. OPEN_ROUTER_API_KEY / NEBIUS_API_KEY) rather than a generic upstream 401 at request time. The per-role model ids are hardcoded per provider — there is no env var to swap the main model id (use the resolveModel hook for that).
Latency tuning
Two related latency wins ship always-on — there is no env var to disable them:
- Request-path caches. Three wins ship together. The per-user
SqliteSaveris reused across the two build-time hook calls per turn instead of being rebuilt on each call. The agent builder reads prior build-time checkpoints via a light path that skips the message-history join (O(1) instead of O(history)). The Memory EngineuserContextcache is keyed byroomIdinstead ofsessionId, so a fresh session for the same room reuses the cached context. - Memory-context compaction. The
## What you know about the userprompt block is deduped across all six memory buckets (entities collapsed by name, keeping the richest summary; facts / episodes / communities collapsed by normalized text — lowercase, punctuation stripped, whitespace collapsed) and bounded by fixed internal caps (per-entity summary and whole-block budget). When either cap trims content, the block ends with a_(More remembered — ask me to recall.)_footer so trimmed detail stays reachable via the memory tool. No unique fact is dropped; paraphrases survive.
Misc
LangSmith tracing (optional)
Declared in the base schema so they show up in
qiforge-cli env output. LangChain auto-wires when these are present in process.env; the runtime never reads them directly.
Per-plugin
Only required when the named plugin is loaded.Variables read but not owned
Some plugins read variables that live in another schema:composioreads the coreNETWORKand forwards it asx-ixo-network.skillsreadsNETWORKand forwards asX-IXO-Network.sandboxreadsORACLE_SECRETS(core) andSKILLS_CAPSULES_BASE_URL(owned byskills) and forwards them as headers.
optional()); a missing value just skips the matching header instead of failing the plugin build.
Generating an .env template
qiforge-cli env generates a .env template for your installed plugin set. Until that lands, write the file by hand using this reference.
Related references
- Plugin catalog — env vars per plugin in catalog form.
- Plugin config and env guide — declaring your own plugin’s vars.