Agentbox is a minimal “dumb agent” runtime designed to run offline, under a tight sandbox, and produce persistent, file-based memory in a mounted /work directory:
work/memory/events.jsonl(append-only step log)work/memory/schema.fields.jsonl(schema baseline, viajd fields)work/memory/drift.jsonl(schema drift report, viajd drift)work/memory/state.json(deterministic compaction summary)work/memory/MEMORY.md(human-readable summary)
The provided docker-compose.yml runs the container with:
- No network (
network_mode: none) - Non-root (
user: 1000:1000) - Read-only root filesystem (
read_only: true; only the/workbind mount is writable) - No Linux capabilities (
cap_drop: ["ALL"]) - No new privileges (
no-new-privileges:true) - Resource limits (pids/memory/cpu)
The runtime itself (runtime/agent.sh) additionally enforces:
- Command allowlist (see
policy/ALLOWED_COMMANDS.md) - No
sh -c ...and noevalof task content - I/O redirections restricted to
/work/...(and task steps may not write to/work/memory/...) - Absolute args must be under
/work/(any arg starting with/but not/work/is rejected)
The binaries kv, jsonl, jd, and the moltbox CLI are expected under tools/ as git submodules:
git submodule update --init --recursiveThe Docker build copies tools/ from the working tree. It must succeed offline when submodules are present.
Recommended clone:
git clone --recurse-submodules <repo-url>mkdir -p work
docker compose up --build --force-recreateBy default, the runtime reads:
/work/task.jsonif present (mounted from./work/task.json)- otherwise
/tasks/demo/task.jsonfrom the image
Publishing requires outbound HTTPS. Use the profile-enabled service:
docker compose --profile molt up --build agentbox-moltProvide the API key as a single-line file mounted under /work:
mkdir -p work/secrets
printf '%s' "YOUR_API_KEY" > work/secrets/moltbook_api_key.txtSecurity note: API keys must only be used with https://www.moltbook.com (moltbox enforces this).
Agentbox invokes the moltbox CLI by absolute path under /tools/moltbox/bin/.
When publishing is enabled, it calls moltbox post or moltbox comment using MEMORY.md as content.
Task files are JSON with:
version(must be1)stepsarray of objects with:cmd(string; allowlisted)args(array of strings; optional)stdin_path/stdout_path/stderr_path(optional; must be under/work/)note(optional; included in event schema and used by demo to show drift)
accept_baseline(optional boolean; whentruerewritesschema.fields.jsonlto match current events)publish(optional object; see below)
Parsing and validation are performed with jq, so full JSON escaping is supported.
Security note: any absolute argument starting with / must also start with /work/, otherwise the task is rejected.
"publish": {
"provider": "moltbook",
"enabled": true,
"mode": "post",
"submolt": "general",
"title": "Required for post",
"post_id": "Required for comment",
"api_key_path": "/work/secrets/moltbook_api_key.txt",
"jsonl_events": "/work/memory/molt.events.jsonl"
}Rules:
- If
publishis missing orenabled=false, no publish happens. - If
enabled=true,providermust be exactlymoltbook. api_key_pathmust exist and be a file.- For
mode=post,submoltandtitleare required. - For
mode=comment,post_idis required. MEMORY.mdmust exist (publish fails if missing).- API keys are never printed or logged.
- If
jsonl_eventsis not provided, it defaults to/work/memory/molt.events.jsonl.
Example (post):
{
"version": 1,
"steps": [
{ "cmd": "date", "args": ["-u", "+%Y-%m-%dT%H:%M:%SZ"], "stdout_path": "/work/out/publish/utc.txt" }
],
"publish": {
"provider": "moltbook",
"enabled": true,
"mode": "post",
"submolt": "general",
"title": "Daily memory",
"api_key_path": "/work/secrets/moltbook_api_key.txt",
"jsonl_events": "/work/memory/molt.events.jsonl"
}
}Example (comment):
{
"version": 1,
"steps": [
{ "cmd": "date", "args": ["-u", "+%Y-%m-%dT%H:%M:%SZ"], "stdout_path": "/work/out/publish/utc.txt" }
],
"publish": {
"provider": "moltbook",
"enabled": true,
"mode": "comment",
"post_id": "REPLACE_WITH_POST_ID",
"api_key_path": "/work/secrets/moltbook_api_key.txt",
"jsonl_events": "/work/memory/molt.events.jsonl"
}
}missing required command: jd(orkv/jsonl): initialize submodules on the host and rebuild:git submodule update --init --recursivedocker compose up --build --force-recreate
See tasks/demo/README.md for a multi-run workflow:
- baseline creation
- drift detection
- baseline acceptance
- JSON escapes
- forbidden path rejection
- publish post (Moltbook)
- publish comment (Moltbook)
- Not an LLM agent
- Not a general shell runner
- No network access, package installs, or dynamic plugins
- No “execute arbitrary script” task steps