aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/transport-roster.js
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-08 01:12:01 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-08 01:12:01 +0200
commitcdd5fd52e981c4c59643e7dee705b6c55acae68e (patch)
tree3aea907ab1f494b8e8fbecf72ef16edcf4f6ae52 /packages/meshbay-hub/src/meshbay_hub/static/transport-roster.js
parented0c680790950354f15fb5835e1d7b213efa1bf8 (diff)
downloadmeshbay-cdd5fd52e981c4c59643e7dee705b6c55acae68e.tar.gz
fix(hub): re-read the roster before saying a key changed
A member invited after the roster was read showed "key changed" on each message until a reload. Read it again once per account and device on the connection, shared by concurrent messages, and pin only the final verdict. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/transport-roster.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/transport-roster.js30
1 files changed, 30 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/transport-roster.js b/packages/meshbay-hub/src/meshbay_hub/static/transport-roster.js
index cac3b52..d2e8c94 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/transport-roster.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/transport-roster.js
@@ -103,6 +103,36 @@ async function _writePinnedAccount(nodePk, userId, keys) {
}
/**
+ * How `devicePk` stands against what is pinned for its account (`known`, null
+ * at first sight) and what the roster lists for it (`entry`): `{ status, pin }`,
+ * `pin` being the keys to keep if that answer stands. Decides and writes
+ * nothing, so a verdict reached on a stale roster can be taken again on a fresh
+ * one without having pinned half a set first. The statuses are
+ * `accountDeviceStatus`'s.
+ */
+function _judgeDevice(known, entry, devicePk) {
+ if (known && known.includes(devicePk)) return { status: 'pinned', pin: null };
+ if (!known) {
+ // First sight, so **everything the node says** is pinned — not only what
+ // a chain reaches. There is nothing to compare against yet: that is what
+ // trust-on-first-use means, and pinning only the verified subset would
+ // raise "key changed" on a legitimate second device whose
+ // countersignature simply predates it being kept. What TOFU buys is that
+ // a substitution *later* is visible; it cannot buy anything now.
+ const pin = entry ? entry.all : null;
+ return { status: entry && entry.all.includes(devicePk) ? 'first' : 'changed', pin };
+ }
+ if (entry && entry.verified.includes(devicePk)
+ && entry.chain.get(devicePk)
+ && known.includes(entry.chain.get(devicePk))) {
+ // Countersigned by a key we already trust for this account: a second
+ // device of someone we know, admitted without anybody comparing digits.
+ return { status: 'linked', pin: [...new Set([...known, devicePk])] };
+ }
+ return { status: 'changed', pin: null };
+}
+
+/**
* A wire payload as text.
*
* A plaintext message arrives as a string from the node; msgpack `bin` arrives