arizuko › components › onbod
onbod
What it is
In plain terms, onbod is the sign-up desk. When a new person arrives, it decides whether to let them in and sets up their own space for the agent to work in.
onbod is the onboarding daemon. It admits a new user — via invite token, an OAuth bind (signing in with an existing account like Google), or a wait in the admission queue — then builds that user’s group folder by calling container.SetupGroup, the one function that creates a group.
onbod owns invites and onboarding in its own onbod.db and migrates them itself. The auth_users identity rows live in authd.
Why it exists
Two problems would have no good answer without onbod. First, a new user reaching the bot has nothing — no record in auth_users, no folder, no skills, no settings, no default tasks. Something has to put them through identity confirmation, decide whether to admit them, and bring them online.
Second, group creation is not mkdir. container.SetupGroup creates <groups>/<folder>/ and logs/, copies the prototype if one is named, seeds .claude/skills/ with the standard skill set, writes default settings, and chowns everything to the container UID. Skipping any step yields a folder the agent cannot use.
The rule is firm: operators never mkdir a group by hand. Stand it up through onbod (invite redemption or the /v1/users path) so it gets the full set of files.
How it fits
new JID reaches a channel adapter
|
v inbound message
|
routd no auth_users row → writes awaiting_message
|
onbod poll loop sends auth link via routd outbound API
| /onboard landing → OAuth (GitHub/Google/Discord)
| gate match → onboarding queue (per-gate daily limit)
| admitFromQueue (~60s) → approved
| container.SetupGroup(folder, prototype)
v auth.Mint(sub, scope=[users:read self], iss=onbod)
user gets redemption token, dashd verifies it via auth.VerifyHTTP
Gate matching uses ONBOARDING_GATES: a list of github-org, google-domain, or catch-all rules with optional daily caps. A user who matches no gate stays queued. Admitted users with a second JID on another channel auto-link to their existing group rather than getting a fresh one.
onbod is also a JWT issuer. At invite redemption it calls auth.Mint with the same HS256 key every other daemon verifies against, issuing a short-TTL token scoped to users:read on the user’s own sub. The token bootstraps the user into dashd before they get a full session from proxyd’s OAuth flow.
Standalone usage
onbod runs as a daemon against the shared SQLite store and the routd outbound API. It will not run without ROUTER_URL pointing at a reachable routd — the poll loop sends auth links through routd, not directly to platforms.
export DATA_DIR=/srv/data/arizuko_demo
export ONBOD_LISTEN_ADDR=:8080
export ROUTER_URL=http://routd:8080
export AUTH_SECRET=$(openssl rand -hex 32)
export CHANNEL_SECRET=$(openssl rand -hex 32)
export AUTH_BASE_URL=https://example.com
export ONBOARDING_ENABLED=1
export ONBOARDING_GATES='github-org:myorg:5,catch-all:0'
export GITHUB_CLIENT_ID=... GITHUB_CLIENT_SECRET=...
./onbod
Set ONBOARDING_ENABLED=0 and onbod exits immediately. Health: GET /health returns 200 when the database is reachable; queued users see their position on /onboard, which refreshes every 30 seconds.
SetupGroup, the one canonical path
Every group folder in an arizuko instance was created by one function: container.SetupGroup(cfg, folder, prototype). It runs the same five steps in the same order:
- Resolve the folder against
cfg.GroupsDir; reject anything that escapes. mkdir -pthe group folder and itslogs/subdirectory.- If a prototype is named, copy its tree in (no symlinks).
- Seed
.claude/skills/with the standard skill set, write default settings. - Chown the whole tree to the container UID (1000) so the agent inside can write.
A folder built any other way is missing one of these steps. The agent then fails on first turn — no skills loaded, or no write permission on logs, or a missing settings file. There is no fallback path that fills the gaps later; the cost of skipping SetupGroup is recreating the folder from scratch.
What onbod does not do
onbod does not send messages, run the agent, or schedule tasks. Its scope is narrow: turn an unknown JID into a row in auth_users, a folder on disk, and a token the user can present to other daemons.
Go deeper
- reference/env — onbod — full env var table.
- components/proxyd — verifies the session tokens onbod mints.
specs/5/1-auth-standalone.md— auth and admission model.specs/5/5-uniform-mcp-rest.md—/v1/invitesand/v1/userssurface.specs/4/26-prototypes.md— the prototype mechanicSetupGroupcopies from.