Shape an agent system by talking to it. You say what you want; an LLM edits the files that are the system — persona, memory, skills, routing, even an operational fix. The conversation is the console; there’s no separate config screen.
What keeps that safe is the daemon structure. Each part runs in its own
compartment — routd routes and authorizes,
runed runs the agent, authd issues identity and
tokens — behind hard walls: folder scoping, a permission gate on
every action, a fresh container per turn. So shaping by language stays inside the permissions you set; it
never becomes a black box you can’t audit.
And every change lands as files you own. The config is text you can
diff, review in a PR, and roll back with git revert, on
your own server. Language makes it easy to shape; the compartments keep
it bounded; ownership makes it permanent.
What you build this way is products you own — a Slack team agent, a personal life agent, a company brain. Each is a folder of files: persona, memory, skills, permissions. The same fixed pipeline runs all of them; only the folder and the routing change. Not a chatbot you rent, and not an opaque SaaS agent you tune through a text box.
github.com/kronael/arizuko, MIT.It looks like a lot — channels, tasks, webhooks, secrets, delegation. It isn’t. Every one of those is the same six-step pipeline with different folder contents. Trace one Slack mention top to bottom:
Slack @mention in #eng
→ Event one inbox row
→ Routing → corp/eng/oncall
→ Agent folder loaded: persona, skills, memory
→ Authorization may it read / send / delegate here?
→ Turn one container run, then it exits
→ State rows written, files edited
→ reply in the thread
A scheduled task is the same pipeline — the event is a clock tick. A webhook is the same pipeline — the event is a POST. Nothing here is a special case, so the surface stays small: the parts don’t multiply.
Those parts stack into what you run and what you ship:
| Layer | What it is | Examples |
|---|---|---|
| Primitives | the concepts | Event, Routing, Agent, Authorization, Turn, State |
| Components | Go packages | store, router, auth, runed |
| Daemons | running processes | authd + routd + runed; webd, timed, slakd |
| Products | agents you install | Slack team agent, reality agent, company brain |
A product sits at the bottom: the same pipeline, a different folder. That’s why a support agent and a personal life agent run the same code — the difference is what’s in the folder, not the machinery underneath. See the six primitives →
An agent is a folder of files the LLM loop reads. The persona is a
file. Memory lives on disk. Skills are directories. Config is a file
you can diff, review in a PR, and roll back with git revert.
The org chart is the folder tree.
Each folder is a wall: corp/sales doesn’t see
corp/eng/sre’s memory. Change the persona by editing
a file; the next session picks it up. Add a skill by dropping a
directory. You can read everything an agent knows, audit it, and
swap it out.
Agents need structure the same way people do — enough to know what they can reach, what their job is, and who they answer to. Small parts with one job each give them that: nothing hidden, parts that fit together. A system a person can reason about is one an agent can manage. That rule drives the whole design.
arizuko gives each team its own agent, memory, channels, and policy
on your own host. Config is files. Rollback is git revert.
A folder is an agent: PERSONA.md, skills/,
memory files, and an ACL row. When a message arrives, arizuko starts
a Docker container for that folder. The container connects to
routd over MCP on a unix socket. Every tool call goes
through that socket; the host controls what the agent can reach. Every
platform operation is an MCP tool and a REST endpoint — no
separate agent API. All state is in SQLite WAL; the container is
stateless.
The stack is for agent teams: dozens of messages a day, not
consumer-scale traffic. Go binaries, Docker Compose, SQLite WAL,
proxyd signing identity headers — no Auth0, no
Redis, no Vault.
corp/sales, corp/eng/sre,
corp/support — each a directory with a prompt
file, a memory file, and a skill set. Add a folder, get an agent.
PERSONA.md (voice and instructions), a
skills/ set, and memory that accumulates across weeks.
An agent that helped close a deal in January remembers it in March.
facts/.
When an agent researches something mid-conversation, it saves the
findings to its own facts/. The next session, it already
knows. No admin step, no sync job.
delegate_group, escalate_group, and
observe_group are the same MCP tools a human operator
uses.
/hook/<token> from any external system —
the payload lands as a message in any folder. Pair with a scheduled
curation agent: no custom integration code, just a prompt and a cron.
slakd,
AI assistant pane), Reddit (reditd), X/Twitter
(twitd), and LinkedIn (linkd).
/chat/<token>/ page
(SSE, works in a browser) and a /hook/<token>
endpoint (POST JSON, fires the agent). No extra config.
routd.db.
Routes match on key=glob. ACL is one row:
(principal, action, scope, params). Siblings see each
other’s observed messages unless the group is closed. Channels
speak HTTP. No hidden state elsewhere.
routd over an MCP unix socket bridged into the container.
Tool calls go through that socket; the host controls what the agent
can reach. You can audit, revoke, or scope every side effect by the
folder’s grant rules.
/dash/ — routes
editor, task scheduler, per-group model selector (Opus /
Sonnet / Haiku), skill toggles, grants editor, invite
management, usage metrics, and secrets (AES-256-GCM encrypted at
rest). No editing .env by hand to rewire a route.
crackbox is the
containment boundary for each agent run — its own container, a
network with no default route, egress only through a per-folder
hostname allowlist (HTTPS via CONNECT, DNS filtering). A compromised
agent reaches only what you explicitly permit.
A minimal deploy is the three core daemons (authd,
routd, runed) and one channel adapter. Add
components as you need them — each is a standalone binary with its
own TOML block. There’s no managed control plane; every piece is
replaceable.
| Setup | What runs | Best for |
|---|---|---|
| Minimal | authd+routd+runed + one adapter |
First deployment, one team |
| Standard | + dashd + timed |
Ongoing ops, scheduled tasks |
| Multi-team | + onbod + crackbox |
Isolated teams, egress control |
| Prebuilt recipe | Pre-seeded config | slack-team, reality — 5 minutes |
One tar of /srv/data/arizuko_<name>/
backs up the entire instance: the daemon DBs (routd.db,
runed.db, auth.db — all WAL), group
folders, per-user memory, secrets, agent files.
# seed the instance directory
arizuko create demo
# /srv/data/arizuko_demo/ now has .env, store/, groups/, ipc/, web/pub/
# pick adapters + tokens
$EDITOR /srv/data/arizuko_demo/.env
# register the first group's inbound JID
arizuko group demo add tg:-123456789 main
# render docker-compose.yml from .env + extras, then bring it up
arizuko run demo
# systemd unit: arizuko_demo (sudo systemctl status arizuko_demo)
# talk to the default group from a browser
arizuko token demo issue chat main # prints a /chat/<token>/ URL
# or from a script — same endpoint, JSON in / JSON out
curl -X POST https://example.com/chat/<token>/ \
-H 'Content-Type: application/json' \
-d '{"text":"hello"}'
routd, runed, authd,
webd, proxyd, dashd,
onbod, timed, plus the channel adapters.
v0.49.0 · MIT ·
Go + SQLite (WAL) + Docker · running on
krons, marinade, sloth.