<feed xmlns='http://www.w3.org/2005/Atom'>
<title>meshbay.git/packages/meshbay-hub/src/meshbay_hub/app.py, branch 0.2</title>
<subtitle>MeshBay — read-only public mirror</subtitle>
<id>https://git.meshbay.org/meshbay.git/atom?h=0.2</id>
<link rel='self' href='https://git.meshbay.org/meshbay.git/atom?h=0.2'/>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/'/>
<updated>2026-08-13T09:00:39Z</updated>
<entry>
<title>fix: swarm privacy, revocation persistence, keystore KDF, audit integrity</title>
<updated>2026-08-13T09:00:39Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-08-13T09:00:39Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=9df71bd1e5244743fae8c1b2bda41143f0748d9d'/>
<id>urn:sha1:9df71bd1e5244743fae8c1b2bda41143f0748d9d</id>
<content type='text'>
Phase 11.5 hardening batch — H7, H4, M2, M6, M7, L1, L3, L6.

H7 — private content hashes leaked to the hub. The daemon registered blake3
hashes for every group it hosted, private ones included, giving the hub a
content fingerprint of every private file and letting anyone confirm whether a
known file exists in the network. The leak was dormant only because the routes
were declared on the groups router with a full path and mounted at
/v1/groups/v1/swarm/* — the node's calls 404'd into a swallowed exception.
Fixing the path alone would have activated the leak, so both land together:
registration is gated on group visibility, the routes moved to a real
/v1/swarm router, and the lookup now requires authentication.

H4 — revocation was advisory. Group revocations were signed and broadcast by
the hub and then dropped by the node, whose handler understood only "user" and
"jti", so "suspend a group" enforced nothing. The denylist was also in-memory
only, so a restart silently un-revoked everyone. Now persisted to
data_dir/denylist.json, group targets honoured on both transports, and live
sessions for a revoked group are closed.

M2 — the node keystore, which protects the node's Ed25519 and X25519 private
keys, was still deriving at 64 MB long after the hub's password verifier moved
to 256 MB; the docs recorded the bump as done, true for the hub only. Raising
the constant alone would have made every existing keystore permanently
undecryptable, so envelopes now record the parameters they were written with
and pre-M2 files continue to open under the legacy profile.

M6 — registration inserted its audit row with a NULL user_id and then ran
UPDATE ip_logs SET user_id=&lt;new&gt; WHERE user_id IS NULL, claiming every
unattributed row in the table: failed logins for other usernames, concurrent
registrations. In logs retained a year for legal requests, that attributed
other people's connections to the wrong account.

M7 — X-Forwarded-For was trusted unconditionally at four call sites, so anyone
could forge the IP written to the compliance log and evade per-IP rate limits.
New netutil.client_ip honours the header only from a trusted proxy and takes
the rightmost hop (the one our proxy appended); no direct header reads remain.

L1 dead GEK_REQUEST/GEK_RESPONSE constants removed; L3 peer errors no longer
echo exception text (paths, internal state); L6 email sanity-checked instead of
accepting any string — deliberately not RFC 5322, to avoid a new dependency.

test_daemon_index_change_pushes_to_peers asserted that a PRIVATE group's hashes
are registered with the hub. Split: private asserts not-called (index push to
members still asserted), and a new test proves public groups still register.
That is the fourth pre-existing test found asserting a vulnerability as
intended behaviour, after gek auto-activation, the transport-wide chat_store
and the blind admin challenge.

Tests: 116 node, 132 hub+common. Regression suite now 43.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(hub): Phase 10.5–10.8, 10.10 — notifications, settings, search, version</title>
<updated>2026-08-11T10:40:13Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-08-11T10:40:13Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=edde9e441fb6b84e9d56215d6e2a8d9338b8f962'/>
<id>urn:sha1:edde9e441fb6b84e9d56215d6e2a8d9338b8f962</id>
<content type='text'>
- 10.5: Notification model + CRUD API (list, mark read, mark all read)
  Triggered on: group invite, role change, suspend/unsuspend
- 10.6: SettingsPage shows role, per-group notification mute (localStorage)
- 10.7: GET /v1/groups?q= search filter (ilike on name)
- 10.8: NotificationFeed on home page + bell with unread badge in navbar
- 10.10: GET /v1/hub/version endpoint for client update checks
- 8 new tests (test_notifications.py), 155 total

Co-Authored-By: Claude Opus 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(hub): Phase 10.1–10.4 — Site overlay + admin/moderation UI</title>
<updated>2026-08-11T09:50:08Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-08-11T09:50:08Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=c8f2de4025ea67b579e66cf608f08a8d35ee4a3c'/>
<id>urn:sha1:c8f2de4025ea67b579e66cf608f08a8d35ee4a3c</id>
<content type='text'>
- Site overlay: landing page, /about, /downloads (dark/light, responsive)
- User role column (user/moderator/admin) with config-based admin sync
- require_moderator dependency + admin API (8 endpoints: stats, users,
  groups, audit logs)
- Admin SPA panel at #/admin with 5 tabs (stats, users, groups, logs,
  blocklist) — visible only to moderators/admins
- SPA also served at /app/ for Caddy site overlay integration
- GET /v1/users/me returns current user role
- 15 new tests, 147 total passing

Co-Authored-By: Claude Opus 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat: Phase 9 — Web client SPA with WebRTC P2P transport</title>
<updated>2026-08-11T02:13:53Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-08-11T02:13:53Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=e23e33adeaf8ee7439187d4451c856b37816a51f'/>
<id>urn:sha1:e23e33adeaf8ee7439187d4451c856b37816a51f</id>
<content type='text'>
Complete browser-based client: Preact SPA with login, group file browser,
encrypted download, video playback, group chat, i18n, and dark/light theme.
Browser connects P2P to nodes behind residential NAT via WebRTC DataChannel
(aiortc). Hub handles signaling only — all data flows E2E.

Performance: pipelined downloads (8-chunk sliding window), binary msgpack
wire format (no base64), redundant I/O elimination. Large file downloads
stream to disk via File System Access API (showSaveFilePicker).

Validated on SFR + Orange residential NATs, Chrome + Firefox, IPv4/IPv6.
132 tests passing. Deployed to meshbay.org + Orange node.

Co-Authored-By: Claude Opus 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat: Phase 9.1–9.5 — WebRTC DataChannel transport for browser P2P</title>
<updated>2026-08-10T20:12:59Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-08-10T20:12:59Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=60c4570e72e36c2a9720593c8baec74ee2ab52d6'/>
<id>urn:sha1:60c4570e72e36c2a9720593c8baec74ee2ab52d6</id>
<content type='text'>
Browser clients can now connect P2P to nodes behind residential NAT via
WebRTC DataChannel with ICE/STUN. Validated on SFR Port-Restricted Cone
NAT + 4G CGNAT across three scenarios (WiFi LAN, 4G IPv6, 4G IPv4 STUN).
No TURN relay needed. Hub serves only as signaling relay (&lt;1 KB).

New files:
- webrtc_server.py: aiortc-based WebRTC transport (node side)
- signaling.py: SDP/ICE relay endpoint (hub side)
- transport.js: browser WebRTC client with msgpack framing
- webrtc-test.html: spike test page for browser→NAT→node validation
- test_webrtc_transport.py: 4 tests (handshake, file transfer, auth, guard)
- meshbay-draft-v4.md: architecture spec updated for web client

Modified:
- hub_client.py: WebRTC offer handling via hub WebSocket
- revocation.py: node_id from WS auth + webrtc_answer routing
- pyproject.toml: aiortc&gt;=1.9 dependency

123 tests passing (117 existing + 6 new).

Co-Authored-By: Claude Opus 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(hub): Phase 8 — Hub v2 security hardening + production readiness</title>
<updated>2026-08-10T01:57:55Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-08-10T01:57:55Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=1a53eb4cc404ec94658fde0ae04cfe2ccf1810dc'/>
<id>urn:sha1:1a53eb4cc404ec94658fde0ae04cfe2ccf1810dc</id>
<content type='text'>
8.1  Config-based admin authz (require_admin on all admin endpoints)
8.2  Email encrypted at rest (AES-256-GCM, HKDF from hub Ed25519 key)
8.3  Refresh token rotation with family-based reuse detection
8.4  Federation persistence (HubPeer model replaces in-memory dict)
8.5  Federation token verification now async (DB-backed)
8.6  CSAM hash check wired into swarm registration flow
8.7  Rate limiting on auth endpoints (5/10/20 per minute)
8.8  Healthcheck endpoint (GET /v1/health, no auth)
8.9  IP log cleanup background task (365-day retention)
8.10 Argon2id params bumped to 256 MB (pw_version, rehash on login)

Deployed to meshbay.org — schema migrated, existing emails encrypted.
117 tests pass (29 hub, 88 common+node).

Resolves security review items S1, S2, S5.

Co-Authored-By: Claude Opus 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(hub): add CSAM hash matching module — 5.8</title>
<updated>2026-08-09T03:21:43Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-08-09T03:21:43Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=a01088d2a1679467124ae230ffda4d39fb3d83a2'/>
<id>urn:sha1:a01088d2a1679467124ae230ffda4d39fb3d83a2</id>
<content type='text'>
CSAMChecker: loads blake3 hash database from file (NCMEC/IWF format).
check_content_hash(): used before serving public content.
/v1/admin/csam/status: hash count + DB path.
/v1/admin/csam/check: admin-only hash check (no hash logged).

Hash database NOT included — hub operators must obtain access
from NCMEC (US) or IWF (EU). Instructions in csam.py header.
59/59 tests.

Co-Authored-By: Claude Sonnet 4.6 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(hub): add MHP federation + Mesh Relay registration — 5.2 + 5.3</title>
<updated>2026-08-09T03:20:43Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-08-09T03:20:43Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=95e045272243043ed9a207115f7737a0e3eb1ccc'/>
<id>urn:sha1:95e045272243043ed9a207115f7737a0e3eb1ccc</id>
<content type='text'>
Federation (MHP 0.1): /mhp/info, /mhp/directory (GET=export, POST=receive),
/mhp/revoke (propagation), /mhp/peers (admin registration).
Peer auth: JWT EdDSA signed by requesting hub's key.
Explicit peer allowlist — no auto-discovery.

Relay (5.3): /v1/relays (GET=list), /v1/relays/register (relay keepalive),
/v1/relays/approve (admin pre-approval). Relays pre-approved by admin,
then self-register with signed endpoint. Nodes query when hole punch fails.
59/59 tests.

Co-Authored-By: Claude Sonnet 4.6 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(hub): add moderation — content blocklist + reports — 5.9</title>
<updated>2026-08-09T03:17:28Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-08-09T03:17:28Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=42556800d103ede20b4e97f2d91d20bbc0000c1e'/>
<id>urn:sha1:42556800d103ede20b4e97f2d91d20bbc0000c1e</id>
<content type='text'>
DB: ContentReport + ContentBlocklist tables.
POST /v1/reports: public endpoint, auto-blocks after 2 reports.
GET /v1/blocklist/check: node sync check before serving public content.
GET /v1/blocklist: full list for node startup sync.
GET|POST|DELETE /v1/admin/blocklist: admin management.
6/6 tests. Full suite: 59/59.

Co-Authored-By: Claude Sonnet 4.6 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat: add revocation push (WebSocket hub→node) — 5.7</title>
<updated>2026-08-09T03:15:57Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-08-09T03:15:57Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=1734c668406c66e2be63e0c6999b4b2af2f60808'/>
<id>urn:sha1:1734c668406c66e2be63e0c6999b4b2af2f60808</id>
<content type='text'>
Hub: /v1/nodes/ws WebSocket endpoint for persistent node connections.
/v1/admin/revoke marks user/group revoked in DB, signs JWT revocation
token (EdDSA), broadcasts to all connected nodes.

Node: RevocationSubscriber maintains WS connection, verifies
incoming revocation tokens offline (hub Ed25519 PK), adds to
local blocklist (_revoked_users/_revoked_groups sets).
53/53 tests.

Co-Authored-By: Claude Sonnet 4.6 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
</feed>
