Environment settings
Resource grants, runtime limits and worker configuration.
An Environment is anything that implements the Environment protocol. A session names each one once; Tool placements and the Agentloop refer to those names; explicit lifecycle policy controls setup and reaches them for every operation after. Where the code behind the protocol runs is the Environment's concern. Brain ships two, and any other is an extension.
The brain env
brainEnv({ name }) is Brain's own Environment, managed by the server in a separate pool of OS worker processes. A Tool placed here is a
Component admitted through POST /v1/tools, or a program paired with a compatible runtime
Component. Agentloop runtimes use POST /v1/agentloops; program sources use POST /v1/programs.
Each invocation receives this Environment's configured grants,
bounded by the deployment's allow-lists:
filesystem.workspaceis"read"or"write"for the session and Environment's retained/workspace;filesystem.scratchgrants an invocation-local/scratch. The root must appear inBRAIN_ENV_FILESYSTEM_ALLOW.networklists HTTP(S) origins, optionallyhttps://*.example.com, withinBRAIN_ENV_NETWORK_ALLOW.secretsnames process environment variables withinBRAIN_ENV_SECRET_ALLOW, mounted read-only under/secrets.
import { brainEnv } from "@aexhq/brain";
const writer = brainEnv({
name: "writer",
filesystem: { workspace: "write", scratch: "read" },
network: ["https://api.example.com"],
secrets: ["SERVICE_TOKEN"],
});Omitted access is denied, and all server allow-lists are empty by default. Tools in the same Environment share its grants; use separate named bindings for different authority. Do not silently union old per-Tool grants. Components bring their own code; this Environment installs no OS packages or interpreters.
By default each invocation receives 10 billion Wasmtime fuel units for guest computation; model, Tool,
HTTP, and filesystem waits consume no fuel. BRAIN_MAX_TURN_SECS still bounds the whole turn.
Prepare before creating sessions
Move reusable loading work out of the first request with await brain.prepare(placed).
The returned Agentloop or Tool retains its placement and configuration and can be reused by
several sessions or clients connected to the same server:
import { Brain, brainEnv } from "@aexhq/brain";
import { pi } from "@aexhq/agentloop-pi";
const brain = new Brain({ baseUrl: "http://localhost:8080", token: process.env.BRAIN_API_TOKEN });
const loop = await brain.prepare(pi({ env: brainEnv({ name: "brain" }) }));
const first = await brain.sessions.create({ model, agentloop: loop });
const second = await brain.sessions.create({ model, agentloop: loop });Imports and factories perform no I/O. prepare admits any missing artifacts and loads them on
all eligible workers before resolving. It works without a session, grants no execution authority,
and rejects placements outside brainEnv. Brain also loads on first execution when explicit
preparation is omitted. Preparing again checks current readiness; it does not replay a turn.
For artifacts already admitted, brain.prepareEnvironment({ agentloops: [runtimeId], programs: [programId], tools: [] }) calls POST /v1/brain-env/prepare directly. IDs are immutable
SHA-256 content addresses. GET /v1/agentloops/{id}, /v1/tools/{id}, and /v1/programs/{id}
report admission, not worker readiness. Preparation errors use preparation_failed.
To prepare during deployment, set BRAIN_ENV_PREPARATION to a JSON file:
{
"agentloops": ["./runtime.component.wasm"],
"programs": ["./loop.program.js"],
"tools": []
}Paths are relative to that file. Startup admits and prepares these artifacts before serving. Unknown fields, unavailable files, invalid components and worker failures stop startup.
Prepared code stays available for the server pool's lifetime, independently of session close, end, deletion or reopen. A replacement worker restores immutable preparation before executing; a full server restart restores startup configuration and loads other retained artifacts as needed. Applications can repeat explicit preparation after a restart. This does not restore mutable workspaces or replay interrupted executions. Each invocation still has fresh guest memory, and preparation does not guarantee free execution capacity or zero initialization cost.
Programs and reusable runtimes
program({ runtime, source }) packages a small UTF-8 source artifact separately from a compatible
Wasm runtime. runtime is a component(...) or an admitted runtime digest; source is a URL or
Uint8Array. Use the result as an Agentloop's or Tool's implementation, just like a Component.
The SDK checks admission before uploading either artifact, including from a fresh client.
The resolved implementation is { type: "brain_program", entrypoint: "turn" | "run", id, runtime }, with optional Tool configuration. The runtime uses the existing Agentloop or Tool
WIT entrypoint. The worker supplies { source, configuration } as its configuration: source
is the admitted UTF-8 text and configuration is the invocation's original configuration.
The source is loaded inside the worker and is not copied into the session configuration or journal.
Runtime authors own source syntax, import support and validation; source admission validates
UTF-8 and size, not language compatibility. Parsing and guest initialization may still happen
at invocation time.
Pi and Codex packages use this interface with the same JavaScript runtime and separate program bundles. Ordinary Wasm Components keep their existing descriptor and WIT contract.
The host env
hostEnv({ name }) is your own process, registered with Brain as a host: a browser tab, a Node
process, a server. A Tool placed here has run, and Brain sends the call over the command stream
the SDK holds open. See Tools.
Environments reached over HTTP
An environment(...) extension defines its options and how each instance is reached. The
application configures every instance; Brain reads the URL and the optional credential, seals the
credential beside the model key without journaling it, and carries the configuration unread:
import { environment } from "@aexhq/brain";
import { z } from "zod";
const sandbox = environment({
options: z.object({ url: z.url(), region: z.string(), token: z.string() }),
url: ({ url }) => url,
credential: ({ token }) => token,
configure: ({ region }) => ({ region }),
});
const env = sandbox({
name: "sandbox",
url: "https://sandbox.example",
region: "eu",
token: process.env.SANDBOX_TOKEN,
});The same Environment object places several Tools in one session:
const session = await brain.sessions.create({
model,
agentloop: loop({ env: brainEnv({ name: "brain" }) }),
tools: [read({ env }), write({ env })],
});What an Environment is told
Setup carries only the Environment's configuration. Execute carries an opaque implementation, input, deadline, and optional invocation-scoped service grants. There is no universal dependency manifest. The Environment's loader may prepare a project before its first execution, using normal lockfiles, scripts, or images. Preparation finishes before imports and the entrypoint. Concurrent first uses share preparation of one installation; reuse follows the actual resource lifetime, not a turn or automatically a session. Setup code cannot widen configured authority.
A lazy preparation failure can happen after session creation succeeds. It is an ordinary execution failure; Brain does not retry, change placement, or claim partial effects were undone.
Lifecycle and delivery
Session creation sets automatic bindings up before reporting ready. Manual bindings wait for an authorized setup operation. Brain journals each operation before it sends it, sends it once, and records a terminal receipt or an unknown outcome; it does not retry. Ending a session detaches it from its Environments; deleting the session asks each one to tear down. An Environment wrapping an externally owned service may implement teardown as releasing only its own binding.
Environment files and process state follow the Environment. They are not session state; the saved conversation and recorded results survive independently. Resource lifetime also depends on the provider: it may expire idle resources, impose a maximum lifetime or terminate them when an execution is cancelled. Read the chosen provider's lifecycle settings before relying on its files or processes between calls. Brain's built-in Environment retains its workspace until teardown, but each invocation has fresh guest memory; a host Environment follows the lifetime of your application process. The standalone lazy Environment shows that resources can be prepared on first use and released on teardown; it does not define every provider's expiry policy. Environment and Tool failures are committed for the Agentloop before their results return.
Worker pool
BRAIN_ENV_WORKERS selects a positive worker count (default two). Every worker can execute both
Agentloop and Tool Components in fresh Stores. Compiled code is cached per process; guest heaps
are not retained. Calls that may dispatch nested work use separate capacity from leaf executions,
so a pool full of Agentloops can still run their Tools. Saturation fails explicitly.
A failed worker affects its own invocations. Brain reports uncertain outcomes, restarts that worker for later requests, and never replays the interrupted work. Shutdown stops and reaps every worker. The deployment controls instance, memory, fuel, and concurrency ceilings; multiplying workers also multiplies their potential memory use. See the configuration reference.