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.jsonmatrix/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 assessions.jsonholding 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:
| Preset | Also seeded under agents/zooid-assistant/ |
|---|---|
claude | CLAUDE.md (imports AGENTS.md), .claude/settings.json |
codex | — (AGENTS.md is read natively) |
opencode | opencode.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.