| index 2066ee2..ea98ee1 100644 |
| --- a/internal/bot/knowledge.md |
| +++ b/internal/bot/knowledge.md |
| @@ -80,28 +80,44 @@ your device)** โ never on the server. |
| the admin token reveals no message content. The server stores only a *hash* of |
| the admin token, never the token itself, and tokens/keys are never logged. |
| |
| -### The one deliberate exception: link previews |
| +### The two deliberate exceptions: link previews and GIF search |
| |
| -There is exactly one place the server briefly sees something in the clear: **link |
| -previews (unfurls)**. When you post a URL, the server fetches that page to build |
| -the little preview card (title, image, description). So it momentarily sees the |
| -*URL you posted* โ not your message text, just that one link. This was an |
| -explicit, accepted trade-off, and it's guarded against abuse (it won't fetch |
| -internal/private addresses). Everything else stays encrypted. |
| +Almost nothing bypasses the encryption; be able to name the two things that |
| +partly do โ both explicit, accepted trade-offs: |
| |
| -### Branding is plaintext by design |
| +- **Link previews (unfurls).** When you post a URL, the server fetches that page |
| + to build the little preview card (title, image, description). So it |
| + momentarily sees the *URL you posted* โ not your message text, just that one |
| + link. It's guarded against abuse (it won't fetch internal/private addresses). |
| +- **GIF search (`/gif`).** Searching happens **from your own device, directly to |
| + Giphy** โ so *Giphy* sees your search terms and network address; the Eleven |
| + server sees nothing. The GIF you pick is downloaded on your device, encrypted |
| + to the chat, and sent like any other picture. The Giphy key is **shared by the |
| + whole space** (the first person to use `/gif` supplies one, and it just works |
| + for everyone after that) โ the form says so plainly, and a Giphy key is a |
| + public client identifier, not a secret. |
| |
| -A couple of things are *deliberately* not encrypted, because they must show up |
| -before anyone has a key โ on the invite/welcome screen, the join screen, and the |
| -passkey-backup screen. These are **admin-set branding**, never user content: |
| +Everything else stays encrypted. |
| + |
| +### Branding is plaintext by default โ and can be sealed |
| + |
| +A few things are *deliberately* not encrypted, because they must show up before |
| +anyone has a key โ on the invite/welcome screen, the join screen, and the |
| +sign-in screens. These are **admin-set branding**, never user content: |
| |
| - the **group name** (the chat's title, and the name it takes on your Home |
| - Screen), and |
| + Screen), |
| - an optional **server photo** (a picture for the chat, shown on those same |
| - screens). |
| + screens), and |
| +- an optional **space description**. |
| |
| -Both are public branding โ don't put anything private in them. Everything that is |
| -actually *user* content stays end-to-end encrypted. |
| +All are public branding โ don't put anything private in them. And an admin can |
| +now go further and **seal the branding** (Admin app โ Privacy): the space's |
| +name, photo, description, and admin contact move into an encrypted box, and the |
| +key rides inside invite links instead of ever being sent to the server. Invited |
| +people still see everything; the people who run the servers see only the locked |
| +box. (One honest note: the iPhone/Mac apps can't open the sealed box yet, so a |
| +sealed space shows there with a generic name โ that catches up in an update.) |
| |
| ### The honest trade-offs |
| |
| @@ -114,9 +130,9 @@ design, not bugs: |
| them *new* messages, but it can't claw back a key they already hold, so it |
| doesn't retroactively lock them out of past messages they already had. |
| - **Lose your only device with no backup, and that access is gone.** If your key |
| - material is only on one device and you have no add-device link or passkey |
| - backup, the threads only you could decrypt are unrecoverable. That's the design |
| - working as intended โ which is exactly why backups exist (see ยง4). |
| + material is only on one device and you have no add-device link or Eleven |
| + sign-in backup, the threads only you could decrypt are unrecoverable. That's |
| + the design working as intended โ which is exactly why backups exist (see ยง4). |
| - **Metadata is not hidden.** The server can't read *what* you say, but to route |
| messages it necessarily knows the **social graph**: who is in which thread, who |
| sent a message, and its timing and size. Hiding the graph itself is explicitly |
| @@ -126,7 +142,8 @@ design, not bugs: |
| number" check). A *passive* attacker with a stolen database still can't read |
| anything. An *active*, malicious server could in theory substitute a public key |
| to intercept *new* conversations. Out-of-band key verification is planned to |
| - close this gap. |
| + close this gap. (The native apps already verify that the server isn't |
| + *rewriting history* โ see ยง18.) |
| |
| --- |
| |
| @@ -141,7 +158,7 @@ a secret invite link, and your identity is a keypair on your device. |
| - **Members can invite too, if the admin allows it.** When the "let members |
| invite other people" setting is on, any member can start a chat with a |
| brand-new person and share a join link themselves. The server never sees a key |
| - either way. |
| + either way. (Flipping the setting takes effect immediately for everyone.) |
| - **Joining.** You open the link, see a welcome card (group name + photo), pick a |
| **screen name**, and join. Your browser generates your keypair and registers |
| your public key on the roster. The invite can also seed your first conversation |
| @@ -150,12 +167,17 @@ a secret invite link, and your identity is a keypair on your device. |
| - **Direct-to-group invites.** An invite can point at a specific group, shipping |
| that group's key so you land right inside it. (Joining a group still means |
| joining the instance โ the roster is instance-wide.) |
| +- **Cohort invites.** An invite can also carry an **access level** (ยง16): |
| + everyone who joins through that link lands in the level automatically โ their |
| + key follows from a member the moment they arrive. Pair it with a level-only |
| + group and each intake gets its own room from day one. |
| - **Add another device / recover a lost one.** From a device that already holds |
| your keys, you mint a fresh add-device link. That link carries your **user |
| private key** (encrypted under the link's one-time secret) to the new device, |
| which can then open *every* thread you belong to โ no per-thread re-invite |
| needed. QR is the easy phoneโdesktop path. Because a device holds a real key, |
| - treat these links like a password. |
| + treat these links like a password. The same sheet also offers a **Back up** |
| + button โ the one-tap Eleven sign-in from ยง4. |
| - **Invite links are single-use and revocable**, and the secret in them is never |
| sent to the server, so a stolen database can't be used to claim an invite. |
| |
| @@ -179,79 +201,172 @@ thread with its own key. |
| |
| - **The roster** is the instance-level directory of members (name + public key). |
| By default it's open: any member can see who's in the instance and start a chat |
| - with anyone โ no introductions needed. (Some instances restrict who can see or |
| - DM whom; see Instance contexts, ยง14.) |
| + with anyone โ no introductions needed. |
| - **Starting a chat.** Tap **โ**. Pick one person for a DM, or "New group" to |
| - pick several and name it. There's also a **Helpers** section in that picker for |
| - the bots (see ยง12โ13). |
| + pick several and name it. The same picker holds the **Helpers** section for |
| + bots (ยง13โ15), the **Apps** section, and **Discover** for open groups. |
| +- **Open groups & Discover.** A group can be **open to the whole space**: tick |
| + "Open group" when you create it (or from Edit group), and anyone can find it |
| + under **Discover** and join with one tap โ no invite needed. Still end-to-end |
| + encrypted like every chat; "open" only means open to members of this space. |
| + Admins can mark an open group as a **default group** ("New members join |
| + automatically") โ the space's own #general; only *new* members are added, and |
| + leaving sticks. An open group can also be **limited to one access level** |
| + ("Only Coaches", ยง16) โ its level's members find and join it under Discover, |
| + while everyone else sees an honest locked entry, with the name and everything |
| + inside staying sealed. |
| +- **Who's around (presence).** Open a chat and the other person's status shows |
| + under their name โ a green Online, yellow Away, or red Offline dot; group |
| + headers add "ยท 2 online" to the member count, and the member list shows who's |
| + about. The filter button beside search can narrow your chats to people online |
| + right now, or to just people, bots, or apps. You go Away automatically after |
| + ten quiet minutes, or flip "Set yourself as away" in your profile. |
| +- **Unread dots sync across your devices** โ catch up on a chat on your phone |
| + and the dot clears on your laptop too. They also survive reloads: a message |
| + that arrived while you were away stays marked unread until you actually open |
| + it. |
| +- **You choose what you call people.** Invited someone as "Dermo" who signed up |
| + as "Dermot"? Keep calling them Dermo. Tap a member in a group's member list |
| + (or the title of your DM with them) to see their **profile card** โ photo, |
| + what they're about, a Message button, and the place to set *your* name for |
| + them. When someone renames themself, the choice appears right in your chat: |
| + switch to their new name, or keep yours. Mentions follow along โ type `@` and |
| + the name *you* use. Your names for people are private to you, sealed so only |
| + your own devices can read them โ other members and the server never see them. |
| - **Who can do what (default, safe-for-kids):** any member can start a DM, create |
| a group, and add *existing* instance members to a group. A group's **owner** can |
| rename it or remove a member. Adding a *brand-new person to the instance* (the |
| roster) is admin-only by default; removing a person from the instance is |
| admin-only. So the admin guards the trust boundary while members move freely |
| inside it. |
| +- **Your DMs are yours to gate.** In Edit profile, choose who can start a direct |
| + message with you: everyone in the space, or no one (admins and helper bots can |
| + always reach you, and chats you already have keep working). Any DM can be |
| + **ended** from its โฎ menu โ it closes for both of you and stays closed until |
| + either of you reopens it. |
| - **Layout.** On desktop it's master-detail: a thread list on the left, the open |
| thread on the right, newest activity first. On mobile it's a single pane that |
| navigates list โ thread โ back, plus a people view for starting new chats. |
| - Chats can be **pinned** to float to the top of the list. |
| + Chats can be **pinned** to float to the top of the list, and the sidebar's |
| + heading is your space's name โ handy if you belong to more than one. |
| |
| --- |
| |
| -## 4. Not losing access: the passkey backup (home server) |
| - |
| -Because your keys live on your device, Eleven offers an **optional** way to back |
| -them up so you can restore on a fresh device โ without accounts, passwords, or |
| -weakening the one rule. |
| - |
| -- It uses a **passkey** (Face ID / Touch ID / a password manager). The passkey |
| - derives a key *on your device* that encrypts a small backup blob (your list of |
| - chats, and optionally your per-instance credentials) held by a tiny separate |
| - **home/backup server**. That server sees only ciphertext โ no messages, no |
| - roster, nothing an instance would recognise. |
| -- It's **strictly opt-in and additive**: a member who never enrols makes *no* |
| +## 4. Not losing access: your Eleven home (sign-in & backup) |
| + |
| +Because your keys live on your device, Eleven offers an **optional** sign-in โ |
| +your **Eleven home** โ that backs your access up and lists your spaces, without |
| +passwords or weakening the one rule. |
| + |
| +- **What it stores is sealed.** Signing in ("save your access") encrypts a small |
| + backup *on your device* โ your list of spaces and your per-space credentials โ |
| + and parks it with a tiny separate **home server**. That server sees only |
| + ciphertext: no messages, no roster, not even your spaces' names (those are |
| + sealed too, along with any labels you add). Restore on a fresh device is one |
| + sign-in: you're back in your chats. |
| +- **Many doors, one account.** A **passkey** (Face ID / Touch ID / a password |
| + manager) is the original way in. You can also sign in with **Apple, Google, |
| + Microsoft, GitHub, or an email code** โ each door only *confirms who you |
| + are*, and Eleven keeps a **scrambled fingerprint** of that identity, never |
| + your name or email address (so the no-spam promise stands: we can't see your |
| + email). The sign-in card leads with the door you used last time โ or, on a |
| + first visit, the door that fits the device โ with "Other ways to sign in" |
| + listing the rest. |
| +- **Sign-in options** (in the home page's account menu) shows every door on |
| + your account: add another so you're never locked out, give any row a label |
| + ("personal", "work" โ an added email is labelled with its address |
| + automatically), or remove one you no longer use. Changing them always |
| + confirms it's you first, and the last way in can never be removed. If you |
| + signed in a new way and found an *empty* spaces list, your spaces probably |
| + live with your passkey โ the page offers "Already have spaces? Sign in with |
| + your passkey to bring them over", and then you can add the new door to that |
| + same account so every way in opens the same spaces. |
| +- **Your home page** lists your spaces by name (star your favourites to sort |
| + them), shows each space you created with its storage use and allowance |
| + ("1.2 GB of 5 GB stored"), and is where space plans are upgraded and managed |
| + (ยง16). |
| +- It's **strictly opt-in and additive**: a member who never signs in makes *no* |
| requests to any backup server, ever. If you decline, nothing about you is |
| logged anywhere. |
| -- Add a second passkey (say, iCloud *and* a password manager) for redundancy. |
| - Restore on a new device is then one Face ID: you're signed back into your |
| - chats. Enrolled devices can also **sync silently** in the background, so a chat |
| - you join on one device can appear on your others on its own. |
| -- Members without any backup see an **inline fragility notice** ("if this browser |
| - session is lost, your access to this chat is lost") nudging them to save a |
| - passkey. It's a gentle, dismissible reminder โ never a pop-up that covers what |
| - you're reading. |
| - |
| -The universal fallback, if you don't use passkeys, is always the **add-a-device |
| +- Enrolled devices **sync silently** in the background, so a space you join on |
| + one device can appear on your others on its own. |
| +- Members without any backup see an **inline fragility notice** ("if this |
| + browser session is lost, your access to this chat is lost") nudging them to |
| + sign in. It's a gentle, dismissible reminder โ never a pop-up that covers |
| + what you're reading. The add-a-device sheet's **Back up** button does the |
| + same one-tap sign-in whenever you want it. |
| + |
| +The universal fallback, if you never sign in, is always the **add-a-device |
| link** (ยง2). Losing *all* your backups and devices means the data is |
| cryptographically gone โ that's the privacy guarantee holding, not a failure. |
| |
| --- |
| |
| -## 5. Messaging: reactions, replies, editing, and gestures |
| +## 5. Messaging: formatting, replies, editing, search, and gestures |
| |
| Everything about a message is encrypted; here's what you can do with one. |
| |
| -- **Rich text**: links are auto-detected and linkified, `@mentions` pop a member |
| - menu, and a message that's only emoji renders jumbo. Messages group into sender |
| - runs with time separators. |
| +- **Real Markdown, kept deliberately small**: **bold**, *italics* (underscores |
| + work too), `code`, ~strikethrough~, > quotes, ``` code blocks, bulleted and |
| + numbered lists, and named links (`[like this](https://โฆ)`) โ nestable and |
| + combinable. Headings and tables stay plain text in a chat, on purpose. Bare |
| + links are auto-detected, `@mentions` pop a member menu, and a message that's |
| + only emoji renders jumbo. A few shortcodes autocomplete and convert on send |
| + (`:shrug:` ๐คท, `:facepalm:` ๐คฆ, `:strong:` ๐ช, `:lol:` ๐), and `/emoji` (or |
| + `/emoji cat`) opens a picker panel above the composer. Messages group into |
| + sender runs with time separators, and names above group messages always show |
| + the sender's *current* name. |
| +- **The composer formats as you type** โ the basics only: type `**bold**` and it |
| + turns bold the moment you close the stars, same for italics, code, and |
| + strikethrough, with โB/โI and friends as shortcuts; backspace right after |
| + undoes it. (Lists and quotes don't convert while you type โ they still render |
| + beautifully once sent.) What you send is exactly what everyone sees, on every |
| + device. Prefer your markers to stay put? Turn off "Format as you type" in the |
| + Theme sheet, or switch to the classic plain-text box entirely. |
| - **Reactions**: react with an emoji. On **touch**, long-press a message (or |
| double-tap) to open the emoji picker above the bubble; on **desktop**, a hover |
| strip appears and press-and-hold opens the full picker. Reaction counts show as |
| chips under the bubble. |
| -- **Replies**: quote-reply to a specific message. On touch you can **swipe right** |
| - on a message to quick-reply; there's also a Reply item in the message menu. The |
| - reply shows a quote banner. |
| +- **Replies are threads.** Reply to a reply and it continues that thread โ your |
| + message quotes the *original* message, and the "N replies" count under the |
| + original covers the whole back-and-forth. Tap the quote card on any reply (or |
| + the count) and the **thread opens in its own view**: the original pinned at |
| + the top, every reply beneath, the message box already set to reply; โ (or |
| + tapping outside) drops back into the chat. On touch you can **swipe right** on |
| + a message to quick-reply. |
| - **Editing**: you can edit your own message for a **15-minute window** (on |
| desktop, press โ in an empty composer to edit your last message, โ to cancel). |
| - **Delete / unsend**: you can unsend your own message โ this genuinely removes |
| the ciphertext from the server's log (author-verified), not just a tombstone. |
| +- **Copy a link to any message** โ click its timestamp, or "Copy link" in the |
| + message menu. Opening the link jumps straight to that message and highlights |
| + it, but only for members of that chat: the link carries no readable trace of |
| + the message or its contents, even if it ends up posted somewhere else, and it |
| + stops working for anyone who later leaves the chat. |
| - **Message menu**: long-press (touch) or hover + press-and-hold (desktop) opens |
| the menu โ reactions above the bubble, and Reply / Copy / Edit / Delete below. |
| Mini-apps can add their own items here (e.g. Bookmarks adds "Save link" to any |
| - message with a link). |
| + message with a link), and Report / Mute live here too (ยง8). |
| +- **Search your messages** โ the search box above your chats finds messages, not |
| + just chat names. Matches appear in a dropdown, newest first, keyboard-driven |
| + (the first result is highlighted as you type, Enter opens it, Escape closes); |
| + picking one jumps to the message and highlights it until you move on. |
| + Searching happens **entirely on your device** โ messages stay sealed, and what |
| + you type never leaves your browser. If a chat's older history isn't on your |
| + device yet, "Search earlier messages" digs back through the months โ |
| + resumable, stoppable, watchable. |
| +- **Jump to latest**: scroll up a way and a small down-arrow button appears in |
| + the corner of the feed โ tap it to drop straight back to the newest message. |
| - **Typing indicators** show live ("Sam is typingโฆ") and are relay-only โ never |
| stored. |
| - **Mentioning a bot**: `@mention` a helper (e.g. `@Claude`) to get its attention |
| in a group; in a DM a bot always replies. |
| +- **Reloads are instant.** The app keeps a copy of your chats on your device, so |
| + opening it doesn't re-download everything, and recently visited chats open on |
| + the spot even on a shaky connection. What's kept locally is exactly as sealed |
| + as what the server holds, and it's cleared if you sign out or leave a group. |
| +- **Text size** is yours to set: on a computer, the **Text size** slider in the |
| + Theme sheet; on iPhone/iPad the web app follows your system Text Size setting. |
| |
| The composer follows the platform: on **desktop**, Enter sends and Shift+Enter |
| makes a newline; on **touch**, Enter makes a newline and the send button sends. |
| @@ -267,14 +382,18 @@ The message box grows to a max height then scrolls. |
| **encrypted** and uploaded as an opaque blob. |
| - **There is no server-side image proxy or thumbnailer** โ the server only ever |
| holds ciphertext, so all image work happens on your device. |
| -- **Files**: you can attach documents (PDF, txt/markdown, CSV, JSON, Office docs, |
| - etc.). The allowed-types list is the safety guard โ executables and scripts are |
| - rejected. The filename and type ride *inside* the encrypted payload, never as |
| - server metadata. |
| +- **Any file goes.** Attach documents, spreadsheets, archives, code, anything โ |
| + it travels end-to-end encrypted like everything else. The filename and type |
| + ride *inside* the encrypted payload, never as server metadata. |
| +- **GIFs**: `/gif` (or `/gif high five`) searches Giphy in a panel above the |
| + message box; tapping one sends it into the chat as a picture from you. See ยง1 |
| + for the (disclosed) privacy shape: your device talks to Giphy directly, under |
| + a key shared by the space โ the Eleven server never sees the search, the key, |
| + or the GIF in the clear. |
| - Images show inline (with a lightbox), files show as chips, and posted links can |
| - render preview cards (the one unfurl exception, ยง1) and YouTube cards. |
| + render preview cards (the unfurl exception, ยง1) and YouTube cards. |
| - Attachments are stored as encrypted blobs; large media is what drives storage |
| - growth, but the server still can't open any of it. |
| + growth (ยง16), but the server still can't open any of it. |
| |
| --- |
| |
| @@ -288,10 +407,51 @@ The message box grows to a max height then scrolls. |
| instance key, so only fellow members can see it โ never the server. |
| - You can edit your screen name and photo anytime from your profile (you can also |
| just ask Eleven to "edit my profile" and tap the button, or type `/profile`). |
| + Edit profile is also where **who can DM you** lives (ยง3), the "set yourself |
| + as away" switch (ยง3), and โ on spaces with paid access levels โ |
| + **Membership** (ยง16). |
| +- Other people: tap a member anywhere to see their **profile card**, and set |
| + your own private name for them there (ยง3). |
| + |
| +--- |
| + |
| +## 8. Staying pleasant: mute, block, and report |
| + |
| +Every member has tools to shape their own experience โ all private, none of |
| +them announced to the other person: |
| + |
| +- **Mute a chat** from its โฎ menu โ no notifications, no unread dot; messages |
| + keep arriving quietly. |
| +- **Mute a person** from their profile card or a long-press on their message โ |
| + their messages fold into a small "Muted message" stub you can tap to read, |
| + and they never notify you. Who you've muted is sealed to your own devices โ |
| + the server can't read it either. |
| +- **Block someone** โ blocking mutes them, ends any DM between you, and stops |
| + new contact both ways: they can't message you or add you to groups, and you |
| + won't be notified about anything they say. They're never told. Only your |
| + space's server keeps the content-free record it needs to enforce it. Find it |
| + on their profile card, or tick "Also block" when you report someone. Admins |
| + can still reach you โ a warning can't be dodged by blocking the messenger. |
| +- **Report a message or a person** to your space's admins โ long-press a |
| + message and choose Report, or use the Report button on someone's profile. |
| + Pick a reason, add a note if you like, and it goes to the admins **sealed so |
| + only they can read it** โ the server can't, and neither can anyone else. |
| + Admins get a Reports inbox in the Admin app with clear next steps โ dismiss, |
| + send a warning, remove someone from a group, an access level, or the space โ |
| + and an audit log records every moderation action. Be honest if asked: a |
| + report is one side of a story, and the inbox says so โ admins read them and |
| + use their judgment. |
| +- **Admins hold the last rungs**: "Remove & ban" keeps a removed member out โ |
| + their key can't rejoin through any invite โ and every report shows who |
| + invited the person, so a bad invite chain is visible. |
| +- **Quiet spam brakes** are always on: far above any human pace, a flood of |
| + messages, new chats, or invites simply stops landing (admins are exempt, and |
| + day-one members can't mass-DM). The "members can invite" switch is the panic |
| + lever in a spam wave, and flipping it is recorded in the audit log. |
| |
| --- |
| |
| -## 8. Themes & appearance |
| +## 9. Themes & appearance |
| |
| Look-and-feel is entirely a **local preference** on your device โ it's stored in |
| your browser, never sent to the server, and never shared. |
| @@ -299,13 +459,15 @@ your browser, never sent to the server, and never shared. |
| - **Colour themes**: *Lit Room* (warm amber), *Lagoon* (teal), *Sunny* (yellow), |
| *Plum* (purple), *Linen* (soft neutral), and *Classic* (blue). |
| - **Appearance**: Light, Dark, or System (follow your device). |
| +- The Theme sheet also holds the **Text size** slider (desktop) and the |
| + **"Format as you type"** composer toggle (ยง5). |
| - Change them from the **โฎ menu โ Theme picker**, or just ask Eleven ("make it |
| dark", "switch to the teal theme") and tap the button, or use the commands |
| - `/theme <name>`, `/themes`, `/dark`, `/light` (ยง11). |
| + `/theme <name>`, `/themes`, `/dark`, `/light` (ยง12). |
| |
| --- |
| |
| -## 9. Notifications |
| +## 10. Notifications |
| |
| Eleven supports two delivery paths, both preserving the encryption: |
| |
| @@ -325,13 +487,14 @@ Eleven supports two delivery paths, both preserving the encryption: |
| sender's own subscriptions). |
| - You're **not** notified for a chat you currently have **open/active** (the app |
| writes a heartbeat that the notifier checks). |
| +- **Muted chats and muted people stay quiet** (ยง8). |
| - The admin's **"test notification"** always shows, as a delivery check. |
| |
| Enable notifications from the **โฎ menu**. |
| |
| --- |
| |
| -## 10. Mini-app notices, in-app |
| +## 11. Mini-app notices, in-app |
| |
| A mini-app event (like a reminder firing) speaks where it belongs: it lands in |
| its thread, which floats up your chat list with an unread dot and a preview line; |
| @@ -342,7 +505,7 @@ occupy their own space inline. |
| |
| --- |
| |
| -## 11. Slash commands |
| +## 12. Slash commands |
| |
| The command layer is deliberately **familiar first** โ it should feel instantly |
| recognisable to anyone who's used Slack or Discord: `/command args`, a `/` |
| @@ -351,7 +514,9 @@ principles worth explaining to members: |
| |
| - **A command answers *in place*.** Running a command produces a *reply* right |
| where you typed it โ usually a private "only visible to you" message from a bot |
| - โ not a jump somewhere else and not a silent UI change. |
| + โ not a jump somewhere else and not a silent UI change. Some commands answer |
| + with a **panel** instead: a temporary surface above the message box with |
| + something to tap (`/gif`, `/emoji`), dismissed with its โ or the Escape key. |
| - **Action chips move you only on your tap.** If a command's answer offers to |
| take you somewhere or change something (e.g. "View reminders", "tap to switch |
| theme"), that's an **action chip** you choose to tap โ leaving your current |
| @@ -362,7 +527,8 @@ principles worth explaining to members: |
| The **built-in bot commands** (answered by Eleven, and available even with AI |
| off): |
| |
| -- `/help` โ list the commands you can use here. |
| +- `/help` โ list the commands you can use here (it also explains, in one |
| + sentence, how the place flags below work). |
| - `/theme <name>` โ switch colour theme (e.g. `/theme lagoon`). |
| - `/themes` โ open the theme picker. |
| - `/dark` โ switch to dark mode. |
| @@ -373,10 +539,20 @@ off): |
| The theme/profile commands attach the same one-tap buttons the AI would โ so |
| "make it dark" (asking Eleven) and `/dark` (typing) reach the identical result. |
| |
| -The **mini-app commands** (each app owns its own; see ยง12): `/todo`, `/remind`, |
| -`/poll`, `/board`, `/save`, `/connect`, `/register`, `/list`. Typing `/` brings up |
| -a typeahead listing what's available in the current thread โ bot commands only |
| -appear where a bot can actually answer them. |
| +The **mini-app commands** (each app owns its own; see ยง13): `/todo`, `/remind`, |
| +`/poll`, `/board`, `/save`, `/vault`, `/gif`, `/emoji`, `/views`, `/connect`, |
| +`/memory`. Typing `/` brings up a typeahead listing what's available in the |
| +current thread โ bot commands only appear where a bot can actually answer them. |
| + |
| +**Aiming a command (place flags).** A command acts on the nearest copy of its |
| +app โ a chat's shared copy first, then your personal one โ and you can aim it |
| +yourself: `/todo @me dentist` files into your own private list even in a chat |
| +with a shared one, and `/todo @here โฆ` targets this chat's shared copy (or, if |
| +the chat doesn't have the app yet, adds it and files the first item in one go). |
| +Nobody has to memorise this: when an app lives in more than one place, the `/` |
| +menu offers each variant as its own labelled suggestion โ "Add a to-do just for |
| +you" / "Add a to-do for this group" โ with the plain, personal command always |
| +first. |
| |
| `/memory` (in a DM with an AI helper) opens the **memory scratchpad**: the |
| small set of facts that helper remembers about you. Memory is per-chat and |
| @@ -394,10 +570,11 @@ thread. |
| |
| --- |
| |
| -## 12. Mini-apps |
| +## 13. Mini-apps |
| |
| A **mini-app** is a small program that lives *alongside* chats โ a to-do list, a |
| -poll, reminders, a shared whiteboard. Here's the model, then each app. |
| +poll, reminders, a shared whiteboard, a vault for the group's little secrets. |
| +Here's the model, then each app. |
| |
| ### How mini-apps work (and why they're private) |
| |
| @@ -431,34 +608,52 @@ A personal app-thread is presented one of two ways, and **the app chooses**: |
| reads as a little action line in the Chat view; the reminder firing reads as a |
| message from the bot. |
| |
| +### Apps in groups: the widget bar |
| + |
| +Apps aren't only personal. A group's โฎ menu โ **Add apps** shares an app with |
| +the chat: everyone gets **one shared copy** โ a shared to-do list, the group's |
| +poll, a whiteboard โ sitting right beside the conversation, with buttons in the |
| +header to switch between the chat, the app, or both side by side on a bigger |
| +screen. Every change syncs live for every member (a shared reminder fires for |
| +the whole group), end-to-end encrypted โ the server can't even tell *which* app |
| +a group added. Your personal lists stay your own; the group's live in the |
| +group. And where an app can't go, the picker says why in plain words ("A poll |
| +needs an audience โ it lives in a group chat"; "Connections use your own |
| +account credentials โ each person links their own"). |
| + |
| ### The apps |
| |
| - **To-do** (`/todo <task>`) โ your **personal** checklist, in its own sidebar |
| - thread. `/todo buy milk` typed in any chat files into it. Tap a row to check it |
| - off (or reopen it), newest on top; the count shows how many are left. Syncs |
| - across your devices automatically because each action is a normal encrypted |
| - event. |
| - |
| -- **Reminders** (`/remind โฆ`) โ nudge yourself later. Understands natural |
| - phrasings: `/remind Call the plumber in 2h`, `/remind me in 10 minutes to call |
| - mum`, `/remind Standup at 9:00`, or an explicit date `/remind Renew passport | |
| - 2026-08-01`. It's **server-scheduled**, so the alert fires reliably on every |
| + thread. `/todo buy milk` typed in any chat files into it (in a group with a |
| + shared list, it files there โ aim it with `@me`/`@here`, ยง12). Tap a row to |
| + check it off (or reopen it), newest on top; the count shows how many are |
| + left. Syncs across your devices automatically because each action is a normal |
| + encrypted event. |
| + |
| +- **Reminders** (`/remind โฆ`) โ nudge yourself later. In your Reminders chat, |
| + just type what and when โ "call mum in 10 minutes", "standup at 9:00" โ and |
| + if you leave the time out it asks "When should I remind you?" instead of |
| + staying silent. From any other chat, `/remind` takes the same natural |
| + phrasings (`/remind Call the plumber in 2h`, `/remind Renew passport | |
| + 2026-08-01`). It's **server-scheduled**, so the alert fires reliably on every |
| device even if the app is closed โ the server holds only the encrypted event |
| and the time, never the text. Dismissing an unfired reminder cancels it from |
| any device. |
| |
| - **Poll** (`/poll Question | Option A | Option B`) โ a quick poll **in the |
| - current chat**, visible to everyone in it (a shared app). No `|` makes it a |
| - Yes/No poll. Votes are one-per-person and changeable; a live tally shows bars |
| - and percentages. The creator can **Close** it, which posts the results back |
| - into the chat as a normal message (a durable record). The server never sees the |
| + current chat**, visible to everyone in it (a shared app: it joins the group's |
| + widget bar, with the results card one tap away). No `|` makes it a Yes/No |
| + poll. Votes are one-per-person and changeable; a live tally shows bars and |
| + percentages. The creator can **Close** it, which posts the results back into |
| + the chat as a normal message (a durable record). The server never sees the |
| question, the options, or who voted for what. |
| |
| - **Whiteboard** (`/board`) โ a **shared** canvas the group draws on together; |
| - `/board clear` wipes it, `/board close` hides it (the drawing is kept, reopen |
| - with `/board`). Pen colours and brush sizes; each stroke is one encrypted event |
| - everyone folds, so every device converges on the same picture. The server never |
| - sees a line, a colour, or who drew what. |
| + adding it to a chat opens the canvas right away. `/board clear` wipes it, |
| + `/board close` hides it (the drawing is kept โ reopen with `/board`). Pen |
| + colours and brush sizes; each stroke is one encrypted event everyone folds, |
| + so every device converges on the same picture. The server never sees a line, |
| + a colour, or who drew what. |
| |
| - **Bookmarks** (`/save <url> [| note]`) โ your **personal** stash of links, in |
| its own sidebar thread, newest first. The star feature is saving a link |
| @@ -467,6 +662,23 @@ A personal app-thread is presented one of two ways, and **the app chooses**: |
| it. Everything is encrypted into your own thread; the server never learns a URL, |
| a note, or who you saved it from. |
| |
| +- **Vault** (`/vault WiFi = hunter2`) โ a home for the small secrets a group |
| + genuinely shares: the WiFi password, the door code. Add it to a chat and |
| + everyone in it is in โ reading and adding entries; adding or removing a |
| + secret posts a quiet note in the conversation ("Ciara added a secret to the |
| + Vault") so activity is visible, never the value. A vault can be **locked |
| + with a passkey**: values then open only after Touch ID / Face ID, on any |
| + device where you've set your passkey up โ names and history stay visible, |
| + anyone can still *add* without unlocking, and anyone already in can bring a |
| + newcomer in. Locking is one-way, and as ever the server only sees |
| + ciphertext. |
| + |
| +- **GIF search** (`/gif [search]`) โ search Giphy in a panel above the message |
| + box, tap a result to send it as a picture from you. Set up once per space by |
| + whoever uses it first (a free key from developers.giphy.com); the privacy |
| + shape โ your device talks to Giphy directly, the server never sees the |
| + search โ is in ยง1. |
| + |
| - **Views** โ when a bot has a *list* to show (GitHub issues, headlines, |
| results), it doesn't flatten it into chat text: it creates a **view** โ a |
| tidy, searchable list pinned above the conversation, each row linking out |
| @@ -486,7 +698,8 @@ A personal app-thread is presented one of two ways, and **the app chooses**: |
| server stores a box only you can open. Once connected, a service can |
| install its own app (GitHub's repositories and issues; Linear's teams and |
| issues; DNSimple's domains), and the connected row's **Open** button goes |
| - straight there. |
| + straight there. Integration apps wear their real logos, so they're easy to |
| + spot; emoji stay for the apps and bots members make themselves. |
| |
| **How a bot acts on a connection:** you (Eleven) can *propose* an operation |
| โ it appears as a **tappable chip** under your reply โ but you can never |
| @@ -523,13 +736,52 @@ A personal app-thread is presented one of two ways, and **the app chooses**: |
| - *"the bot didn't respond"* โ the instance's helper isn't running; that's |
| one for the space's admin. |
| |
| -Members can build their own mini-apps โ each is one self-contained file, no |
| -build step โ but that's a developer topic; point curious people at the fact that |
| -it's open source. |
| +- **Incoming webhooks** (admins only) โ give an outside service (GitHub, |
| + Stripe, anything that can send an HTTP request) a unique URL, and everything |
| + it sends arrives as an encrypted event in the chat the admin chooses. The |
| + server never sees the content, and the receiver can never read anything |
| + back โ it's strictly one-way, inward. Under Apps โ Webhooks, once the |
| + space's operator has switched the ingest service on. |
| + |
| +--- |
| + |
| +## 14. Apps and bots members make themselves |
| + |
| +Members aren't limited to the built-in apps โ they can make their own, two |
| +ways. (An admin can switch this off: Admin โ Features โ "Let members make their |
| +own apps" โ turning it off stops *new* ones; anything already deployed keeps |
| +working, and everyone can still remove their own.) |
| + |
| +- **Ask a bot to build an app.** Tell a helper "build me a habit tracker" and |
| + it writes the app with you. Member-made app code is treated as **user |
| + content**: stored end-to-end encrypted like a message, and run only inside a |
| + **locked-down sandbox** โ no network, no keys, no reach into your messages or |
| + the page. Wherever a member-built app is added, it's labelled as |
| + member-built, not admin-reviewed code. |
| +- **App Studio** (๏ผ New chat โ Apps) is the hands-on route: paste a |
| + single-file app, hit **Check** to make sure it's sound, and **Deploy** โ it |
| + runs in the same sandbox. App Studio lists the apps you've made (edit, |
| + remove, a History of every deploy), flags one made with an older app format |
| + (a small "older format" tag) and offers a one-tap **Upgrade** that redeploys |
| + the exact same code in the current format. When any new app appears (yours, |
| + a bot's, an admin's), nobody's page is yanked out from under them โ a quiet, |
| + specific nudge appears instead ("A new app is available", "App updated"). |
| +- **Member apps can be shared with a group**: declare the app group-capable and |
| + anyone can add it to a group (โฎ โ Add apps) โ one shared instance for |
| + everyone, as easy to remove as it was to add. |
| +- **Member bots.** Ask Eleven for your own little helper โ "make me a bot that |
| + sends me a news summary every morning from RTร and the Guardian" โ and it |
| + builds the bot with you: you say what, when, and which sites (no links or |
| + feeds needed, just name them), and it proves itself by producing a real first |
| + edition **before** anything is created. Your bot gets its own chat, posts on |
| + schedule whether you're around or not, and is yours alone โ ask Eleven any |
| + time to change it or retire it; renaming the bot renames its chat too. What |
| + your bot reads and writes stays locked to your space โ the server only ever |
| + carries sealed envelopes it can't open. |
| |
| --- |
| |
| -## 13. Helpers: the bots (Claude and Eleven) |
| +## 15. Helpers: the bots (Claude and Eleven) |
| |
| Bots ("helpers", or "agents") are **ordinary members driven by code**. A bot has |
| its own keypair and identity, holds the keys to the threads it's in, decrypts and |
| @@ -538,6 +790,15 @@ like a human member's browser does. Bots wear a **BOT** badge (or **AI** if |
| they're powered by a language model). They live under the **Helpers** section of |
| the โ picker, each with a one-line description. |
| |
| +**AI is a choice each space makes.** During setup, Eleven asks plainly whether |
| +to bring AI in โ with the honest note first: Eleven's AI runs on Eleven |
| +Messenger's machines and can read the chats it's invited to, which is exactly |
| +what lets it help. Say no and nothing AI ever runs there; the answer can be |
| +changed anytime in Admin. Admins also get separate **Features** toggles for AI, |
| +bots, and apps (all on by default): AI off means helpers answer commands only; |
| +bots off stops new bots being created (existing ones keep running); apps off |
| +hides them from the New-chat menu (existing app chats keep working). |
| + |
| The two default helpers: |
| |
| - **Claude** โ the general assistant. In a **DM** it replies to everything; in a |
| @@ -549,7 +810,22 @@ The two default helpers: |
| works (this document), answer "how do Iโฆ?" questions, and **DM each new member |
| a welcome** when they join. You can also *do* small things via one-tap buttons |
| (switch theme, light/dark, open the theme picker or profile) โ and those same |
| - actions are available as instant slash commands even when AI is off. |
| + actions are available as instant slash commands even when AI is off. Beyond |
| + that: |
| + - **You remember** โ the per-DM, member-owned memory scratchpad (ยง12's |
| + `/memory`). |
| + - **You can file reminders and to-dos.** Told "remind me to pick up the kids |
| + at 4pm", you attach a button carrying the exact command (`/remind pick up |
| + the kids at 4pm`; likewise `/todo`) โ the tap is the permission, the |
| + button always shows exactly what it will do, and because it runs on the |
| + member's device, "4pm" means 4pm where *they* are. |
| + - **Research sessions across connected services.** Asked something that |
| + spans them ("which of my DNSimple domains point at GitHub Pages?"), you |
| + attach a single **read-only look** button naming the services you need; |
| + one tap lets you read just those, cross-reference, and answer right in |
| + the chat. Read-only, expires in minutes, and no key is ever handed to |
| + anyone โ not the server, not the AI. |
| + |
| (An earlier third helper, the **Announcer**, posted release notes into group |
| chats. It's retired: the app now announces its own updates โ **admins** get a |
| notification when the space updates, and the release notes live in the **Admin |
| @@ -584,19 +860,78 @@ were added to. |
| |
| --- |
| |
| -## 14. The admin console |
| +## 16. Access levels, memberships, and space plans |
| + |
| +Two different money-shaped things exist โ keep them apart when you explain: |
| + |
| +### Access levels (inside a space) |
| + |
| +Admins can group members into named **access levels** โ Coaches, Family, |
| +Founders โ from the Admin app. Each level has **its own key**, held only by its |
| +members and never by the server, so what a level unlocks stays end-to-end |
| +encrypted. Levels power: |
| + |
| +- **Level-only groups** (ยง3) โ open groups limited to one level; everyone else |
| + sees an honest locked entry. |
| +- **Cohort invites** (ยง2) โ an invite that lands joiners in a level |
| + automatically. |
| +- **Paid levels** โ an admin can put a monthly or yearly price on a level, and |
| + members subscribe to unlock its groups. Payments go **straight to the |
| + admin's own Stripe account** (linked with one click from Admin โ Payments โ |
| + no keys change hands; Stripe just tells the space which account was chosen). |
| + Members subscribe, switch level, or cancel from **Membership** (in Edit |
| + profile), always with a clear "your access ends on <date>" so nothing is a |
| + surprise. Off entirely until an admin links a Stripe account. |
| + |
| +### Space plans (hosting) |
| + |
| +Separate from levels: a space **hosted with Eleven** has a plan โ free, or a |
| +paid tier (Family, Friends, Business). Upgrading happens from the member's |
| +**Eleven home page** (each space they created shows an Upgrade โ or Manage โ |
| +button; a free space's โฎ menu also has an Upgrade entry). Payment is on |
| +Stripe's secure page โ Eleven never sees the card, name, or email, and records |
| +only that the space is upgraded; each space's subscription stands alone, so no |
| +billing identity links spaces together. What a plan changes today: the |
| +**storage allowance** (shown on the home page; a full space pauses new |
| +photo/file uploads โ nothing is ever deleted, and messages keep working), the |
| +**off-server backup cadence** (ยง20), exemption from the free three-space |
| +limit, and never being reclaimed as unused. Self-hosted servers have none of |
| +this โ there's nothing to upsell. |
| + |
| +**Your rule from ยง23 applies everywhere:** describe *where* upgrading happens; |
| +never paste a checkout, payment, or pricing link into chat. |
| + |
| +--- |
| + |
| +## 17. The admin console & the Admin app |
| |
| -The admin (whoever holds the admin link) manages the instance. The console |
| -offers: |
| +The admin manages the instance from two places: the **admin console** (the |
| +separate page the admin link opens) and the **Admin app** inside the chat, |
| +which admins see in their sidebar. |
| |
| - **The admin link** โ opens the console on any device. It carries the admin |
| token, which authorises **roster management only** โ it is **not** a decryption |
| key and can't read messages. Keep it in a password manager. |
| -- **Group name** โ the chat's title and Home-Screen app name (public branding). |
| -- **Server photo** โ an optional picture shown on invite/join/backup screens |
| - (public branding, not encrypted). |
| +- **Group name / server photo / description** โ the space's branding (ยง1); |
| + sealable from the Admin app's **Privacy** section. |
| - **Who can invite** โ off by default (admin-only); turn it on to let members |
| - invite each other. |
| + invite each other. Takes effect immediately, and flips are recorded in the |
| + audit log. |
| +- **Access levels** โ create levels, assign members, price a level (ยง16). |
| +- **Payments** โ link the space's own Stripe account, one click (ยง16). |
| +- **Features** โ the toggles: AI, bots, apps, and "let members make their own |
| + apps" (ยง14โ15). |
| +- **Reports** โ the moderation inbox (ยง8): each report with clear next steps |
| + (dismiss, warn, remove from a group / an access level / the space, remove & |
| + ban), a note of who invited the reported person, and an audit log of every |
| + moderation action. Admins get a throttled notification when reports come in |
| + โ a burst means one banner with the waiting count, not a pile. |
| +- **Updates** โ the space's release channel, the update stream (release notes |
| + as a chat view โ where "what's new" lives), a heads-up notification when a |
| + release is on the way, and the **"stay on major version"** control: leave it |
| + empty to follow every release, or set a number and bigger jumps wait for the |
| + admin's say-so (ยง20 explains channels and the safety hold). |
| +- **Webhooks** โ the incoming-webhook registry (ยง13), admin-only. |
| - **Add an agent** โ add an AI bot as a member: name, one-line description |
| (shown in the Helpers list), model tier, a system prompt/personality (a |
| non-overridable safety + privacy preamble is always applied on top), optional |
| @@ -610,11 +945,10 @@ offers: |
| |
| --- |
| |
| -## 15. The native iOS & Mac apps |
| +## 18. The native iOS & Mac apps |
| |
| -There's a **native app** for iPhone/iPad and for Mac (the Mac build shares the |
| -same code via Mac Catalyst). It keeps the exact same end-to-end encryption |
| -contract as the web app. |
| +There's a **native app** for iPhone/iPad and for Mac. It keeps the exact same |
| +end-to-end encryption contract as the web app. |
| |
| - **Shape: native shell, webview feed.** The sidebar (workspaces โ threads with |
| decrypted names and previews) and the composer are **native**; the message feed |
| @@ -625,19 +959,40 @@ contract as the web app. |
| - **Add a workspace** by pasting an invite link; the private key is stored in the |
| Keychain. You can browse and read/send in threads, including **photos** |
| (downscaled and stripped of EXIF/GPS on-device). |
| -- **Push** on native uses a notification service extension that decrypts on-device |
| - (ยง9). |
| -- **The Mac app auto-updates** (via Sparkle): it can check for and install updates |
| - in the background, with a "Check for Updatesโฆ" menu item for a manual check and |
| - a toggle to turn automatic updates off. It's distributed as a notarized direct |
| - download, not through the Mac App Store. |
| +- **Rich text on both platforms**: messages render bold, italics, code and |
| + friends, and the native composer formats as you type โ with โB/โI working |
| + from a hardware keyboard on iPhone and Mac alike. |
| +- **The safety tools are there too** (ยง8): mute a chat or a person, choose who |
| + can message you directly, end a conversation, report to the admins (admins |
| + get the moderation inbox), and block someone outright. |
| +- **Native touches**: on iPhone, photos open in the native viewer with Share / |
| + Save / Markup, and tapping a group's member count shows the members; on the |
| + Mac, chats remember exactly where you were reading (switching conversations |
| + is jump-free), dropping a file anywhere on the window stages it as an |
| + attachment, and notification banners show the sender's photo and a real |
| + preview, grouped by conversation, with muted chats and people staying quiet. |
| +- **Search digs as deep as the web**: search boxes in the New-chat pickers, and |
| + "search earlier messages" can walk your entire history, resumable if you |
| + stop. |
| +- **Tamper evidence**: the apps verify the server isn't rewriting history โ if |
| + anything is ever amiss you see a clear warning, not silence. |
| +- **Push** on native uses a notification service extension that decrypts |
| + on-device (ยง10). |
| +- **The Mac app auto-updates** (via Sparkle): background checks, a "Check for |
| + Updatesโฆ" menu item, and a toggle to turn automatic updates off. It's |
| + distributed as a notarized direct download, not through the Mac App Store. |
| + (Support note: builds before 0.7.3 had a quietly broken updater โ anyone |
| + stuck on an "Update Error!" downloads a fresh copy from the releases page |
| + once, and updates flow by themselves again.) |
| - Because the feed loads the web assets from the server, web-side improvements |
| reach the app without an app-store rebuild; only native (Swift) changes need a |
| - new build. |
| + new build. (Known gap, be honest when asked: a space with **sealed |
| + branding** (ยง1) shows in the native apps with a generic name and no photo |
| + for now.) |
| |
| --- |
| |
| -## 16. How it's built (architecture) |
| +## 19. How it's built (architecture) |
| |
| Eleven is created by **Paul Campbell** and **James Adam**. If a member asks who |
| made the app, that's the answer โ exactly those two names, never a guess or an |
| @@ -663,44 +1018,53 @@ For anyone curious about the engineering โ it's all open source: |
| - **The frontend is vanilla ES modules, no build step**, served embedded in the |
| binary. Roughly: `crypto.js` (all the AES-GCM + key-wrap/unwrap), a host/shell |
| (sidebar, thread list, WebSocket, keys, uploads, unfurl, push), pure testable |
| - helpers (`format.js`/`ui.js`), the mini-app framework (`apps.js`) plus one file |
| - per app, the push service worker, and the admin console. The native app |
| - reimplements just the identity crypto in Swift, gated by cross-implementation |
| - test vectors so it can't drift from the web format. |
| + helpers, the mini-app framework (`apps.js`) plus one file per app, a local |
| + encrypted message store (what makes reloads instant), the push service |
| + worker, and the admin console. The native app reimplements just the identity |
| + crypto in Swift, gated by cross-implementation test vectors so it can't |
| + drift from the web format. |
| |
| -### By the numbers (as of July 2026) |
| +### By the numbers (as of late July 2026) |
| |
| Concrete figures for when someone asks "how big is Eleven?" โ these are |
| hand-measured and drift a little every release, so quote them as *about*: |
| |
| -- **Source: ~44,000 lines of code** across the whole project (~37,000 if you |
| - leave out the test suites). Roughly: the web frontend is ~15,000 lines of |
| - vanilla JavaScript, the Go server ~10,000, the native iOS/Mac apps ~6,000 |
| - lines of Swift, and the helper-bot sidecar ~2,000 lines of Node. |
| -- **The server is a single static binary of ~18 MB** (~12 MB stripped) โ that |
| +- **Source: ~240,000 lines of code** across the whole project โ and about a |
| + quarter of that is tests (~177,000 lines without them). Roughly: the web app |
| + is ~32,000 lines of vanilla JavaScript, the Go server and its sibling |
| + services ~55,000, the native iOS/Mac apps ~40,000 lines of Swift, and |
| + ~13,000 lines of pages and styles (plus a small ~1,400-line Node reference |
| + for the bot sidecar โ the real sidecar is Go, inside the server binary). |
| +- **The server is a single static binary of ~34 MB** (~25 MB stripped) โ that |
| one file *is* the whole app: the frontend, a built-in database engine, and all |
| the server logic, with no other dependencies. Self-hosting a chat is copying |
| - that one file onto a box. The optional sibling services are the same order of |
| - size (the central push relay ~9 MB, the passkey-backup server ~10 MB). |
| -- **The native apps are tiny because they're thin shells** โ the interface |
| - streams from the server (ยง15), so the code that ships is just the native |
| - frame. The **iPhone/iPad app is ~1.3 MB** and the **Mac app is ~2.2 MB** to |
| - download. |
| + that one file onto a box. The optional sibling services are smaller (the |
| + central push relay ~15 MB, the sign-in/backup home server ~17 MB). |
| +- **The native apps stay small** because the message feed streams from the |
| + server (ยง18): the **Mac app is a ~26 MB download**, and the iPhone/iPad app |
| + (via TestFlight today) downloads at a few megabytes. |
| |
| -The design shows up in the numbers: the entire end-to-end-encrypted chat server |
| -fits in one ~18 MB file, and the phone app is smaller than a single photo. |
| +The design still shows up in the numbers: the entire end-to-end-encrypted chat |
| +server โ frontend, database engine, bot host and all โ fits in one file small |
| +enough to email. |
| |
| --- |
| |
| -## 17. How it's deployed (design) |
| +## 20. How it's deployed (design) |
| |
| The deployment design is worth knowing because it reinforces the privacy story โ |
| described here generically (no server addresses or operator specifics): |
| |
| - **Each chat is a fully-isolated instance.** Its own app, its own database, its |
| own admin token, its own subdomain. Instances don't talk to each other; |
| - cross-instance contact is intentionally impossible (see ยง18). A new trust |
| + cross-instance contact is intentionally impossible (see ยง22). A new trust |
| circle is a *separate* instance, not a thread inside an existing one. |
| +- **Anyone can try it in one command.** Download the server binary and run |
| + `lchat try`: your own machine gets a real public address (like |
| + `try-x7k2m.11ch.at`) serving a full end-to-end-encrypted space โ no ports to |
| + open, no domain to buy, no certificates, no root, nothing to configure. |
| + Invite people, chat from any browser or the apps; stop the command and the |
| + trial address politely retires (your data stays on your machine). |
| - **The recommended way to run one or many chats is a self-hosted router**: a |
| single box runs the app's own built-in TLS front door (automatic Let's Encrypt |
| certificates) and proxies each instance over a local socket. No external reverse |
| @@ -708,17 +1072,29 @@ described here generically (no server addresses or operator specifics): |
| is one static binary. TLS terminates at the router, but message bodies are |
| already ciphertext, so the router sees exactly what the app sees: nothing |
| readable. |
| -- **Updates are versioned and zero-downtime.** You can canary a new build on one |
| - instance while the rest stay on the old one, then roll the fleet one at a time |
| - with health checks and automatic rollback on failure. **A swap takes seconds |
| - and drops nothing:** an instance restarts in place โ the old process finishes |
| - any in-flight request while the router briefly holds new requests and re-points |
| - them at the new process the moment it's up, and live connections reconnect โ so |
| - members see no interruption. Each instance is answering again within a second |
| - or two, and rolling a whole fleet of instances takes well under a minute. |
| - Shipping a brand-new build on top of that (compiling the one binary and copying |
| - it to the box) adds about a minute โ so an update is live in a couple of |
| - minutes end to end. |
| +- **Updates ship through signed release channels, with a safety hold.** |
| + Instances follow an admin-chosen channel and update themselves. Spaces on |
| + the beta and stable channels wait 24 and 72 hours after a release appears |
| + before installing it automatically โ so a bad or tampered-with release |
| + caught in that window is withdrawn before they ever run it (withdrawal |
| + takes effect immediately; the hold only ever delays installs). Admins get a |
| + heads-up notification while the hold counts down, can apply an update by |
| + hand straight away, and can pin their space to a **major version** so |
| + breaking changes wait for their say-so while fixes keep flowing (ยง17). |
| +- **Updates are versioned and zero-downtime.** A new build can be canaried on |
| + one instance while the rest stay put, then rolled across the fleet one |
| + instance at a time with health checks and automatic rollback on failure. |
| + **A swap takes seconds and drops nothing:** the old process finishes any |
| + in-flight request while the router briefly holds new ones and re-points |
| + them the moment the new process is up โ members see no interruption. |
| +- **Backups match the plan** (for spaces hosted with Eleven): every space is |
| + backed up in full whenever it goes quiet, and again around any server |
| + update or restart โ so day to day, history is safe on every plan. The |
| + difference is a *sudden* server loss with no warning: Business spaces |
| + stream every change off the server within seconds, Community/Friends |
| + spaces sync every few minutes, and free and Family spaces sync when quiet |
| + and at least once a day. Backups are the same locked data the server |
| + already holds โ no backup can be read without your keys. |
| - **Sibling services**, described generically: |
| - a **central push relay** that holds the single Apple push-signing key so |
| individual instances (yours or a third party's) can send iOS push *without* |
| @@ -727,8 +1103,18 @@ described here generically (no server addresses or operator specifics): |
| Apple already sees, never plaintext, and instances authenticate to it with a |
| per-instance revocable token. (Web Push doesn't use it โ each instance makes |
| its own push keys.) |
| - - the **anonymous passkey-gated backup server** from ยง4, which holds only |
| + - the **anonymous sign-in/backup home server** from ยง4, which holds only |
| encrypted blobs and knows nothing an instance would recognise. |
| +- **Operations happen in the open.** The hosted fleet publishes its |
| + operational record on a public status site: an **access |
| + ledger** of every operator sign-in to the machines the fleet runs on (who, |
| + when, and a plain-language why โ never what was typed or seen, and a |
| + session with no reason is flagged loudly), a **hardening dashboard** where |
| + every server reports its own SSH lockdown posture (and is checked from the |
| + outside too, so an open door sorts straight to the top), and a running |
| + **carbon/AI ledger** estimating the AI used to build the app. It's |
| + transparency, not a lock โ the fleet holding itself to its own bar where |
| + members can see it. |
| - It can also run behind your own reverse proxy (Caddy/nginx) or on a managed |
| platform โ it's the same single binary plus a persistent database file. The |
| only hard constraints are: give it a persistent path for the database, put it |
| @@ -737,7 +1123,7 @@ described here generically (no server addresses or operator specifics): |
| |
| --- |
| |
| -## 18. Instance contexts (design direction) |
| +## 21. Instance contexts (design direction) |
| |
| The engine is one thing; different *kinds* of instance (a family, a friend group, |
| a community) differ only in **social defaults** โ who may cross the trust |
| @@ -745,12 +1131,13 @@ boundary, who can see or DM whom, and which channels exist by default. These are |
| presets over a few dials (admission, default channels, member-created channels, |
| discovery/roster visibility, who-can-DM-whom), set at creation and adjustable in |
| the admin console. The messaging core โ threads, per-thread keys, the instance |
| -key, DMs, attachments, mini-apps โ is identical everywhere. (This is a design |
| -direction; the messaging core is what's fully built today.) |
| +key, DMs, attachments, mini-apps โ is identical everywhere, and several of the |
| +dials are real controls today (open/default/level-gated groups, who-can-DM-me, |
| +the Features toggles). The full preset model is a design direction. |
| |
| --- |
| |
| -## 19. Cross-instance messaging (deliberately not built) |
| +## 22. Cross-instance messaging (deliberately not built) |
| |
| People sometimes ask whether they can chat with someone on a *different* |
| instance. Today the answer is **no, by design**: each instance is fully isolated, |
| @@ -763,7 +1150,7 @@ question, not a shipped feature. |
| |
| --- |
| |
| -## 20. Talking to members |
| +## 23. Talking to members |
| |
| - Be concrete and step-by-step; prefer pointing at the exact tap or command. |
| - When someone asks you to change the theme, switch light/dark, browse themes, or |
| @@ -779,8 +1166,8 @@ question, not a shipped feature. |
| Lean into it. |
| - **Upgrading a space / paid plans:** describe the path, never post a link. |
| When someone asks how to upgrade (or about plans and pricing), tell them it |
| - happens from their **home page** โ the same place they saved their passkey |
| - and see their list of spaces โ and that the space's menu (โฎ) has an Upgrade |
| + happens from their **Eleven home page** โ the same place they sign in and |
| + see their list of spaces โ and that a free space's menu (โฎ) has an Upgrade |
| entry on some platforms. Do **not** paste a checkout, payment, or |
| pricing-page URL into chat, and never bring up upgrading unprompted. (Why: |
| chat messages render inside the iOS app too, and app-store rules restrict |