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:
- It resolves the folder against
cfg.GroupsDirand refuses a path that escapes it. - It runs
mkdir -pfor the group folder and itslogs/subdirectory. - If a
seedDiris given, it copies that tree in, without symlinks. - It puts the standard skill set into
.claude/skills/and writes the default settings. - 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
- reference/env: onbod: the full table of environment variables.
- concepts/onboarding: the whole path from a held chat to a route, in one figure.
- components/proxyd: signs people in before
/onboardand/invite/see them. - [onbod/README.md]: the
/v1/invitesand/v1/gatesendpoints, and who can write which route.