<feed xmlns='http://www.w3.org/2005/Atom'>
<title>meshbay.git/packages/meshbay-node/tests/test_roster_pairing.py, branch 0.20</title>
<subtitle>MeshBay — read-only public mirror</subtitle>
<id>https://git.meshbay.org/meshbay.git/atom?h=0.20</id>
<link rel='self' href='https://git.meshbay.org/meshbay.git/atom?h=0.20'/>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/'/>
<updated>2026-10-15T12:24:44Z</updated>
<entry>
<title>fix(node): a removal made at the machine is in audit.db</title>
<updated>2026-10-15T12:24:44Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-10-15T12:24:44Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=283771ff468bd9e1a60a4902a5ada4319c063073'/>
<id>urn:sha1:283771ff468bd9e1a60a4902a5ada4319c063073</id>
<content type='text'>
The Node page and the CLI remove access through the loopback API, and
revoke and unpin there wrote nothing to audit.db; only the signed MNP door
did. A removed person's next connection then showed an auth_failed with
nothing before it to explain it. Both routes now record member_revoke and
member_unpin once the operation has succeeded, with the node's own account
as the actor and 127.0.0.1 as the address, which tells the two doors apart.
A refusal changes nothing and records nothing.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix(node): an operator who was a member first holds the authority at once</title>
<updated>2026-10-15T10:32:24Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-10-15T10:32:24Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=e0a1036e0fee15b3c6c83f3e935d50f472a2e776'/>
<id>urn:sha1:e0a1036e0fee15b3c6c83f3e935d50f472a2e776</id>
<content type='text'>
A member pinned on the node who then redeems the operator pairing code was
written down as the operator but the node kept its "nobody can authorize"
flag until it restarted: every admin operation — removing a folder, inviting,
the apps' settings — was refused meanwhile. Only a key paired fresh set it.

The join result now names `operator` for an account holding the node-wide
operator row, whatever its row in the group says. The page and the CLI read
that role to know what to offer, and refused before asking deletions the node
would have accepted. Nothing is granted by it: every admin operation is still
checked against the operators' keys.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix(node): name members admitted without an invitation name</title>
<updated>2026-10-07T20:20:45Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-10-07T20:12:54Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=462d76898a306981fbeac859cd54da1468e80639'/>
<id>urn:sha1:462d76898a306981fbeac859cd54da1468e80639</id>
<content type='text'>
A member who joined by link, by a new device or into an open group was
pinned in the roster with no name, so the audit log showed only the
first characters of their id. The hub's MNP token now carries the
account's username, and after the handshake the node writes it into the
roster for an account whose name is empty. An invitation's name is never
overwritten; the name is a label, authority stays on `sub`.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix: a member can no longer lock a node, crash it with a link, or stop hub cleanup</title>
<updated>2026-09-30T09:22:24Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-30T09:22:24Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=69554fac7eba6eef7eb8a1c0111c5b92e7f21256'/>
<id>urn:sha1:69554fac7eba6eef7eb8a1c0111c5b92e7f21256</id>
<content type='text'>
- node: only a wrong code counts towards the join lock, now per account
  (5) as well as node-wide (20), and it is consulted only when a code is
  tried. Every member reconnecting gets the group key through join_request,
  so a lock checked before recognition let one member refuse it to everyone.
- node: link previews read the body as a stream and stop at the cap,
  counted on decoded bytes; a declared oversized image is not read; 15 s
  total deadline; image decoding off the loop. `client.get` had buffered
  the whole (decompressed) response before the caps looked at it.
- hub: the daily purge of never-verified accounts detaches their IP-log
  rows (keeping the name) and clears every other reference first, and each
  cleanup step runs on its own. On PostgreSQL the bare DELETE violated the
  ip_logs foreign key and stopped every purge behind it for good.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>test: make both suites pass on Windows</title>
<updated>2026-09-27T20:21:26Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-27T20:21:26Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=a45e77df1b0024707914a446aa89d33baa223787'/>
<id>urn:sha1:a45e77df1b0024707914a446aa89d33baa223787</id>
<content type='text'>
Most of these failed on Windows for reasons that had nothing to do with the
code under test, which is how real Windows defects hid among them:

- Read and write files as UTF-8, and talk to Node in UTF-8. read_text(),
  write_text() and subprocess text=True use the locale codepage, cp1252 on
  Windows: "é", "—" and "→" arrived as "?" or crashed, some sixty tests.
  Calls to PowerShell and schtasks are left alone -- they answer in the
  console codepage.
- Import ESM harness modules by file URL (as_uri): a raw "C:\..." path is not
  a module specifier.
