Docker environment
Run a crouter daemon in a container with the separate Docker environment package.
Docker environment
Phase 1.
@north-light/crouter-env-docker runs a crouter daemon in a container and tells you how to reach it. It is a separate package because container lifecycle is real work the SDK does not otherwise do — and it has no dependencies, including on the SDK itself.
npm i @north-light/crouter-env-docker
Starting a container and connecting to it
import { start } from '@north-light/crouter-env-docker';
import Crouter from '@north-light/crouter-sdk';
const env = await start({ volume: 'my-agent-home' });
const client = new Crouter(env.connection());
await client.nodes.create({ prompt: 'Do the thing.', root: true });
await env.stop();
connection() returns { baseURL, headers } — exactly the shape the Crouter constructor takes, so the result passes straight through with no adapter.
Methods
| Method | What it does |
|---|---|
| start(opts?) | Starts a container running crtrd and returns an environment handle. |
| attach(name) | Returns a handle for the already-running container named name. |
| stop() | On a start() handle, stops and removes the container. On an attach() handle, stops it without removing it. |
| connection() | Connection ({ baseURL: string; headers?: Record<string, string> }) — returned synchronously and passed to new Crouter(). |
| start() option | Type | Default | Effect |
|---|---|---|---|
| image | string | ghcr.io/vallum-security/crtrd:latest | Image to run. |
| env | Record<string, string> | unset | Extra container environment variables; they are not logged. |
| name | string | generated name | Container name for later attach(name). |
| volume | string | unset | Named volume mounted at /home/agent/.crouter. |
| port | number | Docker-assigned port | Host port bound on 127.0.0.1. |
connection() replaced daemon()
daemon() is gone, along with the package's Environment type. The new name says what the method returns, and its field is spelled baseURL to match the constructor option rather than baseUrl.
// before
const env = await start({ volume: 'my-agent-home' });
const { baseUrl, headers } = await env.daemon();
const client = new CrtrClient({ baseUrl, headers });
// after
const env = await start({ volume: 'my-agent-home' });
const client = new Crouter(env.connection());
connection() is typed structurally — { baseURL: string; headers?: Record<string, string> } — which is how the package stays dependency-free while producing something the SDK accepts directly.
The volume is the agent's home
The volume you pass is the container's canvas home: nodes, transcripts, artifacts, memory, and profiles all live there. Reuse the same volume across start calls and the agent keeps everything it learned; use a fresh one and it starts from nothing. start() mounts an existing named volume unchanged; it does not inspect or reject it based on the crouter version that last wrote it.