<feed xmlns='http://www.w3.org/2005/Atom'>
<title>meshbay.git/packages/meshbay-hub/src/meshbay_hub/static/profile-page.js, 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-15T07:33:28Z</updated>
<entry>
<title>feat(hub): one account, several linked nodes</title>
<updated>2026-10-15T07:33:28Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-10-15T07:33:28Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=1e8eae1f3de765fcd8163716de6e5fe5aab5a034'/>
<id>urn:sha1:1e8eae1f3de765fcd8163716de6e5fe5aab5a034</id>
<content type='text'>
An account links up to MAX_NODES_PER_ACCOUNT node keys (node_keys table)
instead of the one users.pk_node_ed25519; the migration carries each
account's key over and the downgrade keeps the last one linked.
/v1/nodes/auth accepts any linked key, and its token names the key that
won it (nk). PUT /me/node_key adds a key, idempotently, 409 past the
limit; DELETE /me/node_key names the key: a browser session any of its
own, a node only its own. Unlinking a key closes that node's socket and
refuses its tokens at once, on REST and on the socket; the account's other
nodes are untouched. /pubkeys no longer gives a node key to other accounts,
and gives the caller its last linked one, which installed desktop apps read.

The node reports waiting_for_node_key while its key is unlinked and signs
in again as soon as the hub says so, so a key linked again is back within
seconds; unlinking itself signs in first to hold a token naming its key.

