summaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-15 02:34:19 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-15 02:34:19 +0200
commit84b032c65e17267d41e04605e79eea82a6f5a59f (patch)
treeeb7708a72944bb15540e103b4319242199af6fbb /docs
parent5338894f7fec9e1a60affb0e2ff3b9797bcbc968 (diff)
downloadmeshbay-84b032c65e17267d41e04605e79eea82a6f5a59f.tar.gz
feat(groups): editable description, and one source of operator authority
A description could only be set the moment a group was created, so every group made before anyone thought of one stayed blank for good. The owner can now edit it from the group's page, and PATCH /v1/groups/{id} takes it. That endpoint takes the description and nothing else, deliberately. The name, the visibility and the join policy are the terms members joined on; a private group that can quietly become public is not the group they agreed to be in. Changing those needs a decision about who gets told, not a field on a form — there is a test saying so. Separately, the legacy operator key is gone. `admin_pk_ed25519` in node.toml named the operator before the roster existed and was kept so that an existing deployment would keep working; nothing uses it, and a second source of node authority is not something to carry around out of politeness. Authority is the roster, read fresh on every check. It is removed rather than ignored: a config that still names the key gets a warning at startup pointing at the file. Dropping it in silence would refuse invites and file deletion with a signature error that looks like a bug somewhere else — which is exactly how finding M3 presented. Two tests were verifying admin operations by naming a key in the context, which was the legacy path. They now pair an operator into a roster, the way an operator does. The authority test anchored on the deleted function and passed vacuously once it disappeared; it states the invariant against the verifier and the daemon instead. Also defined .btn-secondary, used in four places and styled in none. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'docs')
-rw-r--r--docs/USERGUIDE.md20
-rw-r--r--docs/invite-pairing-v1.md11
2 files changed, 27 insertions, 4 deletions
diff --git a/docs/USERGUIDE.md b/docs/USERGUIDE.md
index 214b6e9..8a1e405 100644
--- a/docs/USERGUIDE.md
+++ b/docs/USERGUIDE.md
@@ -221,6 +221,25 @@ Response:
Save `group_id` — you will need it in your `node.toml` and when adding members.
+### The description
+
+Set it at creation with `"description"`, or later from the group's page — the owner
+sees an **Edit description** link under the name. Members see it on their home page
+and, for public groups, in Explore.
+
+```
+PATCH /v1/groups/{group_id}
+Authorization: Bearer <access_token> (the group's owner)
+{"description": "host grenoble"}
+→ 200 {"group_id": "...", "description": "host grenoble"}
+```
+
+An empty string clears it; anything past 512 characters is trimmed rather than
+refused. The description is all this endpoint changes: the name, the visibility and
+the join policy are the terms members joined on, and a private group that could
+quietly become public is not the group they agreed to be in. Changing those needs a
+decision about who gets told, so it is not a field on a form.
+
### Public vs. private groups
| | Public | Private |
@@ -815,6 +834,7 @@ they speak MNP (§6), and their only HTTP surface is the operator's admin UI on
| POST | `/v1/groups` | Access token | Create group. Body: `name`. Returns `group_id`. |
| GET | `/v1/groups` | None / Access token | List/search groups. Private groups require membership. |
| GET | `/v1/groups/{group_id}` | None / Access token | Group metadata. |
+| PATCH | `/v1/groups/{group_id}` | Access token (owner) | Edit the description. Body: `description`. Nothing else is editable — see §3. |
| DELETE | `/v1/groups/{group_id}` | Access token (admin) | Revoke and delete group. |
**GEK distribution — removed.** The hub used to carry wrapped group keys between members.
diff --git a/docs/invite-pairing-v1.md b/docs/invite-pairing-v1.md
index 290c756..ad36a1a 100644
--- a/docs/invite-pairing-v1.md
+++ b/docs/invite-pairing-v1.md
@@ -96,8 +96,11 @@ node writes the pin to its roster DB, prints it in `status`
The operator types 8 characters into their own browser. Nothing is pasted, nothing
is copied out of a terminal, no browser is needed on the node host, and the hub is
-not involved at any point. `admin_pk_ed25519` in `node.toml` becomes a legacy
-fallback, still read, no longer required.
+not involved at any point. `admin_pk_ed25519` in `node.toml` became a legacy
+fallback, and was removed on 2026-08-15: the roster is the only source of operator
+authority. A config that still names the key is warned about at startup, so a
+deployment relying on it learns why its invites stopped rather than discovering a
+signature error.
### 3.2 Invite (one click, operator or delegate)
@@ -357,7 +360,7 @@ is a roster row and a CLI command — no protocol change, no migration.
| Two people race one code | Single-use row, `used_at` set under a transaction; the loser gets a plain refusal |
| Invite created while the node is offline | Not possible — invites are created on the node. The SPA must say "node offline, cannot invite" instead of failing obscurely |
| Member connects while the group has no active GEK | `join_result {ok: false, reason: "no_gek"}`; the operator runs `gek-init` |
-| Legacy deployment with `admin_pk_ed25519` set | Read at startup and inserted as an `identities` row with `pinned_via = 'legacy-config'`; no migration needed for the current demo |
+| Legacy deployment with `admin_pk_ed25519` set | Ignored since 2026-08-15, with a warning at startup naming the config file. The operator pairs a browser; there is no second path |
---
@@ -393,7 +396,7 @@ coherent with each other rather than individually demo-able.
| `identities` / `members` / `invites`, codes, single-use redemption | `meshbay_node/roster.py` |
| `join_transcript` — both public keys signed together | `meshbay_common/join.py` |
| `join_request` / `join_result` handler, valid pre-proof and post-handshake | `transport/webrtc_server.py` |
-| Admin authority read from the roster on every check, `admin_pk_ed25519` kept as legacy | `webrtc_server._verify_admin_sig` |
+| Admin authority read from the roster on every check, and from nowhere else | `webrtc_server._verify_admin_sig` |
| **Auto-pin of the keystore key deleted** (M3) | `daemon._legacy_admin_pk` |
| `meshbay-node operator pair`, roster in `status` | `daemon.main`, `ui/app.py` |
| Pairing form in the group's Members tab | `app.js`, `transport.js` |