diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-25 17:10:15 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-25 17:10:15 +0200 |
| commit | 90c69477d5f701158112b3c294eff26312f89da6 (patch) | |
| tree | 2691ca096da5cf93482bc750c7a03599fbc44dc8 /docs | |
| parent | 8bb94a39609f57be2c486f579849eebb04606808 (diff) | |
| download | meshbay-90c69477d5f701158112b3c294eff26312f89da6.tar.gz | |
A link is redeemable by whoever opens it first, so it can be sent by any
messaging app. The address is optional (mail + label only); a link lives
7 days, fixed. Adds a Share button; see MESHBAY_DESIGN.md §3.4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/MESHBAY_DESIGN.md | 61 | ||||
| -rw-r--r-- | docs/MESHBAY_NODE_PROTOCOL.md | 9 | ||||
| -rw-r--r-- | docs/QUICKSTART.md | 7 | ||||
| -rw-r--r-- | docs/USERGUIDE.md | 32 |
4 files changed, 63 insertions, 46 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md index 8bf5e22..c6d3cc7 100644 --- a/docs/MESHBAY_DESIGN.md +++ b/docs/MESHBAY_DESIGN.md @@ -404,6 +404,7 @@ Code lifetimes differ because the acts differ: | Code | Default | Setting | |---|---|---| | Member invitation | **7 days** | `[node] invite_ttl_hours` | +| Invitation link | **7 days** | none — fixed, and the hub clamps its ticket to the same | | Operator pairing | 24 h | `[node] pair_ttl_hours` | | Device add request | 1 h | `[node] device_request_ttl_minutes` | @@ -428,7 +429,7 @@ carries **two secrets with two jobs**: | Half | Secret | Checked by | Grants | |---|---|---|---| -| Hub ticket | 128 random bits, stored as `sha256` | the hub, against the redeeming account's **verified address** — the blind index of the address the inviter typed | membership, i.e. the right to *reach* the node (§7.1). Nothing about keys | +| Hub ticket | 128 random bits, stored as `sha256` | the hub: unknown, used by another account, expired or cancelled is refused; otherwise the **first account that redeems it** gets it | membership, i.e. the right to *reach* the node (§7.1). Nothing about keys | | Node code | the ordinary 40-bit code, with `kind = "link"` | the issuing node only | pinning and the group key, as for any code | ``` @@ -461,14 +462,26 @@ The rules the client follows, each of which a test holds: - a link whose origin is not this hub is refused (the desktop client accepts one pasted in), so a ticket is never sent to another hub. -What it holds against, per the convention at the top. **A third party holding the -link** — a messaging service that previews it, a forwarded mail — gets nothing -without the invitee's mailbox. **An active hub** is not stopped: it verifies the -addresses it compares, and could already be anybody. **A hijacked signaling path** -no longer receives the code on a native client; a browser is still **T3**, exactly as -for a typed code. What it costs: the hub learns that an owner invited an address -(as a blind index and a masked form, until the link is used or expires). Addresses -are compared case-insensitively and nothing more, so `+tags` and dots must match. +**Both halves are bearer secrets, and the link is not bound to any address.** It +is meant to be sent by whatever the inviter and the invitee already use — a +messaging application, a text — and an address the invitee must register with +was what made that fail: the person opened the link, registered with the address +they use, and was told it had been sent to another one. The address is therefore +optional and binds nothing: it is where the hub mails the link, if asked, and a +masked label in the owner's list. + +What that gives up, per the convention at the top. **Whoever holds the link first +joins**: a forwarded message, a shared screen, a group chat it was posted in by +mistake. What bounds it is that it works **once** — the person it was meant for +then finds it used, which is noticed — for **seven days**, fixed rather than the +operator's `invite_ttl_hours`; that joining is an explicit click showing the group +and the inviter; that the owner is notified of every redemption; and that +**Cancel** takes an unused one back, and removing the member a used one. A service +that *previews* the link learns nothing: everything is after `#`, which a fetch +never sends. **An active hub** is not stopped, and never was. **A hijacked +signaling path** no longer receives the code on a native client; a browser is +still **T3**, exactly as for a typed code. When an address is given, the hub +learns it was invited (in a masked form, until the link is used or expires). **Mailing the code is the inviter's choice, and it gives up the row above.** The Members tab can ask the hub to mail the code to the invitee's address on file @@ -480,8 +493,8 @@ checked by default, and is **remembered per account** (the `invite_email` preference), so an operator who unticks it once is not asked to again. Unticked, the hub is never called and the table above holds exactly. The CLI mails nothing. -The same box sits under the link form, sharing the same preference. Ticked, the hub -mails the link to the address the ticket is bound to — no second address, and +The same box sits under the link form, sharing the same preference. Ticked, and +with an address typed, the hub mails the link to that address — and **the hub writes the link itself** from its own origin, the ticket it has just minted, and the node key and code it is handed, each checked against its exact shape, so a hub-signed mail cannot be made to carry any other URL. Because that mail @@ -1676,7 +1689,7 @@ Node page: | Setting | Default | What it controls | |---|---|---| -| `invite_ttl_hours` | 168 | how long a member invitation stays valid | +| `invite_ttl_hours` | 168 | how long a member invitation stays valid. Not an invitation link, which is fixed at seven days (§3.4) | | `pair_ttl_hours` | 24 | how long an operator pairing code stays valid | | `device_request_ttl_minutes` | 60 | how long a device request waits for approval. Comfort, not security: the code is bound to the keys by its hash | | `max_concurrent_streams` | 8 | simultaneous video streams. One process per viewer, ~50 MB each; a slot is held for the length of a film, so this counts viewers | @@ -1826,23 +1839,21 @@ appear in the directory and are brokered to strangers. The check is at creation only, which is correct because the update endpoint refuses to change visibility. **An invitation link's hub half is a ticket, and it grants membership to one -account.** `group_invite_links` holds `sha256(ticket)`, the invitee's address as -the same blind index `users` has, and a masked form for the owner's list — never -the address. Redeeming requires a signed-in account whose verified address has -that index; anyone else holding the ticket is told only that it is not for this -account, and an unknown, used, expired or cancelled ticket, or a group no longer -active, is one uniform refusal. Creating a link says nothing about whether the -address has an account (**M1**). At most twenty outstanding per group, as on the -node; a lifetime clamped to thirty days; a node token may create one for its own -operator's group (the CLI) and may not ask for mail. **The list answered to the +account: the first signed-in account that redeems it.** `group_invite_links` +holds `sha256(ticket)` and, when the inviter gave an address, a masked form of it +for the owner's list — never the address, and nothing a redemption is checked +against. An unknown ticket, one used by another account, an expired or cancelled +one, or a group no longer active, is one uniform refusal. Creating a link says +nothing about whether an address has an account (**M1**). At most twenty +outstanding per group, as on the node; a lifetime clamped to **seven days**, the +node's own fixed lifetime for a link code; a node token may create one for its own +operator's group (the CLI) and may not ask for mail, and mail needs an address. **The list answered to the owner holds the links nobody has used yet**, which are the ones there is still something to do about: a redeemed one has become the member row it produced, and showing both says the same thing twice. The row itself outlives the list by thirty days, so the account that used a link is answered on a reload or in a -second tab rather than refused. What the binding holds -against, per the convention: **third parties** — a messaging service that -previews the link, a forwarded mail — and not this hub, which verifies the -addresses it compares and could already be anybody. +second tab rather than refused. What a leaked link costs is in §3.4: it is a +bearer secret, bounded by being single use, seven days and cancellable. **Which nodes host a group is answered to its members.** For a public group that is everyone, which is what public means; for a private one it is the membership diff --git a/docs/MESHBAY_NODE_PROTOCOL.md b/docs/MESHBAY_NODE_PROTOCOL.md index 2206971..3351793 100644 --- a/docs/MESHBAY_NODE_PROTOCOL.md +++ b/docs/MESHBAY_NODE_PROTOCOL.md @@ -866,10 +866,11 @@ source of authority is a second thing to get wrong. ### 8.6 Invitation links (`kind = "link"`) A code for someone who may have no account yet, so it names none: it is bound to the -first account that redeems it (`invite_link_create`, §10.4). What stops a stranger -holding it is not the node but the hub, which lets only the account whose verified -address the inviter named reach the node at all (`MESHBAY_DESIGN.md` §3.4). At the -node it is therefore a **bearer code**, and everything else about it is fixed: +first account that redeems it (`invite_link_create`, §10.4). Nothing stops a +stranger holding the whole link — the hub's ticket is a bearer secret too +(`MESHBAY_DESIGN.md` §3.4) — so what bounds it is that it works once, for seven +days, and can be cancelled. At the node it is a **bearer code**, and everything else +about it is fixed: * role `member`, never `operator`; a row that says otherwise is refused, not honoured; * one named group, never node-wide, and only redeemed on a connection authenticated to diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index bf6cc80..a11bb00 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -318,9 +318,10 @@ Send that code however you normally talk to them — message, mail, out loud. It works once, for that account only, for that group only, and the hub never sees it. You do not need to be online when they use it. -Someone with no account yet gets a link instead, bound to their e-mail address: -`meshbay-node member invite alice@example.org --link`. They open it, register -with that address, and land in the group without typing a code +Someone with no account yet gets a link instead, which you send them however you +like — a messaging application, a text: `meshbay-node member invite --link`. It +works once, for seven days, for whoever opens it first. They open it, register, +and land in the group without typing a code ([`USERGUIDE.md` §7](USERGUIDE.md#7-managing-people)). You can do the same thing from the **Members** tab of the group, from the diff --git a/docs/USERGUIDE.md b/docs/USERGUIDE.md index 0589929..6a86897 100644 --- a/docs/USERGUIDE.md +++ b/docs/USERGUIDE.md @@ -226,15 +226,15 @@ The person running the group can send you a **link** instead of a code — the way in when you have no account yet. 1. **Open the link.** It says *"You have been invited"*. -2. **Create an account** with the e-mail address the invitation was sent to — - exactly that address — or sign in if you already have one. Confirm your - address with the code the hub mails you, then sign in. +2. **Create an account**, with any e-mail address you like, or sign in if you + already have one. Confirm your address with the code the hub mails you, + then sign in. 3. You are brought back to the invitation: *"… invites you to join …"*. **Join.** 4. The group opens, and you type no code: the link carried it. -The link works once, and only for the account registered with that address; to -anyone else it says it was sent to another address. In the desktop +The link works once, for seven days: after that, or once somebody has used it, +it says it is no longer valid — ask for a new one. In the desktop application, paste the link into **Join with an invitation link** on the home page. @@ -522,7 +522,7 @@ and running `meshbay-node reload`: | Setting | Default | What it decides | |---|---|---| -| `invite_ttl_hours` | 168 (7 days) | how long an invitation stays usable | +| `invite_ttl_hours` | 168 (7 days) | how long an invitation code stays usable (an invitation link is always 7 days) | | `pair_ttl_hours` | 24 | how long an operator pairing code lasts | | `device_request_ttl_minutes` | 60 | how long a device request waits for approval | | `max_concurrent_streams` | 8 | how many people can watch video at once | @@ -570,16 +570,20 @@ need to be online then. **Inviting someone who has no account yet** is a link: ```bash -meshbay-node member invite alice@example.org --link +meshbay-node member invite --link meshbay-node member cancel <link id> # printed with the link, and in member list ``` -or the **Invite by link** box in the Members tab, under the first one: type -their e-mail address and **Create link**. Send them the -link, or leave **Send the invitation by e-mail** ticked and the hub mails it. -They register with that address and land in the group without typing a code. -The link works once and only for an account with that address, so a copy that -travels further — a forwarded mail, a chat — lets nobody else in. Links nobody +or the **Invite by link** box in the Members tab, under the first one: **Create +link**, then **Copy** it — or **Share** it, on a phone — and send it however you +like: a messaging application, a text. The e-mail address is optional: type one +and leave **Send the invitation by e-mail** ticked, and the hub mails the link +there too; it also names the link in the list below. They register (with any +address) and land in the group without typing a code. +**The link is for whoever opens it first**: it works once, for seven days, and +anyone holding it can use it — so send it only to the person it is for, and +**Cancel** it if it went somewhere else. You are notified when somebody joins +through one. Links nobody has used yet are listed under the box, and **Cancel** takes one back; once somebody joins through a link it leaves that list, and they are in the member list above it. A hub mails at most ten @@ -855,7 +859,7 @@ and every member re-admitted. It is small — back it up somewhere safe. | | | |---|---| | Invitation code | 7 days, single use, one account, one group | -| Invitation link | 7 days, single use, one group, the account registered with its address; 20 pending per group | +| Invitation link | 7 days (fixed), single use, one group, whoever opens it first; 20 pending per group | | Operator pairing code | 24 hours | | Device linking code | 1 hour | | Devices per account per node | 5 | |