- test_cli_golden: mask the tmp path in its JSON-escaped form, spell it the
  POSIX way, record on Linux, mask the protocol version (the recording had
  failed everywhere since the MNP 4.0 bump) and argparse's version-dependent
  quoting; point USERPROFILE at the tmp home, or `member invite` and
  `operator pair` wrote their codes into the developer's profile.
- test_disk_io_off_loop: expect what a free loop can reach on the platform's
  timer, 15.6 ms on Windows, not an assumed 5 ms.
- test_root_paths_are_operator_only: expect the OS's spelling of the path.
- test_audio_meta_cache: find ffprobe with shutil.which.

Node suite on Windows: 1489 passed, none failed. Hub suite: 3 failures left,
all older than this change (two SQLite concurrency tests, one transfer resume).

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>refactor(node): move the CLI out of daemon.py into cli/</title>
<updated>2026-09-24T16:19:13Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-24T16:19:13Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=6e9afe3e54ab6fb0162393150b7892655b4561d4'/>
<id>urn:sha1:6e9afe3e54ab6fb0162393150b7892655b4561d4</id>
<content type='text'>
Each `if args.command == ...` branch of main() becomes a function in
cli/ (one module per family of verbs); the parser, the process setup
and a VERBS table go to cli/parser.py and cli/dispatch.py. daemon.main
runs the verb or starts the daemon. Tests patch the CLI through
conftest.patch_cli; the CLI golden is unchanged.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>test(node): read the daemon's source wherever it is split</title>
<updated>2026-09-24T15:58:08Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-24T15:42:10Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=ef942b93d1e9bfc28bf607dfaf501cc7d9685e7b'/>
<id>urn:sha1:ef942b93d1e9bfc28bf607dfaf501cc7d9685e7b</id>
<content type='text'>
Tests that read daemon.py now take their text from node_source
(daemon.py, NodeDaemon's bases, the cli package once it exists). The
CLI tests that sliced main() run the CLI instead.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>test: read the WebRTC transport's source as a set of files</title>
<updated>2026-09-24T14:45:37Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-23T20:25:28Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=328b01a2dd545d70a078db8df1e91b02d65bfc9c'/>
<id>urn:sha1:328b01a2dd545d70a078db8df1e91b02d65bfc9c</id>
<content type='text'>
Source-reading tests take their text from node_source (node) and node_tree
(hub): webrtc_server.py plus anything under transport/webrtc/, so a check
for something's absence keeps reading the code it guards if that code moves.
test_node_source_scope holds the boundary.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix: removing someone who never redeemed their invitation</title>
<updated>2026-09-20T16:56:58Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-20T16:56:58Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=375ad7d0435a176ad593a32045a5f0182a36d505'/>
<id>urn:sha1:375ad7d0435a176ad593a32045a5f0182a36d505</id>
<content type='text'>
A member row appears only when a code is consumed, so revoking someone
invited to the wrong group was refused for having no row — and the node's
refusal aborted the browser's removal before its hub half, leaving them a
member everywhere with a live code. Revoking now cancels unredeemed codes
for that group, and a node refusal no longer cancels the hub removal.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>style: the 98 ruff could not fix, so the linter is a signal again</title>
<updated>2026-09-19T12:39:38Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-19T12:39:38Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=9e7b75bb0f6f6649fb00f2dc97059e90b7d52875'/>
<id>urn:sha1:9e7b75bb0f6f6649fb00f2dc97059e90b7d52875</id>
<content type='text'>
The pass before this applied ruff's own fixes. These are the ones needing a
decision, and the point of doing them is that `ruff check .` now passes: a
linter reporting 98 known-acceptable findings reports nothing, because the next
real one arrives invisible.

**Lines over 100 (70).** Mostly wrapped where they stood. Two exceptions: the
aligned trailing comments in `protocol.py`'s message table were shortened rather
than wrapped, because wrapping one row of a table breaks the table; and in
`models.py` the column comments moved above their columns for the same reason.

**Imports below the first statement (14).** `csam.py` kept its FastAPI imports
under a section header halfway down the file; two node tests had a constant and
a `pytestmark` wedged between two import blocks. Moved, not suppressed.

**Bindings nothing reads (4).** Three in tests, where the call stays and only the
name goes — `_user(client, "listener")` is there to create the user, not to
return one. The fourth was in `revocation.py` and was not a lint finding at all:
`_connect_and_listen` opened an httpx stream to the WebSocket URL, did `pass`,
and then opened the real connection through the `websockets` library. One
pointless request per connect, left over from before that library was used
directly. Removed, and `httpx` with it.

**`l` as a name (4)**, **semicolons (6)** in the POC spikes, and the rest.

2893 passed, the same count as the two commits before it.

`meshbay_node/revocation.py` is worth a decision separately: 154 lines that
nothing imports, superseded by `hub_client.maintain_ws`'s `on_revocation`. This
commit only stopped it failing the linter.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
</feed>
