Getting started
This guide runs the server with its deterministic offline model and sends one message through the full environment → agent → session → event flow.
Requirements
- Docker with Compose
curljqfor the shell examples
Run the server
make local-up
make local-health
This builds and starts separate API and worker containers plus PostgreSQL,
Temporal, Temporal UI, and NATS. The examples use http://localhost:8080; the
Workflow explorer is at http://localhost:8233. No model credentials are
required because the worker defaults to the deterministic offline model.
In another shell, verify readiness:
curl -i http://localhost:8080/readyz
Create an environment
Environment records identify where a session runs. The implemented cloud
record routes work to the Temporal worker:
ENV_ID=$(
curl -sS http://localhost:8080/v1/environments \
-H 'content-type: application/json' \
-d '{"name":"local","config":{"type":"cloud"}}' |
jq -r .id
)
self_hosted environment sessions are not implemented and return 422.
Create an agent
AGENT_ID=$(
curl -sS http://localhost:8080/v1/agents \
-H 'content-type: application/json' \
-d '{
"name": "Example agent",
"model": "offline-fake",
"system": "Be concise."
}' |
jq -r .id
)
An agent is versioned. Updating it creates a new version; sessions retain the resolved version and configuration captured at creation time.
Create a session
SESSION_ID=$(
curl -sS http://localhost:8080/v1/sessions \
-H 'content-type: application/json' \
-d "{
\"agent\": \"$AGENT_ID\",
\"environment_id\": \"$ENV_ID\",
\"title\": \"First session\"
}" |
jq -r .id
)
Send a message
curl -sS "http://localhost:8080/v1/sessions/$SESSION_ID/events" \
-H 'content-type: application/json' \
-d '{
"events": [{
"type": "user.message",
"content": [{"type":"text","text":"hello"}]
}]
}' | jq
Sending an event admits durable work and returns the accepted input events. The agent response is asynchronous. Poll history:
curl -sS \
"http://localhost:8080/v1/sessions/$SESSION_ID/events?order=asc" |
jq
A completed turn includes agent.message followed by
session.status_idle.
Stream events
Open the stream before sending the next message:
curl -N \
"http://localhost:8080/v1/sessions/$SESSION_ID/events/stream?event_deltas%5B%5D=agent.message"
The opt-in adds ephemeral event_start and event_delta frames while text is
generated. The final agent.message is authoritative and persisted; preview
frames are not. NATS carries low-latency wakeups and previews, while every
persisted event is reconciled from PostgreSQL by sequence.
Use a real model endpoint
The quick start already runs an offline worker. Stop it before launching a source worker with different model or sandbox configuration:
docker compose -f deployments/local/compose.yaml stop worker
export MANAGED_AGENT_DATABASE_URL="postgres://postgres:postgres@localhost:5432/managed_agent?sslmode=disable"
export MANAGED_AGENT_TEMPORAL_HOSTPORT="localhost:7233"
export MANAGED_AGENT_NATS_URL="nats://localhost:4222"
export MANAGED_AGENT_MODEL_BASE_URL=https://api.example.com
export MANAGED_AGENT_MODEL_API_KEY=replace-me
export MANAGED_AGENT_MODEL_ID=claude-model-id
export MANAGED_AGENT_MODEL_AUTH=x-api-key # or authorization-bearer
# A real model must not run against the local sandbox (it is a dev-grade
# guardrail, not a security boundary), so select the Docker sandbox for real
# isolation. The server refuses to start with a real model + local sandbox.
export MANAGED_AGENT_SANDBOX=docker
go run ./cmd/managed-agent orchestrate
The provider name is validated strictly. The compiled choices are local,
docker, e2b, cube, opensandbox, and daytona; an unknown value fails
worker startup and never silently falls back to local host execution. Remote
provider variables and live-test commands are listed in
Sandbox backends.
The configured endpoint must expose an Anthropic-shaped /v1/messages API.
Do not run workers with different model or sandbox configuration on the same
Temporal Task Queue. Keep credentials in the environment and never commit them.
If you understand the risk and deliberately want a real model against the local
sandbox during development, set MANAGED_AGENT_ALLOW_UNSAFE_LOCAL_SANDBOX=1 to
override the startup guard. This is a dev-only escape hatch — the local sandbox
runs tool commands on the host with no isolation, so never use it with untrusted
input or in production.
Reusable local credentials
Development secrets should not be committed or copied between worktrees. Create a user-local file once, then edit the values you actually use:
make dev-env-init
$EDITOR ~/.config/mango/dev.env
Run a command with that environment explicitly:
scripts/with-dev-env go run ./cmd/managed-agent orchestrate
The wrapper requires the file to have no group or other permissions. Set
MANGO_ENV_FILE only when a different repository-external path is needed.
Clean up
make local-down
This keeps the PostgreSQL volume. Add VOLUMES=1 only when you intentionally
want to delete local data. PostgreSQL schema changes are applied by embedded,
versioned goose migrations when API or worker processes start.