arizuko

arizuko › components › onbod

onbod

onbod greets an unrouted chat with a pairing link, admits the paired person through the gates, and writes the route that the person chooses.

onbod turns an unknown chat into a routed chat with consent

onbod is the sign-up desk. When a new person arrives, onbod decides whether to let the person in and helps the person get a space for the agent.

onbod is the onboarding daemon. It greets an unrouted chat with a pairing link, admits the paired person through the gates, and writes the route that the person chooses. A new user gets in through an invite token or through the admission queue. When a user may create a workspace, onbod builds the group folder with container.SetupGroup, the one function that creates a group.

onbod owns the invites, onboarding and onboarding_gates tables and creates them. The identity rows live in authd.

A new user has nothing until onbod admits them

onbod solves two problems. First, a new user who reaches the bot has nothing: no identity, no folder, no skills, no settings and no default tasks. A program must confirm the identity, decide whether to admit the user and bring the user online.

Second, a group is not a mkdir. container.SetupGroup creates <groups>/<folder>/ and logs/, and copies seedDir in if one is given. It puts the standard skill set into .claude/skills/, writes default settings and gives everything to the container UID. A folder that misses any step is a folder the agent cannot use.

The rule is strict: an operator never creates a group with mkdir. Create it through onbod, the arizuko CLI or the dashboard, which all call SetupGroup, so the folder gets the full set of files.

onbod sits between routd, webd and the browser

a chat that no route claims sends a message
        │
        ▼
      routd   holds the chat; POST onbod /v1/onboarding
        │
        ▼
      onbod   poll loop: create a pairing token, send the greeting
        │     with https://<host>/pair/<token> through routd /v1/outbound
        ▼
      webd    /pair/<token>: login, confirm page, then the pairing edge
        │
        ▼
      onbod   admitJID: no gate ► approved; a gate matches ► queued;
        │     gates but no match ► refused; the chat is told
        │     admitFromQueue about every 60s ► approved, per-gate daily limit
        ▼
      browser /onboard: pick or create a world ► onbod writes the route

Rows in onboarding_gates control admission. You manage them with arizuko gate or /v1/gates. A gate key names an identity provider, such as *, google:domain=example.com or email:domain=example.com, and can carry a daily limit. A github: gate matches every GitHub account and does not check the organisation. A user who matches no gate is refused when gates exist. A person who already has a world pairs a second chat to the same account and gets no new group.

onbod creates no tokens. authd signs every token, and webd writes the pairing edge when the person confirms. onbod calls routd with a service:onbod token that it gets from authd in exchange for AUTHD_SERVICE_KEY.

onbod runs alone only with a routd

onbod runs as a daemon against its own tables, the tables of routd, and the outbound API of routd. It does not run without ROUTER_URL pointing at a reachable routd, because the poll loop sends greetings through routd and not directly to platforms.

export DATA_DIR=/srv/data/arizuko_demo
export ONBOD_LISTEN_ADDR=:8080
export ROUTER_URL=http://routd:8080
export AUTHD_URL=http://authd:8080
export AUTHD_SERVICE_KEY=$(grep ^AUTHD_SERVICE_KEY .env | cut -d= -f2)
export AUTH_BASE_URL=https://example.com
export ONBOARDING_ENABLED=1
./onbod
arizuko gate -i <instance> add '*' 50/day

With ONBOARDING_ENABLED=0, onbod exits at once. GET /health returns 200 when the store is reachable. A queued user sees the queue position on /onboard, which refreshes every 30 seconds.

SetupGroup is the one path that creates a group

Every group folder in an arizuko instance comes from one function: container.SetupGroup(cfg, folder, seedDir). It runs the same five steps in the same order:

  1. It resolves the folder against cfg.GroupsDir and refuses a path that escapes it.
  2. It runs mkdir -p for the group folder and its logs/ subdirectory.
  3. If a seedDir is given, it copies that tree in, without symlinks.
  4. It puts the standard skill set into .claude/skills/ and writes the default settings.
  5. It gives the whole tree to the container UID 1000, so the agent inside can write.

A folder made in another way misses one of these steps. The agent then fails on the first turn: no skills load, it cannot write to logs/, or a settings file is missing. No later step fills the gap, so a folder made without SetupGroup must be made again.

onbod does not run the agent or sign tokens

onbod does not run the agent, sign tokens or schedule tasks. It sends exactly three kinds of message, all through routd: the greeting with its pairing link, a notice that the person is queued, and a refusal. Its job is narrow. It turns an unknown chat into a paired person, an admission decision, a folder on disk and a route that the person chose.

Four pages cover the details