arizuko

arizuko › products › shapes

shapes

Four products are shapes. You assemble each one from the six primitives with commands that you run yourself.

This is a shape, not a template. No tree ships for these four in ant/examples/, so arizuko create --product refuses their names. The catalog marks which products ship as templates.

Every shape needs the build part of the install: the binary, the images and the guest kernel, with DATA_DIR set to the data directory of the instance. A turn is one KVM guest, so the host needs /dev/kvm. Every shape also needs Anthropic API access. For production, run the instance as a systemd service, as getting started shows.

The supermarket price agent answers every user from one folder

“Where’s milk cheapest this week?” — the agent searches, compares, and replies with sources. One shared folder serves everyone who asks.

One folder and one route serve every user

The supermarket agent is the smallest product shape. The six steps of the pipeline are at their smallest:

Event
A Telegram message, a web chat message or a webhook POST. All arrive as rows in the same inbox. The agent does not know which channel sent a message.
Routing
One catch-all route sends every inbound message to the supermarket folder. No per-user routing and no topics exist.
Agent
The folder holds a SYSTEM.md with the allowed tools, the citation format and the refusals, and facts/retailers.md. The operator writes that file: the retailers to check, their URLs and their known problems.
Authorization
The route is the only fence. Anyone who reaches the bot or the chat URL can ask.
Turn
One short-lived guest for each question. The agent reads facts/retailers.md, searches the named retailers on the live web and replies. It caches the results in facts/cache/ by item and date, so a second question about the same item on the same day needs no search.
State
The message rows on the store server, and facts/retailers.md and facts/cache/ on disk. Nothing carries over from one user to the next.

The limit is the number of concurrent guests, MAX_CONCURRENT_CONTAINERS, default 5, not the number of users.

telegram · DM

  user   where's the cheapest whole milk this week?
  agent  Checked Tesco, Sainsbury's, Lidl (prices as of today):
         Tesco: 2L semi-skimmed £1.09 (Clubcard price £0.95)
         Sainsbury's: 2L whole milk £1.10
         Lidl: 2L whole milk £0.99

         Cheapest: Lidl £0.99.
         source: lidl.co.uk/grocery/dairy, tesco.com/groceries, sainsburys.co.uk

The shape leaves out five features

The message history is on the store server, and the cached prices are files under /srv/data/arizuko_<name>/groups/supermarket/. The outbound paths are the channel and Anthropic. Web search goes through the egress proxy of the turn.

A running instance holds the group

Start an instance named shop as the getting started guide describes.

$EDITOR /srv/data/arizuko_shop/.env                  # WEB_HOST, WEB_PORT, ANTHROPIC_API_KEY
arizuko group -i shop add web:supermarket supermarket   # the folder and its route
arizuko route -i shop add '' supermarket                # catch-all: every channel
$EDITOR /srv/data/arizuko_shop/groups/supermarket/facts/retailers.md
$EDITOR /srv/data/arizuko_shop/groups/supermarket/SYSTEM.md

An empty match matches every message. For Telegram, set TELEGRAM_BOT_TOKEN, run arizuko package add catalog:teled and restart the service. Use the narrower match 'platform=telegram' when the bot is the only entrance. Without Telegram, arizuko token -i shop issue chat supermarket creates a web chat link.

facts/retailers.md lists each retailer with its URL and notes, for example “Prices update Thursday”. The agent searches no other retailer. The shape ships no SYSTEM.md, so write one that makes the cache and the citations true:

# Supermarket price agent

Answer "where is X cheapest" questions. Read facts/retailers.md first;
search only the retailers listed there. Always cite source URLs.

Cache results in facts/cache/<item>-<YYYYMMDD>.md. Before searching,
check the cache; a file dated today answers without a new search.
Ignore cache files older than one day.

The proxy blocks every domain that the list does not allow, so allow each retailer:

arizuko network -i shop allow supermarket tesco.com
arizuko network -i shop allow supermarket sainsburys.co.uk
arizuko network -i shop allow supermarket lidl.co.uk

