Skip to content

Project structure

A Zooid project — sometimes called a workforce — is a directory on your disk. Everything zooid needs lives inside it: the configuration, secrets, and per-agent workspaces. This guide walks the layout and explains what each piece is for.

The layout

my-zooid/
zooid.yaml # workforce config: agents, transports, runtime
.env # API keys and secrets (gitignored)
.gitignore # ignores .env and data/ by default
agents/
architect/ # one agent's workspace
dev/
qa/
data/ # runtime state: homeserver db, sessions, logs (gitignored)

The directory containing zooid.yaml is the workforce root. Run zooid dev (or zooid start) from there. Agents are scoped to their own subdirectory under agents/<name>/ — that’s the agent’s working directory, the only place it can write files.

zooid.yaml

Workforce-as-code: the single source of truth for which agents exist, which rooms they join, what model they run on, and what runtime hosts them. See the Quickstart for a minimal example.

.env

Provider API keys (Anthropic, Vertex, OpenAI, …) and any other secrets your agents need. zooid dev also writes the auto-generated Matrix Application Service tokens here. Never bind-mounted into agent containers — the runtime’s mount allowlist excludes it on purpose.

agents/<name>/

Each agent’s working directory. The agent reads and writes files here and nowhere else. Drop project files the agent needs into its own folder; if multiple agents share files, see the shared/ reserved namespace.

If you want to give an agent its own role prompt or project conventions, the shim’s standard convention files work natively: a CLAUDE.md in agents/architect/ is picked up by Claude Code via upward-traversal, an AGENTS.md is picked up by opencode and codex, and so on. These are not zooid-specific — zooid just makes sure they reach the agent’s filesystem on both the local and docker runtimes. The same applies to a CLAUDE.md / AGENTS.md at the workforce root: optional, user-authored, inherited by every agent in the workforce.

data/ — runtime state

Anything zooid generates while running goes into the persistent data root, which defaults to data/:

data/
matrix/ # tuwunel-mounted state (config, db, media, registrations)
logs/ # daemon + tuwunel logs
agents/ # per-agent daemon state
<agent-id>/
sessions.json
  • matrix/ contains the Tuwunel homeserver database and configurations.
  • logs/ acts as a unified log directory for the daemon and the Tuwunel container.
  • agents/ stores per-agent runtime state, such as sessions.json holding active ACP sessions.

It’s recreated on demand, gitignored by default, and safe to delete between runs if you want a clean slate. You can override the root directory via the --data CLI flag.

.gitignore

zooid init seeds a .gitignore that excludes .env, data/, node_modules/, and .pi-agent/ — the things you never want in version control. (.pi-agent/ is the relocated pi config directory the pi preset scaffolds; on the shared-login path it holds a link to a 600-mode token file.) The rest of the workforce (config, agent workspaces, anything project context you author) is meant to be committed: that’s how you share a workforce with collaborators or move it between machines.

What zooid creates for you

Running zooid dev (or zooid init) on a fresh directory seeds zooid.yaml, .env, .gitignore, and an agents/zooid-assistant/ workspace with sensible defaults. That workspace includes a starter AGENTS.md describing the assistant’s job, plus whatever the chosen preset needs to boot:

PresetAlso seeded under agents/zooid-assistant/
claudeCLAUDE.md (imports AGENTS.md), .claude/settings.json
codex— (AGENTS.md is read natively)
opencodeopencode.json
pi.pi-agent/settings.json

Beyond that, zooid seeds no shim convention files — extra CLAUDE.md or AGENTS.md files for agents you add yourself are entirely up to you, the same way they would be in any other project. Everything zooid does seed is user-editable and won’t be rewritten on subsequent runs.