eleven / messenger Public

Clone
git clone https://amadan.net/eleven/messenger

Plain git — no account needed to clone.

Download

Download this file

1# πŸ€– Conversational onboarding & the admin mini-app β€” design
2
3Status: **phases 1–3 implemented** (roles, the setup conversation, the Admin
4app v1 β€” landed with this doc); later phases still proposed. Captured from
5design discussion 2026-07-06.
6
7> **Language note (2026-07-22):** this doc predates the anchor model
8> (`accounts-and-recovery.md`) and still says "Save a passkey" / "backup" in
9> places. The shipped flows say **"Sign in"** β€” the member-facing framing is
10> signing in with Eleven, never the mechanism. Read the passkey/backup
11> wording here as design history, not shipped copy. The "recovery kit"
12> (Β§ keys, path 2) is likewise superseded by that doc's paper-kit phase.
13Builds on `onboarding-and-home-server.md` (welcome page, home-server vault,
14fragility banner β€” phases 1–3a shipped) and the `/tutorial` mini-app
15(`web/static/tutorial.js`), whose client-driven, in-Eleven's-voice pattern
16this design generalises.
17
18> **Follow-on: `conversation-scripts.md`.** With iOS/Mac moving to fully
19> native rendering, these conversations become **flows-as-data** interpreted
20> by a per-platform engine (web first, Swift when the native surfaces land),
21> so one script definition serves every client. The flows and trust
22> boundaries described here are unchanged β€” only where the step logic lives.
23
24## Goals
25
26Two onboarding moments, one shape for both:
27
28- **Server-admin onboarding.** Standing up a server should land the admin in
29 the **chat view**, in a conversation with Eleven that walks setup β€” no
30 `/admin` console, no forms-first overlay. `/admin` is eventually retired,
31 replaced by an **admin mini-app** only admins can see.
32- **User onboarding.** Joining from an invite link should land the joiner in
33 the **chat view**, where Eleven runs a welcome script β€” name, avatar, "save
34 your profile", then an invitation to take the tour.
35
36The bar from CLAUDE.md holds: familiar before novel; a command/script invokes a
37response *in place*; delight rides on top. Onboarding is a conversation because
38that's the most familiar thing in a chat app β€” not because it's clever.
39
40## Non-negotiables
41
42- **The scripts are deterministic, always.** Client-driven, like `/tutorial` β€”
43 never a bot round-trip, never AI. A fresh instance has no sidecar and no API
44 key, and "enable AI?" is itself one of the setup questions, so AI cannot be a
45 dependency of asking it. Eleven owns the *voice*; the logic is a script with
46 tap-chips and captured composer answers. AI Eleven converses *after* setup,
47 where present.
48- **E2E rules unchanged.** Everything the scripts collect lands on existing
49 trust boundaries: display names encrypted under the instance key; group name,
50 server photo, and contact email are admin-set **branding/settings**
51 (plaintext by design, already carved out in CLAUDE.md); no new plaintext
52 categories are introduced.
53- **One voice.** Where a bot sidecar exists, its welcome-DM behaviour stands
54 down in favour of the client script (see *Joiner flow*): two Elevens must
55 never greet the same person.
56
57---
58
59## 1. Prerequisite: admin becomes a role, not just a token
60
61Today "admin" is only a bearer token (`requireAdmin` checks
62`checkAdmin(token)`); roster users carry no admin flag. Everything here β€” an
63admin-only mini-app, "first user is the admin", promoting others, moderators β€”
64hangs off one change:
65
66- **`users.role`**: `admin | moderator | member`. Moderator is in the enum
67 from day one, unenforced at first, so it's designed-in rather than bolted on.
68- **Role-based admin auth.** `/api/admin/*` accepts *either* the admin token
69 *or* a member device token whose user has `role=admin`. The admin mini-app
70 then works with the session the user already has β€” no token pasting.
71- **First claimer is admin.** The first user to claim membership on a fresh
72 instance gets `role=admin`. Admins can later grant/revoke `admin` (and
73 `moderator`) on other users via the admin app.
74
75**The admin token survives as a break-glass API credential** β€” it's how
76`add-instance` provisions, how the runbook recovers access, and it's
77auth-not-decryption by design. What retires is the `/admin` *page*, once the
78mini-app reaches parity. Losing the token path entirely would leave "admin lost
79their only device" with no recovery.
80
81### Moderators (the aside, captured)
82
83Problem: on instances where members can invite (`user_invites`), an invited
84member currently has the same social surface as anyone else. Roster-affecting
85powers should be tiered:
86
87| power | member | moderator | admin |
88|---|---|---|---|
89| invite new people (when `user_invites` on) | βœ“ | βœ“ | βœ“ |
90| remove a user from the instance | – | βœ“ | βœ“ |
91| rename another user | – | βœ“ | βœ“ |
92| grant/revoke moderator | – | – | βœ“ |
93| grant/revoke admin | – | – | βœ“ |
94| instance settings, branding, bots | – | – | βœ“ |
95
96Enforcement is a later phase (see *Phasing*); the schema and the admin-app UI
97should assume it from the start.
98
99## 2. The admin mini-app
100
101A mini-app (per `web/static/apps/README.md`) that replaces the `/admin`
102console. Visible only to users with `role=admin` β€” the gate lives in the app
103framework (an app can declare `adminOnly`), not scattered through `shell.js`.
104
105Surface (parity with today's `admin.js`, reachable in-app instead of on a
106separate page): invites, roster (remove/rename, now also roles), group name,
107server photo, contact email, `user_invites` toggle, bot config, test push.
108Companion-view shape (Chat | Interface) fits naturally: the Interface is the
109console; the Chat view is where admin-flavoured bot responses land (e.g.
110`/invite` already exists as a command).
111
112Retirement is two-step: (1) ship the app, `/admin` stays; (2) once parity is
113proven in use, `/admin` becomes a redirect/pointer and the page + `admin.js`
114are deleted.
115
116## 3. Server-admin onboarding (first run)
117
118### Entry
119
120`lchat add-instance <host>` stops printing an `/admin#token=` link and instead
121prints a one-time **setup link** β€” a normal invite marked `grants_admin`
122(claiming it creates the membership *and* sets `role=admin`). The printed
123admin token / admin link remain available (`lchat admin-link <host>`) as the
124break-glass path.
125
126Landing on the setup link does what invites do today: claim, generate the
127keypair, and β€” already true β€” the first member bootstraps the **instance key**
128client-side (`shell.js`, `generateKey()` when no holders exist). The admin
129lands in the chat view with a conversation with Eleven already open.
130
131### The setup script
132
133Client-driven, keyed on: caller has `role=admin` **and** the server-side
134`setup_complete` config flag is unset. Steps, each one message + captured
135answer or tap-chip, each landing on an *existing* endpoint (now reachable with
136role-based auth):
137
1381. **Your name** β†’ `postLabel` (encrypted under the instance key, as today).
1392. **Group name** β†’ `POST /api/admin/group`.
1403. **Contact email** β†’ `admin_email` in `POST /api/admin/settings` (exists).
1414. **Server photo** β†’ `POST /api/admin/server-avatar` (plaintext branding, as
142 designed). Offered as a chip that opens the picker; skippable.
1435. **Enable AI?** β†’ a new instance setting (see Β§6). Presented honestly: on
144 hosted instances it takes effect; on self-hosted it's a preference the
145 sidecar respects when one exists.
1466. **Can people invite other people?** β†’ `user_invites` (exists).
1477. **Who is this space for?** (#1374) β€” the plan question, moved here from the
148 create flow so creating is never pay-gated. Web-only, and only when home's
149 public `/api/pricebook` answers with tiers (native wrappers and self-hosted
150 spaces never see it); a paid answer becomes a "Set up your plan" chip on
151 the finish line that hands off to home's upgrade picker. The space runs
152 free until that tap.
1538. **The key ceremony** (next section) β€” deliberately near-last, so it's the
154 note the setup ends on.
155
156On completion the script sets `setup_complete` and hands over: "you can change
157any of this later β€” I've put an **Admin** app in your sidebar."
158
159Answers are captured the `/tutorial` way β€” the script asks, and either a chip
160tap or the next composer submit is consumed as the answer (deterministic
161"extraction"; no parsing cleverness). The conversation itself is local
162notices (`buildLocalNotice`), not server messages: nothing needs to persist
163except the settings each step writes.
164
165### The key ceremony
166
167One precision the copy must get right: what the admin must not lose is their
168**credential bundle** (private key + session), not "the instance key" per se.
169The instance key is re-wrappable by any other holder β€” but on a fresh
170one-member server *they are the only holder*, so losing their keys loses the
171server transitively. The script says so, sternly, in Eleven's voice: **we
172cannot get this back for you, and that is the design working.**
173
174Offered paths, in order:
175
1761. **Primary β€” "Save a passkey"** (one click). The shipped phase-3a home-server
177 enrolment popup: credentials into the vault blob, one Face ID. This is the
178 same mechanism as the fragility banner's CTA; completing it here means the
179 banner never appears for this user.
1802. **Secondary β€” download a recovery kit.** A small file (1Password
181 Emergency-Kit style) for people who won't do passkeys: origin, display
182 name, the private key, and a re-auth credential. **New work + an open
183 design question:** session tokens are revocable, so the kit likely wants a
184 durable server-side credential (a long-lived "recovery token" whose hash
185 the server stores, exchangeable once for a fresh session β€” same
186 hash-only-storage discipline as the admin token). Needs its own short
187 design pass before building.
1883. **Not offered: screenshots.** Screenshots sync to cloud photo libraries in
189 plaintext and are casually browsed and shared. Recommending one would
190 undercut the seriousness the step is trying to convey. (A QR inside the
191 recovery-kit file is possible later polish for cross-device import.)
192
193The step is skippable β€” it's their server β€” but skipping routes into the
194existing fragility banner's double-dismiss discipline rather than silence.
195
196## 4. User onboarding (joining from an invite)
197
198Same dance, assembled almost entirely from shipped parts:
199
200- **Chat view first, no overlays.** The join flow's name overlay goes away;
201 claiming an invite lands directly in the chat view with Eleven's welcome
202 conversation open.
203- **Welcome script** (client-driven, keyed on first-open-after-join):
204 1. **Name** β†’ `postLabel`.
205 2. **Avatar** β†’ existing avatar upload; chip opens the picker; skippable.
206 3. **"Save your profile"** β€” the phase-3a enrolment popup, one click and
207 redirect. This *is* the post-join nudge from `onboarding-and-home-server.md`,
208 now delivered in Eleven's voice (one voice: the old backup toast is
209 already slated for retirement; this replaces the banner's first
210 appearance too, for joiners who complete it).
211 4. **"Take the tour"** β€” the existing chip into `/tutorial`.
212- **Sidecar stand-down.** On bot instances, sidecar-Eleven currently
213 welcome-DMs new members. The client script owns onboarding; the sidecar's
214 welcome DM is suppressed (or reduced to creating the DM thread the script
215 runs in). Two Elevens must not greet the same person.
216- **Admin-editable welcome (later).** The script reads an optional
217 admin-authored welcome blob from config (written via the admin app) and
218 plays it before/instead of the stock greeting. Cheap hook to leave in the
219 script's structure now; UI later.
220
221## 5. Roles vs identity: who is "the admin" in the script?
222
223`spaceInfo()` already exposes an administrator identity to the tutorial. With
224roles on the roster this stops being config-only: the roster itself knows who
225the admins are (useful for "contact an admin" chips, the moderator table
226above, and the admin app's role management). `admin_name`/`admin_email` in
227settings remain the *outward* contact surface (they're branding-adjacent);
228role is the *authorisation* surface. Keep them distinct.
229
230## 6. The AI toggle (honest scope)
231
232"Enable AI on this server?" is a real setting with an operator-shaped
233dependency: AI is a **sidecar deployment with an API key**, not something an
234instance flag can conjure. Design:
235
236- A new instance setting (e.g. `ai_enabled`), admin-writable, readable by the
237 sidecar.
238- The sidecar respects it: when off, Eleven runs deterministic-only (the
239 existing per-bot kind machinery); when on β€” and a sidecar with a key exists β€”
240 conversational AI is active.
241- Hosted instances: flipping the toggle works end-to-end (sidecars are
242 provisioned). Self-hosted without a sidecar: the setting records intent and
243 the script/admin app says what's missing, pointing at `add-bot` docs.
244
245This slice gets its own small design pass before building (interaction with
246per-bot config, and whether "off" also silences AI for bots other than
247Eleven).
248
249---
250
251## Phasing (each slice independently shippable)
252
2531. **Roles** β€” `users.role`, first-claimer-is-admin, role-based
254 `requireAdmin` alternative, moderator in the enum (unenforced). Pure
255 server + schema; no UI change.
2562. **Admin mini-app** β€” `adminOnly` app gate + the app itself over existing
257 admin endpoints. `/admin` untouched.
2583. **First-run setup script** β€” `grants_admin` setup invite from
259 `add-instance`, the Eleven setup conversation, `setup_complete` flag.
260 Ends with the passkey step only (recovery kit not yet built).
2614. **Key ceremony completion** β€” recovery-kit design pass, then the
262 download path.
2635. **Joiner welcome script** β€” name/avatar/save-profile/tour, join overlay
264 removal, sidecar welcome stand-down.
2656. **Moderator enforcement** β€” the permission table in Β§1 enforced
266 server-side; role management UI in the admin app.
2677. **AI toggle** β€” design pass + `ai_enabled` + sidecar respect.
2688. **Retire `/admin`** β€” after the app proves parity in use.
269
270## Open decisions
271
272- **Recovery kit re-auth credential**: durable recovery token (hash-only
273 server storage) vs re-using the add-device machinery with a non-expiring
274 variant. (Β§3, key ceremony β€” blocks phase 4 only.)
275- **Sidecar stand-down mechanism**: suppress the welcome DM entirely, or keep
276 the sidecar creating the DM thread and have the script play inside it?
277- **Does `grants_admin` ride on key-invites or user-invites?** Setup is a
278 key-invite shape (there's no inviter to wrap thread keys), but confirm
279 against the invite-redesign fragment format (`#e1.<T>`).
280- **Admin-editable welcome**: plain text blob vs a tiny script format
281 (chips?). Start with text.
282- **Moderator table details**: can moderators cancel invites? See other
283 members' invite links? Decide when enforcing (phase 6).
284