The proxy answers 403 for a domain that the list does not allow. To check the shape, ask a price question: the reply must cite its sources. To force new results, delete the files in facts/cache/.

The Discord team agent gives each guild member memory and grants

One agent in a Discord guild, with channel-scoped persona and thread context but per-member memory, grants, and a full web workspace.

The Slack team agent holds the shared parts

The Discord team agent has the same folder model, grants and web workspace as the Slack team agent. A Discord channel maps to a group folder, and the SYSTEM.md of the folder holds the rules of the channel. A Discord thread is a topic under the JID of its parent channel, and replies stay in the thread.

Personal cloud storage
Each member has a private workspace over WebDAV at /dav/<folder>/. A WebDAV client needs a Bearer token, as WebDAV shows.
Web chat and dashboard
A browser chat at /chat/<token>/ talks to the same agent. Members with the grants see their folders at /dash/.
Per-member memory and grants
The agent keeps one memory file for each member. Grant rows decide which actions each member can ask the agent to do.
Linked identities
The channel needs no sign-in, because membership of the guild is the fence. A member who signs in at /auth/login with GitHub, Google or Discord reaches the same memory and grants from the web chat or a DM.
#dev-chat · thread

  alice  @bot summarize the fix from the cold-start runbook for this thread
  bot    Bundle growth after deploy is coming from broad SDK imports.
         Recommended: switch back to per-service imports.
         source: /docs/runbooks/lambda-cold-start.md:67

  bob    @bot save that conclusion to my notes
  bot    Wrote notes/lambda-cold-start.md in your workspace.

  alice  @bot remember: prefer per-service imports for any new lambda
  bot    Saved to your memory.

The team sees the answer. The write of Bob goes to the workspace of Bob, and the preference of Alice goes to her memory file.

A Discord channel starts a turn only on a mention

arizuko group add for a Discord server channel writes two routes: a verb=mention route that starts turns, and an #observe route for the other messages. A reply to a bot message also counts as a mention. A DM is discord:dm/<channel_id>. The Slack team agent page lists the other differences from Slack: no AI pane, the mention format and the JIDs.

The channel files and the member memory are under /srv/data/arizuko_<name>/groups/. The tool keys live in the secrets table and stay on the host, unless a shell:env:<KEY> grant names a key. A member in one channel cannot open the files and the persona of another channel without an explicit grant.

A running instance and Discord bot support the team

Start an instance named acme. Set the token, run arizuko package add -i acme catalog:discd and restart the service. The Discord guide has the steps.

$EDITOR /srv/data/arizuko_acme/.env    # WEB_HOST, WEB_PORT, DISCORD_BOT_TOKEN, a model key
arizuko group -i acme add discord:123456789/987654321 main

No discord-team template exists, so the main group starts with the generic persona. The nearest start is ant/examples/slack-team/: copy its PERSONA.md into groups/main/ and adapt the package's CLAUDE.md as that group's SYSTEM.md. For linked identities, add GITHUB_CLIENT_ID, GOOGLE_CLIENT_ID or DISCORD_CLIENT_ID with its secret to .env.

At discord.com/developers/applications, make the application and its bot, copy the token, and enable the Message Content Intent. Without that intent, the bot gets message events with no text. Invite the bot with the OAuth2 URL generator: scope bot, and the permissions Send Messages, Read Message History, Add Reactions and Attach Files. Discord has each step.

A guild bot usually answers every DM and stays quiet in channels until someone mentions it. These rows do that for every guild, with arizuko route -i acme add <match> <target> --seq N:

