arizuko › concepts › onboarding
onboarding
Routing, engagement, and topics all assume a group already exists. Onboarding is how one comes to be. When a new user arrives, the onbod daemon decides whether they get in, which group they land in, and how their identity binds to one canonical sub. The principle to hold: a group is never created by hand — it is always the output of an admission. Three entry paths feed it: invite tokens, the admission queue, and the second-JID auto-link.
invite tokens
An operator mints a token with the CLI:
arizuko invite <instance> create <target_glob> [--max-uses N] [--expires DURATION]
arizuko invite <instance> list [--issued-by SUB]
arizuko invite <instance> revoke <token>
The token is a row in the invites table (owned by onbod). target_glob picks which group the redeemer lands in — for example corp/eng/* or a specific folder. --max-uses defaults to 1; --expires is a Go duration like 72h. Omit it and the token never expires.
The recipient opens the link, picks an OAuth provider (GitHub, Google, Discord, Telegram widget — whichever the instance has configured), and onbod writes an auth_users row keyed by the returned sub. See auth for what a sub is and how linking works.
admission queue
When ONBOARDING_ENABLED=1, a new JID that hits the instance without an invite doesn’t go straight into a group. onbod sends an auth link via routd’s outbound API and writes the user into the queue.
The queue is gated by ONBOARDING_GATES — a list of rules like github-org=kronael, google-domain=example.com, or a catch-all. Each gate carries an optional daily limit. The admitFromQueue loop runs roughly every 60 seconds, counts admissions in the current day, and promotes queued users to approved until the gate’s limit is hit.
Queued users see their position on /onboard, which auto-refreshes every 30 seconds. Operators approve manually through dashd or by adjusting gate config.
SetupGroup writes a group
Once a user is approved (via invite or queue), onbod calls container.SetupGroup(cfg, folder, prototype). That function is the only correct way to create a group — never mkdir a folder under groups/ by hand.
It creates the group directory plus logs/, copies the prototype tree if one is configured (ONBOARDING_PROTOTYPE), then runs seedGroupDir:
.claude/skills/— every directory underant/skills/with a valid name is copied in. See skills..claude/CLAUDE.md— copied fromant/CLAUDE.md..claude/.claude.json— seeded with a per-folder user id (sha256 ofarizuko:<folder>).- Ownership chowned to uid 1000 so the in-container
nodeuser can write.
Skipping SetupGroup produces a folder the agent can’t use: no skills, no settings, wrong ownership. Everything onbod does for a brand-new user funnels through this one call.
second-JID auto-link
A user who already has a world and shows up on a second platform — same human, new JID — doesn’t re-onboard. onbod detects the existing canonical sub via the OAuth callback and links the new JID to the existing world. No queue, no new group.
limits and lifetime
Invite tokens enforce max_uses at redemption: the UsedCount column increments, and RevokeInvite marks them dead. Expired tokens (expires_at < now) are refused with an error. Gate daily limits reset on UTC day boundaries — the queue counts admissions whose queued_at falls in the current day.
go deeper
How identity becomes a signed header that downstream daemons trust: auth. What skills land in a freshly-seeded group: skills. Full token model, planned /v1/invites REST surface, and the cross-daemon flow: onbod/README.md. Spec: specs/5/1-auth-standalone.md.