diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-10-15 02:06:35 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-10-15 02:06:35 +0200 |
| commit | 419af660eeb8a4403e37a63c54a15586d04d5a8e (patch) | |
| tree | b0c0cdd831a5fb2864d5bedf581681aa0ec86023 /packages/meshbay-cli/src/meshbay_cli/client/chat.py | |
| parent | 47583c5e405f3011a526df73cc9485c46598f214 (diff) | |
| download | meshbay-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/chat.py')
| -rw-r--r-- | packages/meshbay-cli/src/meshbay_cli/client/chat.py | 15 |
1 files changed, 11 insertions, 4 deletions
diff --git a/packages/meshbay-cli/src/meshbay_cli/client/chat.py b/packages/meshbay-cli/src/meshbay_cli/client/chat.py index bb0c83f..60629e2 100644 --- a/packages/meshbay-cli/src/meshbay_cli/client/chat.py +++ b/packages/meshbay-cli/src/meshbay_cli/client/chat.py @@ -13,9 +13,10 @@ shown as written by anyone — then the epoch key, then the content. A message that does not open is kept and marked, never dropped: a gap the reader can see is honest, a conversation quietly missing a message is not. -Not done here, and done by the application: checking that the signing device -is one the group's roster evidences for the account the node names (Tier 2, -design §3.3). The name shown is the one sealed in the message by its sender. +Then whose device signed it: the node names an account (`sender_id`), and +`roster.RosterCheck` answers whether the signing device is one this machine +accepted for that account, or one countersigned into it since (Tier 2, design +§3.3). The name shown is the one sealed in the message by its sender. """ import time @@ -27,6 +28,7 @@ from meshbay_common.groupbox import PURPOSE_CHAT_KEYS, unseal from ..errors import NODE, USAGE, CliError from .keys import b64, unb64 +from .roster import RosterCheck from .session import GroupSession # The node answers at most this many messages per request. @@ -45,6 +47,7 @@ class Message: sender: str # the name sealed by its sender, else the node's text: str unreadable: str = "" # why it could not be read: signature, epoch, ... + trust: str = "" # roster.RosterCheck's verdict on the signing device class ChatKeys: @@ -85,7 +88,8 @@ async def history(session: GroupSession, count: int) -> list[dict]: return rows[-count:] -def open_row(row: dict, keys: ChatKeys, group_id: str) -> Message: +async def open_row(row: dict, keys: ChatKeys, group_id: str, + check: RosterCheck | None = None) -> Message: sender = str(row.get("sender_name") or row.get("sender_id") or "") msg = Message(int(row.get("id") or 0), float(row.get("timestamp") or 0), sender, "") if (row.get("format") or 0) != FORMAT_SEALED: @@ -110,6 +114,9 @@ def open_row(row: dict, keys: ChatKeys, group_id: str) -> Message: return msg msg.text = str(payload.get("text") or "") msg.sender = str(payload.get("sender_name") or sender) + if check is not None: + msg.trust = await check.status(str(row.get("sender_id") or ""), + b64(bytes(device))) return msg |