eleven / messenger Public

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

Plain git — no account needed to clone.

Download

Download this file

1<meta charset="utf-8" />
2<meta name="viewport" content="width=device-width, initial-scale=1" />
3<title>__GROUP_NAME__ · admin</title>
4<link rel="stylesheet" href="/static/style.css" />
5
6<!-- Apply the saved theme before first paint (see web/static/ui.js — the canonical
7 list; this copy must stay in sync). A local preference, never sent to the server. -->
8<script nonce="__CSP_NONCE__">
9 (function () {
10 try {
11 var THEMES = ["litroom", "lagoon", "sunny", "plum", "linen", "classic"];
12 var t = localStorage.getItem("lchat_theme");
13 if (THEMES.indexOf(t) < 0) t = "classic";
14 var a = localStorage.getItem("lchat_appearance");
15 var root = document.documentElement;
16 root.dataset.theme = t;
17 root.style.colorScheme = a === "light" ? "light" : a === "dark" ? "dark" : "light dark";
18 } catch (e) {}
19 })();
20</script>
21
22<div class="admin" id="admin">
23 <h1>__GROUP_NAME__ admin</h1>
24 <p class="sub">Create secret invite links. Each one lets someone pick a screen name and join the chat.</p>
25
26 <div id="locked" class="card hidden">
27 <h1>Admin link required</h1>
28 <p>Open the admin link that was printed when you started the server. It contains your admin token.</p>
29 </div>
30
31 <div id="console" class="hidden">
32 <section>
33 <h2>Your admin link</h2>
34 <p class="sub" style="margin:0 0 8px">
35 Opens this admin console on any device. It carries your admin token, which
36 authorises managing the roster — it is <strong>not</strong> a decryption key,
37 so it can't read any messages. Save it somewhere safe (e.g. a password manager).
38 </p>
39 <div class="key-box mono" id="masterLink"></div>
40 <div class="row"><button class="secondary" id="copyMaster">Copy admin link</button></div>
41 </section>
42
43 <section>
44 <h2>This space</h2>
45 <p class="sub" style="margin:0 0 8px">The name is shown as the app name on a Home Screen and as the chat's title. The description greets people on their invite's welcome card and lives under ⋮ → About this space. The administrator name &amp; contact email are optional — they're shown to members (e.g. by Eleven's welcome tour) so people know who runs the space and how to reach you.</p>
46 <form id="nameForm" autocomplete="off">
47 <label class="field-label" for="groupName">Group name</label>
48 <input id="groupName" placeholder="e.g. Family Chat" maxlength="60" style="display:block;width:100%;margin:0 0 8px" />
49 <label class="field-label" for="groupDesc">Description (optional)</label>
50 <textarea id="groupDesc" placeholder="What is this space? Members and the people they invite can read this." maxlength="500" rows="3" style="display:block;width:100%;margin:0 0 8px"></textarea>
51 <label class="field-label" for="adminName">Administrator (optional)</label>
52 <input id="adminName" placeholder="e.g. Paul Campbell" maxlength="60" style="display:block;width:100%;margin:0 0 8px" />
53 <label class="field-label" for="adminEmail">Contact email (optional)</label>
54 <input id="adminEmail" type="email" placeholder="e.g. paul@example.com" maxlength="120" style="display:block;width:100%;margin:0 0 8px" />
55 <button type="submit">Save</button>
56 </form>
57 </section>
58
59 <section>
60 <h2>Server photo</h2>
61 <p class="sub" style="margin:0 0 8px">
62 An optional photo for this chat, shown when people are invited, while they
63 join, and on the passkey-backup screen. Like the name, it's public
64 branding — it isn't encrypted, so don't use anything private.
65 </p>
66 <div class="row" style="align-items:center;gap:12px;margin:0 0 8px">
67 <img id="serverAvatarPreview" alt="" class="hidden"
68 style="width:56px;height:56px;border-radius:14px;object-fit:cover" />
69 <button type="button" id="serverAvatarPick">Upload photo</button>
70 <button type="button" class="secondary hidden" id="serverAvatarRemove">Remove</button>
71 </div>
72 <input type="file" id="serverAvatarFile" accept="image/png,image/jpeg,image/webp" style="display:none" />
73 <p id="serverAvatarStatus" class="sub" style="margin:0" aria-live="polite"></p>
74 </section>
75
76 <section>
77 <h2>Who can invite</h2>
78 <p class="sub" style="margin:0 0 8px">
79 By default only you (the admin) can invite people. Turn this on to let members
80 invite each other — each member can then start a chat with someone new and
81 share a join link themselves. The server never sees any key either way.
82 </p>
83 <label class="toggle-row">
84 <input type="checkbox" id="userInvites" />
85 <span>Let members invite other people</span>
86 </label>
87 </section>
88
89 <section>
90 <h2>Features</h2>
91 <p class="sub" style="margin:0 0 8px">
92 Switch off the parts of the app your space doesn't want. Existing chats
93 keep working either way — turning something off just stops new use: AI off
94 means helpers answer commands only, bots off stops new bots being created,
95 apps off hides them from the New-chat menu, member-made apps off keeps the
96 built-in apps but stops members building their own.
97 </p>
98 <label class="toggle-row" style="margin:0 0 8px">
99 <input type="checkbox" id="aiEnabled" />
100 <span>Enable AI</span>
101 </label>
102 <label class="toggle-row" style="margin:0 0 8px">
103 <input type="checkbox" id="botsEnabled" />
104 <span>Enable bots</span>
105 </label>
106 <label class="toggle-row" style="margin:0 0 8px">
107 <input type="checkbox" id="appsEnabled" />
108 <span>Enable apps</span>
109 </label>
110 <label class="toggle-row">
111 <input type="checkbox" id="memberAppsEnabled" />
112 <span>Let members make their own apps</span>
113 </label>
114 </section>
115
116 <section>
117 <h2>Updates</h2>
118 <p class="sub" style="margin:0 0 8px">
119 The server can keep itself up to date. Stable is well tested and what most
120 spaces should run; Beta gets features a little earlier; Edge is the bleeding
121 edge. Updates install in seconds and never touch your data.
122 </p>
123 <p id="updateStatus" class="sub" style="margin:0 0 8px"></p>
124 <label class="toggle-row" style="margin:0 0 8px">
125 <input type="checkbox" id="updateAuto" />
126 <span>Install updates automatically</span>
127 </label>
128 <label class="toggle-row" style="margin:0 0 8px">
129 <input type="checkbox" id="updateDownload" />
130 <span>Download new versions automatically</span>
131 </label>
132 <label class="field-label" for="updateChannel">Release channel</label>
133 <select id="updateChannel" style="display:block;width:100%;margin:0 0 8px">
134 <option value="stable">Stable — well tested (recommended)</option>
135 <option value="beta">Beta — near-stable, newer features</option>
136 <option value="edge">Edge — the bleeding edge</option>
137 </select>
138 <label class="field-label" for="updateMajor">Stay on major version</label>
139 <input id="updateMajor" inputmode="numeric" placeholder="any" style="display:block;width:100%;margin:0 0 4px" />
140 <p class="sub" style="margin:0 0 8px">
141 Leave empty to follow every release. With a number set, small updates keep
142 arriving but a release crossing that major version waits for your say-so.
143 </p>
144 <p id="updateFeedback" class="sub" style="margin:0" aria-live="polite"></p>
145 </section>
146
147 <section id="agentSection">
148 <h2>Add an agent</h2>
149 <p class="sub" style="margin:0 0 8px">Adds an AI bot as a member. This mints a one-time link the agent sidecar uses to set up the bot's identity; it then appears in chats as a member with a BOT badge. The sidecar for this instance must be running.</p>
150 <form id="agentForm" autocomplete="off">
151 <input id="agentLabel" placeholder="Bot name (e.g. Claude)" maxlength="60" style="display:block;width:100%;margin:0 0 8px" />
152 <input id="agentDescription" placeholder="What this helper does, in one line — members see this in the Helpers list" maxlength="200" style="display:block;width:100%;margin:0 0 8px" />
153 <select id="agentModel" style="display:block;width:100%;margin:0 0 8px">
154 <option value="">Default model (Sonnet)</option>
155 <option value="claude-opus-4-8">Opus — most capable</option>
156 <option value="claude-sonnet-5">Sonnet — balanced</option>
157 <option value="claude-haiku-4-5">Haiku — fast &amp; cheap</option>
158 </select>
159 <textarea id="agentPrompt" rows="4" placeholder="Personality / instructions (system prompt). Leave blank for a friendly default. A safety + privacy preamble is always applied on top." style="display:block;width:100%;margin:0 0 8px"></textarea>
160 <textarea id="agentDocs" rows="3" placeholder="Reference the bot should always know — house rules, an FAQ, etc. Optional." style="display:block;width:100%;margin:0 0 8px"></textarea>
161 <select id="agentListen" style="display:block;width:100%;margin:0 0 8px">
162 <option value="">Replies in groups only when @mentioned (default)</option>
163 <option value="name">Also replies when spoken to by name (&ldquo;Claude, &hellip;&rdquo;)</option>
164 </select>
165 <label class="toggle-row" style="margin:0 0 8px">
166 <input type="checkbox" id="agentAvailable" checked />
167 <span>Show in everyone's Helpers list (untick to add it to chats yourself)</span>
168 </label>
169 <button type="submit">Add agent</button>
170 </form>
171 </section>
172
173 <section>
174 <h2>Apps</h2>
175 <p class="sub" style="margin:0 0 8px">
176 Install custom mini-apps for this space — each is a single JavaScript
177 module file, just like the built-in To-do or Polls. Members' open apps
178 refresh automatically when you install or remove one.
179 </p>
180 <p class="sub" style="margin:0 0 8px">
181 <strong>Install only code you trust.</strong> An installed app runs its
182 code on every member's device, with access to everything that device can
183 decrypt — messages, names, and photos included.
184 </p>
185 <div id="appsList" style="margin:0 0 10px"><p class="sub">Loading…</p></div>
186 <div class="row" style="margin:0 0 8px">
187 <button type="button" id="appPick">Install from file…</button>
188 </div>
189 <input type="file" id="appFile" accept=".js,text/javascript" style="display:none" />
190 <label class="field-label" for="appName">Or paste the source</label>
191 <input id="appName" placeholder="App name (lowercase letters, digits, dashes)" maxlength="60" style="display:block;width:100%;margin:0 0 8px" />
192 <textarea id="appSource" rows="6" placeholder="// paste the app module's JavaScript here" class="mono" style="display:block;width:100%;margin:0 0 8px"></textarea>
193 <button type="button" id="appInstall">Install</button>
194 <p id="appsStatus" class="sub" style="margin:8px 0 0" aria-live="polite"></p>
195 </section>
196
197 <section>
198 <h2>Notifications</h2>
199 <p class="sub" style="margin:0 0 8px">Send a test notification to everyone who has them enabled — a quick way to check delivery.</p>
200 <div class="row"><button class="secondary" id="testPush">Send test notification</button></div>
201 </section>
202
203 <section>
204 <h2>Shared key</h2>
205 <p class="sub" style="margin:0 0 8px">Every member's name and photo is locked with one shared key, passed member to member. If everyone who held it is gone for good, newer members stay locked out — invites included. Resetting mints a fresh key the next time anyone opens the app.</p>
206 <p class="sub" style="margin:0 0 8px"><strong>Resetting is permanent:</strong> profile photos, "about me" notes and member-built apps locked with the old key can't be recovered — people re-add them. Names come back on their own the next time each member opens the app. Open groups close to invite-only (reopen them afterwards). Messages are untouched — chats have their own keys.</p>
207 <div class="row"><button class="secondary" id="resetInstanceKey">Reset the shared key…</button></div>
208 <p id="resetKeyStatus" class="sub" style="margin:8px 0 0" aria-live="polite"></p>
209 </section>
210
211 <section>
212 <h2>Delegate admin role</h2>
213 <p class="sub" style="margin:0 0 10px">Create an invite that makes the claimer an admin. They can then access this console with their device token (no need to share your admin link). Use this to share admin duties or to recover access if you lose your token.</p>
214 <form id="createAdminForm" class="inline-form" autocomplete="off">
215 <input id="adminLabel" placeholder="Their name (e.g. Sophie)" maxlength="60" />
216 <button type="submit">Create admin invite</button>
217 </form>
218 </section>
219
220 <section>
221 <h2>People</h2>
222 <div id="bootstrapInvite">
223 <p class="sub" style="margin:0 0 10px">Invite the first person with a short, single-use link (and QR) — they pick a screen name and their device makes its own keys, so the server never sees any key. After someone joins, people are added from inside the chat.</p>
224 <form id="createForm" class="inline-form" autocomplete="off">
225 <input id="label" placeholder="Their name (e.g. Sophie)" maxlength="60" />
226 <!-- Cohort invites: hidden until the space has access levels. -->
227 <select id="inviteTier" class="hidden" aria-label="Access level for this invite"></select>
228 <button type="submit">Create invite</button>
229 </form>
230 </div>
231 <p id="inviteGuidance" class="sub hidden" style="margin:0 0 10px">People are added from inside the chat now — turn on "Let members invite other people" above, then use "Invite someone" when starting a new chat. That way the new person is connected the moment they join.</p>
232 <div id="people" style="margin-top:16px"><p class="sub">Loading…</p></div>
233 </section>
234 </div>
235</div>
236<div id="toast" class="toast"></div>
237
238<script type="module" src="/static/admin.js"></script>
239