Profile -&gt; Linked nodes lists each node (connected, last seen, IP, linked
since, This machine in the desktop app) with Copy and Unlink, and adds a
key by hand. The Node page banner, Create group and the desktop app add
this machine's key instead of replacing another's. Messages that pointed
at Settings -&gt; Link Node point at Profile -&gt; Linked nodes.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix(client): say when the account's node is elsewhere, and let go of it</title>
<updated>2026-10-10T07:57:29Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-10-10T07:57:17Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=876f4a351f859201ca68fbf8d585c86905571686'/>
<id>urn:sha1:876f4a351f859201ca68fbf8d585c86905571686</id>
<content type='text'>
Signing in on a desktop links this machine's node to the account, but never
over a key already linked (that would cut off the user's other machine) nor a
node set up for another account. On a real install both left the node reading
"Running" while the hub refused it in a loop, and nothing said why. And the
only way to unlink was the linked machine's own Node page, of no use once
that machine is gone.

- The Node page works out, from the node and the hub each time it looks,
  whether this node can serve the signed-in account (nodeLinkProblem), and
  says why not: "Link this node instead" (asked first) puts this node's key
  on the account; "Use this node for my account" switches a node set up for
  another account through node:start, which takeOver now lets past a node
  that answers "running". The first version went through node:start for
  both, and clicking it on a real install did nothing: a node signed in
  before the account was linked elsewhere answers "running".
- The Profile page unlinks the account's node (DELETE /v1/users/me/node_key),
  from any machine.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat: move the desktop client's Hub section from Settings to Profile</title>
<updated>2026-10-05T08:39:50Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-10-05T08:39:50Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=0b2634a20febbfe6d05e9b588093adcfbf13cab6'/>
<id>urn:sha1:0b2634a20febbfe6d05e9b588093adcfbf13cab6</id>
<content type='text'>
The hub address is where the person's account lives, so it sits with the
account: on the Profile page, after Sessions and before deleting the account.
Settings keeps "Keys on this device", which describes the machine.

The hint now says what the setting is: the hub this application connects
to, and that changing it signs you out while the account stays on that hub.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix(hub): the pepper and a device key take the passphrase, not a token</title>
<updated>2026-09-30T19:04:39Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-30T19:04:39Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=0ed56d3a1b4f71cf622d3e27edc87a15ef33c185'/>
<id>urn:sha1:0ed56d3a1b4f71cf622d3e27edc87a15ef33c185</id>
<content type='text'>
POST /me/bundle-pepper (was GET) and POST /users/devices require auth_key.
A refreshed or lifted token could otherwise fetch the pepper, or register a
device whose every sign-in carries it. Both callers have just been given the
passphrase.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat: browser access, decided in the desktop application</title>
<updated>2026-09-30T15:13:40Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-30T15:13:40Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=b1ebcdeb9082457972c41a47e77494902335d262'/>
<id>urn:sha1:b1ebcdeb9082457972c41a47e77494902335d262</id>
<content type='text'>
Off for an account made there: its identities stay on the device and nothing is
left on nodes. Turned on from the Profile page behind a native confirmation;
each node is settled when its group next opens. The hub keeps a mirror a
browser reads to say why a group will not open; it grants nothing.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(client): the desktop application keeps M and every node identity in its main process</title>
<updated>2026-09-30T14:58:31Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-30T14:58:31Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=6d167392f6f8ede37e2794a68a3738f8ba03131d'/>
<id>urn:sha1:6d167392f6f8ede37e2794a68a3738f8ba03131d</id>
<content type='text'>
keyring.js derives, opens, mints, seals, signs and agrees there; the page gets
public keys and a handle. Argon2 comes from the page's own WebAssembly build
(Electron's crypto has none). Without OS key storage the page keeps its keys as
a browser does. A node's bundle is settled after connecting, re-sealed when the
key changed.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat: bundles sealed per node under the passphrase and the hub's pepper</title>
<updated>2026-09-30T13:06:14Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-30T13:06:14Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=91297944791a36f30302ef8c86dd69ebeb177671'/>
<id>urn:sha1:91297944791a36f30302ef8c86dd69ebeb177671</id>
<content type='text'>
The session key is M = HKDF(Argon2(passphrase) || pepper, account id); each
node's bundle key and the playlist key derive from it. Bundles are MBK3, bound
to account and node; MBK1/MBK2 are refused by name, never replaced silently.
Playlists move to key v2 and are re-sealed over unreadable node copies.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix(hub): require the passphrase to change the e-mail on file</title>
<updated>2026-09-25T15:24:16Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-25T12:28:03Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=6b9b5394c5ec01c5de01b7bf23bc161792f38278'/>
<id>urn:sha1:6b9b5394c5ec01c5de01b7bf23bc161792f38278</id>
<content type='text'>
A member hands its hub access token to every node it connects to (the MNP
handshake), so a node operator holds a live bearer token for that member.
PATCH /v1/users/me {email} needed only that token, and the confirmation code
goes to the new address — so an operator could point the account's e-mail at
their own inbox, confirm it, and then use the passphrase-reset path to take the
account over. This is the immediate mitigation of that chain; the full fix
(a node-audience token distinct from the API session token) follows.

Changing the address now requires the passphrase-derived auth_key, verified
through the same throttle as a passphrase change or an account deletion — the
hub still never sees the passphrase. A PATCH that does not change the address is
unaffected. The profile page prompts for the passphrase and derives auth_key
with the existing MeshBayKeys.deriveAuthKey, as the delete and change-password
flows already do.

test_email_change_requires_passphrase.py: refused without / with a wrong
passphrase, proceeds with the right one, and a no-email PATCH still works; red
before, green after. test_mail_is_not_a_relay.py updated to pass the auth_key.

Co-Authored-By: Claude Opus 4.8 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix: ask in the page instead of native confirm/alert</title>
<updated>2026-09-21T17:53:33Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-21T17:53:33Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=9f3445d03f106ee3ebd8b4b1bd546a08d9169af7'/>
<id>urn:sha1:9f3445d03f106ee3ebd8b4b1bd546a08d9169af7</id>
<content type='text'>
A native confirm() or alert() leaves the desktop client unable to type
until the window is refocused. ask.js draws both in the page; the SPA
test now bans all three browser dialogs.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>docs: cite MESHBAY_DESIGN.md and a section instead of the merged notes</title>
<updated>2026-09-19T00:12:47Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-19T00:12:47Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=20a824118c09af15d6c338db4c9480ffe5cbcdb6'/>
<id>urn:sha1:20a824118c09af15d6c338db4c9480ffe5cbcdb6</id>
<content type='text'>
The per-feature design notes were merged into docs/MESHBAY_DESIGN.md and
deleted from the tree on 2026-09-11, but ~230 comments across the three
packages still named them — usually written `docs/musicbay.md §3.2`, as
though the file were still in docs/. A reader had to know §16 existed to
resolve any of them. They now name the section directly.

Every mapping comes from §16, the concordance, which already records where
each old section landed: musicbay -&gt; §9.8, mediacenter -&gt; §9.7 for the
Videos app and §6.5 where the subject is derived data, photos -&gt; §9.9,
auth-confirm -&gt; §3.6, refactoring-search -&gt; §9.11, invite-pairing-v1 -&gt;
§3.4, per-node-identity-v1 -&gt; §3.2, captcha -&gt; §7.7, chat-sender-keys -&gt;
§4.5, apps/refactor-groups -&gt; §9.1–§9.4, desktop-client-v1 per section.
Bare citations of the same documents (`draft-v6 §2.11`, `§4.8`, `§3.4`)
are retargeted too: those collide with real section numbers in the design
document, so leaving them would have been worse than the named form.

Four cases the concordance does not cover, each decided rather than guessed:

  Sub-item references into documents that no longer exist — mediacenter's
  `§3.3 row 4`, `§3.4b/c`, `apps.md §3b` — name rows and sub-items §9.7 and
  §9.2 do not reproduce. The module-level citation stays; the sub-item
  pointer is dropped.

  The V-findings keep their labels but lose the dead `§10.1/` prefix.
  §13.8 lists V1–V13 as per-application open items, which is not what the
  labels mean in these comments, so pointing them at §13.8 would have been
  a false citation.

  `apps.md §5`'s virtualization requirement has no counterpart anywhere in
  the design document. The requirement is stated in the comment itself, so
  the citation is dropped rather than aimed at a section that does not say
  it.

  Comments that attributed a *sentence* to an old note — musicbay's "several
  thousand files" example, its "what got measured" note, its measured
  ~11%/~26% cover-art figures, the "original no root, whole shared tree"
  call — state the fact without attribution now. §9.8 does not contain those
  sentences and citing it for them would have been wrong.

CLAUDE.md's "a reference to a document that no longer exists" row now says
the concordance is for git history and out-of-tree material; the code cites
sections directly.

Verified: 2851 passed, 4 skipped. The 12 errors in the run are the Firefox
leg of test_sticky_header.py's browser harness, which is broken at the
browser level on this machine — headless Firefox (snap) dies with
`[GFX1-]: RenderCompositorSWGL failed mapping default framebuffer`, renders
nothing, and the probe exits `{"error": "no measurement"}` after its full
90s wait. Chrome runs the same 12 assertions in 3.2s and passes. Nothing
here can affect it: every changed line in style.css is inside a comment.
Also checked: ast.parse on every changed .py, `node --check` on every
changed .js, the /* */ balance in style.css, and that no changed line
exceeds the width its file already used.

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