Environment protocol
HTTP operations, callbacks and resource lifecycle.
This reference is for authors of environment services: HTTP endpoints that run code or manage resources for Brain. Start with Write an environment. To use an existing provider, follow Choose an environment.
Factory
import { environment } from "@aexhq/brain";
import { z } from "zod";
export const sandbox = environment({
options: z.object({ url: z.url(), token: z.string(), image: z.string() }),
url: ({ url }) => url,
credential: ({ token }) => token,
configure: ({ image }) => ({ image }),
});
const env = sandbox({ name: "sandbox", url: "https://sandbox.example",
token: "your-service-token", image: "node:22" });Load the real service token from your credential store. Brain posts to {url}/v1/operations.
The service must authenticate requests; the factory alone does not enforce authentication.
Credentials are sealed by the server and never journaled.
Configuration is recorded and forwarded unread. The Environment name is unique within its session.
JavaScript service adapters
@aexhq/brain/runtime provides environmentProtocol(run) to validate commands and receipts
using the generated Environment schema. run receives the operation and any authenticated
context supplied by the server. Thrown BrainError values retain their code, retryability and
details; ambiguous callback outcomes become unknown receipts.
serveEnvironment(handle, options) serves POST /v1/operations on Node.js. Supply a bearer
token or an authenticate(headers) function that returns your private context. It defaults to
loopback, an automatically allocated port and a 16 MiB request limit; set host, port or
maxBodyBytes as needed. The returned object exposes url and asynchronous close().
An optional callback(headers, input) relay serves /v1/callback and must authenticate its own
invocation credentials. HTTP disconnect does not itself cancel an operation.
callExecution(callback, method, input) checks the granted method and sends one callback.
reportEnvironment(reporter, event) uses the binding event envelope and validates its committed
sequence. finishExecution(operation, receipt) commits a synchronous Tool result before returning
it; a lost or invalid acknowledgement becomes unknown. These helpers accept an optional
{ fetch, signal }. They do not retry or follow redirects. Execution and reporter credentials
remain separate; the provider controls background execution and resource lifetime.
Operations
A command has
contract: "environment/v1" and operation: { session_id, environment, sequence, request }.
New operations also carry the pinned binding reference, current configuration, source
template, operation-scoped context and binding-scoped reporter capabilities. The template
selects the originally authorized driver credential; provider configuration cannot replace it.
The response carries the same contract and sequence and a receipt.
| Request | Responsibility |
|---|---|
setup { configuration } | Validate Environment-owned options and establish the binding. Allocation can be lazy. |
execute { implementation, input, deadline_ms?, callback? } | Prepare and run the opaque implementation within configured authority. deadline_ms is the remaining duration in milliseconds, not a Unix timestamp. An absent deadline is unlimited. |
call { name, input } | An Environment-specific operation, such as explicit workspace reset. |
cancel { target_sequence } | Best-effort cancellation of the named invocation. |
detach | Release session attachment mechanisms while retaining owned resources. |
teardown | Release resources owned by this Environment instance. |
Receipts are accepted, progress, result { output }, returned { output? }, failure, or unknown. Return an
unsupported failure for an unsupported operation or descriptor. Neither turn nor invoke
is an Environment operation. Brain uses this boundary for Tools; harnesses use the
startup protocol.
For a Tool, result and returned end only the synchronous
phase, optionally emitting output. Call the granted finish service to complete the Tool, either
before returning or later. Its callback remains valid after return under the original deadline.
Returning accepted or progress leaves the dispatched outcome unknown. Failure fields code, message, retryable,
and optional JSON details reach Tool results unchanged. Retryability is advisory; Brain does
not retry or restore failed resources automatically.
For Tool invocations, return a failure with code timeout when its deadline expires, or cancelled
for explicit cancellation. Keep the first stop cause if cleanup also fails, including cleanup
evidence in details. These codes do not promise rollback. Use unknown only when dispatch may
have happened but no reliable result is available. An explicit unknown receipt does not itself
mark the Environment unreachable; a transport failure still does.
Invocation services
An execution may receive callback: { url, token, methods }. POST { method, input } to that
URL with its bearer token. The response is the method's JSON result. Only listed methods are
granted. A Tool receives emit, result, returned, finish, model, environments, and telemetry;
model input is ModelRequest; emit input is { event_type, data }; telemetry input is the
diagnostic record. Results are respectively ModelResult, committed sequence, and null.
Tool result takes an Outcome; returned and finish take an optional Outcome (JSON null
means no new result). Each returns its committed sequence. Finish orders after earlier admitted
emissions and closes further emission authority. Journal observations never automatically
edit model messages; the harness owns its conversation.
The token belongs to one invocation and is revoked on completion or cancellation. It is never
part of the journal. environments accepts the generated EnvironmentControlRequest and is
bounded by that extension's own grants. It grants no general session API access. Cancellation makes
subsequent calls fail. Never preserve a callback for the next execution.
Lifecycle and delivery
Resource lifetime belongs to the Environment's configuration. Automatic session creation calls setup, ending calls detach, and deletion calls teardown. Brain passivating between turns does not end the session or release its resources.
Setup can return { type: "accepted", on_turn_end: "release" } to register a method for
turn completion. After a successful, failed, or cancelled turn, Brain sends
call { name: "release", input: { sequence } }, where sequence names turn_started.
The Environment decides what this method does and whether its configuration enables it.
For example, a disposable sandbox can terminate; a workspace can remain available.
The answer is saved and returned before this call finishes. Unfinished Tools defer turn-end cleanup until their execution resources have been released. The session can process later messages and Tool observations while that background work remains open. Brain journals cleanup and waits for it before completing a graceful drain. A failed cleanup does not replace the answer. Like other effects, callbacks are not retried or replayed after a restart; a provider deadline still bounds resources if Brain stops before delivery. Report resource loss explicitly, without silently allocating replacements.
Controller services and binding observations
An Env operation receives its own context callback with environments, emit and result.
It uses the Env's grants, independently of the execute.callback belonging to a hosted Tool
. environmentHandler(...) exposes this as ctx.environments, ctx.emit and
ctx.emitResult; the context closes when that operation returns or is cancelled.
ctx.reporter may remain with the provider controller. POST { type: "event", event_type, data }
for diagnostics, or { type: "result", output: { observation, content? } } for actionable facts,
to operation.reporter.url with its bearer token. Brain assigns Environment provenance and
returns the committed sequence. The reporter survives Brain restart but cannot control the
session; closed or replaced bindings reject old reports. Pending observations share the existing
emission limit. Never give a provider controller's capability to untrusted executed code.
Observation scopes are environment (availability), resource (provider identifier, code and
message), and operation (pending sequence and confirmed resolution). An unknown lifecycle
operation remains reserved until its correlated resolution. A resource observation does not
change binding availability. Actionable observations wake the Agentloop through the existing
watermark and coalescing mechanism; diagnostics alone do not. See
Control environments for grants and templates.
Brain commits each operation before delivery and sends it once. Identify it by session and sequence, retain uncertainty when an effect may have occurred, and never automatically retry an unknown execution. The provider defines the scope and lifetime of retained files; invocation state and callback credentials belong only to the current call.
The lazy workspace example tests concurrent first allocation and caller-controlled cleanup. It binds loopback and is an authoring example; it does not isolate untrusted code.
Optional implementation preparation
Keep dependency details inside the implementation's package and this Environment's loader. The descriptor can select a project with a lockfile and optional setup script. Bootstrap the interpreter and import-time dependencies before loading setup or run functions. A setup function written in Python cannot install the Python interpreter required to invoke itself.
The Python Environment example
uses operator-selected projects, uv sync --locked, an optional setup module, then
uv run --no-sync. Concurrent first use shares preparation by installation directory, even
across sessions. Detach and teardown release the binding, not the externally owned installation.
A failed preparation remains failed until the operator replaces the loader; no request silently
retries it. A preprepared package needs no custom setup hook.
This helper is an OS-runtime example, not a sandbox. The operator must supply an isolated runtime, package-network policy, and appropriately limited process credentials. Project paths and module names are operator configuration, not arbitrary session input. Brain does not run these commands, resolve dependencies, or fall back from incompatible Wasm placement.