Packages: container/, groupfolder/, mountsec/.
Each agent invocation follows this sequence:
arizuko-* containers left from a previous crashgroupfolder.Resolver maps the group folder name to an absolute host pathsettings.json to the session's .claude/ directory: env vars, arizuko MCP server config via socat, sidecar MCP entriescontainer/skills/ to the session on first run (non-destructive; skips files that exist). Also seeds .claude.json if missing (required by Claude Code; keyed by folder for a stable userID hash)docker run -d)docker stop then Process.Kill if exceededContainer run logs are written to groups/<folder>/logs/container-<timestamp>.log.
| Host path | Container path | Mode | Purpose |
|---|---|---|---|
groups/<folder>/ | /workspace/ | rw | Group working directory ($HOME) |
groups/<world>/share/ | /workspace/share/ | rw | Cross-group shared state within a world |
data/sessions/<folder>/ | /root/ | rw | Agent session state (.claude/ dir) |
data/ipc/<folder>/ | /var/run/pub/arizuko/ | rw | MCP unix sockets |
container/skills/ | /skills/ | ro | Skill CLAUDE.md files (read by agent) |
groups/<folder>/media/ | /workspace/media/ | rw | Received media files |
web/ (optional) | /workspace/web/ | ro | Web assets (when web channel active) |
| extra mounts | /workspace/extra/<name>/ | rw or ro | Additional mounts from container_config |
Written to the container's stdin as a single JSON line:
{
"sessionId": "session ID or empty for new session",
"messages": [ { "role": "...", "content": "..." } ],
"systemPrompt": "prepended XML blocks (diary, episodes, grants, etc.)",
"grants": [ "rule1", "rule2" ],
"folder": "group folder name",
"senderJid": "JID of the originating sender"
}
The container writes all output to stdout. The gateway ignores all lines until it sees the start marker, then captures until the end marker:
---ARIZUKO_OUTPUT_START---
{"status":"ok","result":"agent reply text","newSessionId":"...","error":""}
---ARIZUKO_OUTPUT_END---
During the run, the gateway polls the IPC socket for tool call results and pipes responses back via stdin. A _close sentinel on the IPC input channel signals the container to flush and exit.
Status values: ok (success, cursor advances), error (failure with output, cursor advances), fatal (no output, cursor rolls back).
arizuko-<folder>-<timestamp_ms>arizuko-<folder>-task-<task_id> (sender: scheduler-isolated:<task_id>)seedSettings writes settings.json into the session's .claude/ directory before each invocation. It contains:
arizuko MCP server entry pointing to socat UNIX-CONNECT:/var/run/pub/arizuko/router.sockseedSkills runs once per group (non-destructive). It copies the bundled container/skills/ tree into the session's .claude/skills/ directory. Files are skipped if they already exist, so operator customizations are preserved.
Skills versioning: the bundled skills carry a MIGRATION_VERSION file. On startup, the gateway compares the installed version against the bundled one. If the bundled version is higher, it applies the numbered migration files (NNN-desc.md) in order, which may overwrite specific skill files.
Per-group MCP sidecars are defined in GroupConfig.Sidecars. For each sidecar:
StartSidecars runs docker run -d with the sidecar image, socket volume at ipc/sidecars/<name>.sock, memory limit, and CPU limitsettings.json as an additional mcpServers entryStopSidecars calls docker stop then docker rm -f after the agent container exits--cap-drop ALL
--security-opt no-new-privileges
--memory 1g
--cpus 2
--network none (default; overridable per group)
--read-only (root filesystem; writable mounts are explicit)
Package: mountsec/. Additional mounts from container_config are validated against ~/.config/pub/arizuko/mount-allowlist.json before the container starts:
AllowedRoot.ssh, .gnupg, .env, credential files, private keysNonMainReadOnly is configuredWhen the gateway itself runs inside Docker, container.hp() translates local paths to host-side paths. HOST_DATA_DIR provides the host-side base; HOST_APP_DIR maps the application directory. Volume mounts passed to docker run use the translated paths so the inner Docker daemon can resolve them.