Operating a live Aether engine
This part of the guide is the runbook for an agent—or a human at the same controls—operating Aether through the MCP harness. It starts after code has been built and answers four practical questions:
- Which process owns the thing I am looking at?
- Which identifier is safe to reuse?
- What evidence can I collect without changing the engine?
- What must I clean up when the task ends?
For the transport topology and connection setup, begin with
The MCP harness. The pages here assume its tunnel,
aether-mcp, and hub are reachable.
The operating model
The harness has four ownership layers. Confusing adjacent layers is the most common source of bad recovery decisions.
| Layer | Owns | Survives a hub restart? | Operator handle |
|---|---|---|---|
| MCP client session | tool calls and returned evidence | the tunnel normally does; an aether-mcp restart does not | MCP connection |
| Hub | supervised engine table, engine proxies, artifact store | fleet: no; stored artifacts: yes | hub-local tools |
| Substrate | one live engine, native caps, live kind and mailbox registries | no | engine_id |
| Loaded component | one wasm actor behind a substrate mailbox | no | lineage address |
Keep three nouns separate:
- Stored means bytes and a manifest exist in the hub's content-addressed
artifact store.
list_binariesand registrylist_componentsreport this. - Running means a substrate is in the hub's current supervised fleet.
list_enginesreports this. - Loaded means a component actor is registered inside one running
substrate.
load_componentcreates it;describe_componentinspects it.
A stored component is not loaded. A loaded component on engine A says nothing
about engine B. An old engine_id says nothing about a new hub process.
The safe operating loop
Use this loop even for a short probe:
-
Start with
list_enginesand decide whether to adopt an existing engine or create one. -
Before using any selector, prove the artifact is stored with
list_binariesor registrylist_components; otherwise upload it first. -
Record the exact
engine_idreturned byspawn_substrate. -
Record each explicit load's returned lineage
address; for boot loads, record the configured name used to derive its lineage. -
Inspect the live surface before guessing mail:
describe_kinds,describe_handlers, ordescribe_component. -
Prefer settled
send_mail; usesend_mail_tracedwhen causal timing or whole-chain proof matters. - Collect logs, traces, costs, and frames before destructive recovery.
-
Drop task-owned components and require their successful
drop_result, or terminate task-owned engines when done. -
After termination, call
list_enginesagain and verify the engine cleanup is visible. A component drop is verified by its result, not the fleet list.
The tool's live JSON schema is the parameter reference. This guide names the current tools and their contracts, but does not duplicate every field because that surface evolves.
Choose the next page
| If you need to… | Read |
|---|---|
| restart or stop harness processes, or decide who may | Harness lifecycle |
| adopt, spawn, identify, or terminate substrates | Engine fleet |
| pass a host path, upload code, or persist evidence | Host paths and artifacts |
| upload, select, load, replace, inspect, or drop wasm | Component registry |
| learn the live schema or gather diagnostic evidence | Inspect and debug |
| respond to a failure without making it worse | Recovery |
The subsystem explanations remain useful alongside these runbooks:
- Components & lifecycle explains the wasm host and trampoline model.
- Mail, kinds & scheduling explains addresses and payload schemas.
- Tracing & settlement defines what “settled” proves.
- Logging explains what reaches actor log rings.
Ownership rules
Ownership is operational, not merely descriptive:
| Resource | Default owner | Cleanup rule |
|---|---|---|
| Engine you spawned | your task/session | call terminate_substrate on its exact engine_id |
| Engine you merely found | whoever created it | do not terminate without explicit authority |
| Component you loaded into a shared engine | your task/session | send the component drop mail when safe |
| Component loaded at boot into your engine | your engine | terminating the engine cleans it up |
| Named artifact in the hub store | hub operator | names persist and protect their current target from LRU eviction |
| Per-engine scratch/handle-store directory | hub operator | termination does not expose directory cleanup through MCP |
| Evidence file returned by a spill or capture | caller | preserve or remove it deliberately; it is host-side state |
There is no current MCP delete tool for stored binaries or components. Reusing an upload name repoints that name to the new hash; it does not erase the old bytes. If no other name points at the old hash, it becomes unnamed history and can later be evicted under the store's disk budget. Do not promise registry cleanup that the tool surface cannot perform.
Component drop closes that one instance: its guest's unwire runs, its name
tombstones, and a later load of the same name is refused as retired (ADR-0241
§8). Engine termination ends every instance at once.
Identifier discipline
- Treat
engine_idas a hub-lifetime routing handle. The hub allocates it from process-local state and only guarantees uniqueness in its current fleet. A later hub can mint the same text for a different engine. - Treat
rpc_portas observation, not identity. Never route a tool call by substituting a port for anengine_id. - Hand tagged ids such as
knd-…andhdl-…back verbatim. Address an actor by its path; a mailbox id in output is for reading. - Address a loaded component by the full lineage
namereturned by the load, its published name:NS, orNS:keyfor an instanced type. - Prefer a content hash when a rollout or rollback must select exact bytes. A human-readable registry name is a movable pointer.
Mutation posture
Read-only discovery should precede mutation. list_*, describe_*,
actor_logs, and actor_cost are the normal first calls. capture_frame is
observational to the engine when used without mail bundles; its mails and
after_mails fields make it engine-mutating. Independently, save_path creates
parent directories and can overwrite a host file even when both bundles are
empty. send_mail, component lifecycle calls, spawn, replace, and terminate all
change live state. Host paths have an independent authority boundary; follow
Host paths and artifacts before uploading native
code or persisting a frame.
Fire-and-forget is not a faster form of verification. It proves that the call was written, not that the engine completed the work. Use it only when the recipient intentionally has no useful reply or when a later observation is the actual success criterion.
Source routes
When prose and behavior disagree, inspect these routes in order:
- MCP tool names and user-facing contracts:
crates/aether-mcp/src/tools/mod.rs - Live JSON argument and response shapes:
crates/aether-mcp/src/args.rs - Fleet and component orchestration:
crates/aether-mcp/src/tools/engine.rsandcomponents.rs - Hub fleet ownership and engine death handling:
crates/aether-fleet/src/server/runtime.rsandcrates/aether-fleet/src/proxy/runtime.rs - Stored artifacts:
crates/aether-fleet/src/store/ - Live kind inventory:
crates/aether-inventory/src/ - Loaded component registry and lifecycle:
crates/aether-component/src/component/