seq  match                                target
-2   chat_jid=discord:dm/*                main
-1   chat_jid=discord:*/* verb=mention    main
0    chat_jid=discord:*/*                 main#observe

To check the shape, send @acme hello in a channel that the bot can see; the agent replies. When the bot answers in DMs and not in channels, the channel routes are #observe only: add a verb=mention route to main. When discd is unhealthy, the token changed or the bot left the guild: copy the new token into DISCORD_BOT_TOKEN and restart the instance. A new channel is one more group: arizuko group -i acme add 'discord:123456789/987654321' dev-chat.

The company brain runs one agent for each team

A network of per-team agents, each with its own inbox and memory, reading intake from Slack, email, and webhooks — writing briefs, escalating issues, coordinating across folders on schedule.

The company brain acts on knowledge and does not search it

Agents read intake, write daily briefs, escalate issues to the right team and coordinate across the folder tree. The agents search facts/ with grep and have no vector index.

Folder tree as org chart
Each team gets its own group folder: corp/eng/sre is the SRE team, and corp/intake is the shared desk for inbound triage.
Memory for each team
Each folder holds a PERSONA.md, a facts/ knowledge base and a diary. Context stays in the team until an agent delegates or escalates.
Scheduled briefs
A timed task starts a turn on a schedule: a daily standup summary, a weekly digest or an on-call handoff. No cron on the host is necessary.
Many input sources
Slack messages, inbound email through emaid, webhook POSTs from GitHub, Sentry, Linear or PagerDuty, and WebDAV file drops all arrive as events in a team inbox.
Escalation and delegation
The SRE agent calls escalate_group to reach corp/eng. A triage agent calls delegate_group to give work to the responsible team.
Observed channels
A Slack channel can route to corp/eng#observe. The agent stores the messages, starts no turn, and finds them later with find_messages.
ACL for each folder
The acl rows of a folder decide which MCP tools and which external hosts its agent can reach. Other teams cannot read the memory, facts and diary of a team without a delegation or a grant.
[Sentry webhook fires]
  POST /hook/<token>
  {"issue": "OOM in billing-worker", "level": "error"}

  ↓ Event lands in corp/eng/sre/oncall
  ↓ Agent reads alert + facts/runbooks/billing-worker-oom.md
  ↓ Checks find_messages for recent occurrences (last 7 days)

  ↓ Sends to #eng-oncall Slack channel
  sre-bot   OOM in billing-worker — seen 3x this week.
             Last fix (2026-05-30): heap limit too low post-deploy,
             fix was rolling restart + config bump.
             Action: restart billing-worker pod + check HEAP_MAX.

  ↓ Creates task: "Raise HEAP_MAX cap — billing-worker"
  ↓ Logs to diary: alert handled, task created

Three limits apply

No connector sync
You fill facts/ by hand, over WebDAV, or through agents that a webhook starts. No OAuth crawler for Confluence, Notion or Google Drive exists.
No embeddings and no vector search
The agent searches facts/ with grep. find_messages is a BM25 search in memory over the stored messages, not over facts/. For tens of thousands of documents, the agent calls a retrieval service over MCP.
No permissions from source systems
arizuko does not copy Jira roles, Salesforce profiles or Confluence space permissions. The two permission systems are separate, and the operator manages both.

The setup makes folders, routes and tasks

Start an instance named corp. Set the Slack keys, run arizuko package add -i corp catalog:slakd and restart the service. The Slack guide has the steps.

$EDITOR /srv/data/arizuko_corp/.env     # SLACK_BOT_TOKEN, SLACK_SIGNING_SECRET, ANTHROPIC_API_KEY

# one folder for each team: the JID first, then the folder; the route is written for you
arizuko group -i corp add 'slack:T012ABCD/channel/C0ONCALL' corp/eng/sre/oncall
arizuko group -i corp add 'slack:T012ABCD/channel/C0ENG'    corp/eng
arizuko group -i corp add 'web:corp/intake'                 corp/intake
arizuko route -i corp add '' corp/intake --seq 10           # catch-all for unrouted messages

# webhook intake: a hook token, then a route for its JID
arizuko token -i corp issue webhook corp/eng/sre/oncall sentry
arizuko route -i corp add 'chat_jid=hook:corp/eng/sre/oncall/sentry' corp/eng/sre/oncall --seq -10

$EDITOR /srv/data/arizuko_corp/groups/corp/eng/sre/oncall/facts/runbooks/billing-worker-oom.md

A lower seq comes first, so put specific routes before wide ones. The hook URL is https://brain.example.com/hook/<token>, and the body arrives unchanged in the target folder. Webhooks explains signature checks.

No CLI subcommand for tasks exists. Add tasks on the tasks page of /dash/, or ask the agent, which writes the row with the schedule_task MCP tool:

@corp-brain schedule a recurring task:
  folder: corp/eng   cron: "0 9 * * 1-5"
  task: write daily standup summary to facts/briefs/today.md and send to #eng-standup

Make each task instruction say what to produce, where to write it and where to send it. A brief with no send step writes to facts/ and stays silent. When a webhook arrives and no turn starts, compare arizuko route -i corp list with the JID in the hook inbound log line. A grant gives a person admin over a folder subtree: arizuko group -i corp grant google:cto@acme.com 'corp/**'. Grants has the full rule syntax.

Marble answers questions over a dataset in its folder

“What does a child need to learn before fractions?” — the agent traces the prerequisite graph of an open education dataset and answers in plain words. One folder, one bundled dataset, a public chat widget anyone can open.

A public link reaches a read-only dataset

Marble is a read-only knowledge bot over a fixed dataset. The supermarket agent searches the live web at each turn. Marble queries a dataset inside its folder, with no outbound fetch for data and no per-user state.

Event and routing
A message from the public web chat arrives in the inbox of the folder. A /chat/<token>/ link is the only entrance, and one route maps the JID of the chat to the marble folder.
Agent
The folder holds a SYSTEM.md and a taxonomy/ directory with topics.json, dependencies.json and clusters.json. The agent answers with jq over those files.
Authorization
One public chat token. Anyone with the link can ask.
Turn and state
One short-lived guest for each question. The agent reads the JSON and writes nothing back. The only persistent state is the message log.
web chat · public link

  visitor  what does a child need before fractions?
  marble   Before fractions, these foundations come first:
           whole-number place value, equal sharing / partitioning,
           multiplication as equal groups, and division as sharing.
           Each is a prerequisite edge into "Introduce fractions".

The example uses the Marble Skill Taxonomy v1: 1,590 micro-topics for grades K to 5, connected by 3,221 prerequisite edges across 8 subjects. The taxonomy is © Marble, withmarble.com, under CC BY-SA 4.0. Keep the attribution in the SYSTEM.md and cite it in replies.

Nothing is specific to Marble: replace taxonomy/*.json with any dataset, describe it in SYSTEM.md, and the same bot answers over your data.

Five commands set it up

arizuko group -i <instance> add web:<folder> <folder>
mkdir -p /srv/data/arizuko_<instance>/groups/<folder>/taxonomy
cp topics.json dependencies.json clusters.json \
   /srv/data/arizuko_<instance>/groups/<folder>/taxonomy/
$EDITOR /srv/data/arizuko_<instance>/groups/<folder>/SYSTEM.md
arizuko token -i <instance> issue chat <folder>    # the public chat link

The agent reads the dataset from ~/taxonomy/ in its guest. The files must have the same owner as the rest of groups/. Keep SYSTEM.md short, because the agent reads every line at each turn: who it is, what it answers, how it cites, and the attribution.

Set the model at /dash/groups/<folder>/settings → Model → Claude Haiku 4.5. The setting writes the model into the config of the group, and runed gives it to the guest as ARIZUKO_MODEL at the next turn. On the same page, keep self and hello and turn off the other skills. Each turn starts a new guest with no prompt cache, so fewer skills make a smaller prompt. The dashboard writes a .disabled marker in the directory of each skill that you turn off.

To check the shape, curl -sI https://<WEB_HOST>/chat/<token>/ | head -1 returns 200, and a question in the chat gets a reply in a few seconds.