summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-cli/src/meshbay_cli/client/account.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-15 02:06:35 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-15 02:06:35 +0200
commit419af660eeb8a4403e37a63c54a15586d04d5a8e (patch)
treeb0c0cdd831a5fb2864d5bedf581681aa0ec86023 /packages/meshbay-cli/src/meshbay_cli/client/account.py
parent47583c5e405f3011a526df73cc9485c46598f214 (diff)
downloadmeshbay-419af660eeb8a4403e37a63c54a15586d04d5a8e.tar.gz
feat(cli): chat — Tier 2 check on the signing device, interactive room
Each message's signing device is checked against the group's roster as the page checks it (a port of transport-roster.js, held to it by the parity test): chains of countersignatures walked from each account's roots, the keys accepted per account and node kept in the store, the roster read again once before a key is called changed. Only "changed" is said, under the message; --json carries every verdict. chat with no group opens the interactive chat: the groups, then one group's conversation IRC-style with pushed messages arriving live and a line to write in; Esc or /part goes back to the groups, /quit or Ctrl+Q quits. Like the bare command, it opens on the sign-in screen when nothing is stored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-cli/src/meshbay_cli/client/account.py')
-rw-r--r--packages/meshbay-cli/src/meshbay_cli/client/account.py14
1 files changed, 14 insertions, 0 deletions
diff --git a/packages/meshbay-cli/src/meshbay_cli/client/account.py b/packages/meshbay-cli/src/meshbay_cli/client/account.py
index 9bb85ce..96483e9 100644
--- a/packages/meshbay-cli/src/meshbay_cli/client/account.py
+++ b/packages/meshbay-cli/src/meshbay_cli/client/account.py
@@ -162,6 +162,8 @@ class Account:
"known_browser": r.get("known_browser") or previous.get("known_browser"),
# A node's key does not depend on who signed in.
"pins": previous.get("pins", {}) if previous.get("hub") == self.hub.url else {},
+ "account_pins": (previous.get("account_pins", {})
+ if previous.get("hub") == self.hub.url else {}),
}
if same_account and previous.get("browser_access"):
state["browser_access"] = previous["browser_access"]
@@ -341,6 +343,18 @@ class Account:
def pepper_version(self) -> int:
return int(self.secrets.get("pepper_version") or 1)
+ # The device keys this machine accepted for each account, per node (Tier 2,
+ # `roster.py`): public keys, kept with the other pins. Losing them costs one
+ # "first sight", never a wrong answer.
+
+ def account_pins(self, node_pk: str, user_id: str) -> list[str] | None:
+ return self.state.get("account_pins", {}).get(node_pk, {}).get(user_id)
+
+ async def set_account_pins(self, node_pk: str, user_id: str, keys: list[str]) -> None:
+ def put(state: dict, secrets: dict) -> None:
+ state.setdefault("account_pins", {}).setdefault(node_pk, {})[user_id] = keys
+ await self.update(put)
+
def pin(self, node_id: str) -> str | None:
return self.state.get("pins", {}).get(node_id)