<feed xmlns='http://www.w3.org/2005/Atom'>
<title>meshbay.git/packages/meshbay-hub/tests/test_federation.py, branch main</title>
<subtitle>MeshBay — read-only public mirror</subtitle>
<id>https://git.meshbay.org/meshbay.git/atom?h=main</id>
<link rel='self' href='https://git.meshbay.org/meshbay.git/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/'/>
<updated>2026-09-15T00:21:01Z</updated>
<entry>
<title>feat(hub): usernames are at least 8 characters at registration</title>
<updated>2026-09-15T00:21:01Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-15T00:16:39Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=bdefcd025604f2c3009fe5e0cc01213c2ba62a6a'/>
<id>urn:sha1:bdefcd025604f2c3009fe5e0cc01213c2ba62a6a</id>
<content type='text'>
Existing shorter accounts keep signing in. Test usernames padded to match.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01XuNrwLf5EFWCMHzfoEvnpm
</content>
</entry>
<entry>
<title>fix(hub): federation is closed until two hubs have run it</title>
<updated>2026-09-13T20:14:47Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-13T20:14:47Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=413837a0845240241ed7e9d9ac1f3b1dc45a2f40'/>
<id>urn:sha1:413837a0845240241ed7e9d9ac1f3b1dc45a2f40</id>
<content type='text'>
Every MHP route answers a stated 503. `federation.FEDERATION_ENABLED` is the
only thing that decides it, applied as a dependency on the router so the six
routes that exist and any added later are covered by construction — a gate you
have to remember to write in each handler is the shape C6 is the standing
lesson about.

The protocol is not what is wrong with it. What is wrong is that nothing has
ever run it: two hubs have never completed one authenticated request between
them. AV14 was two defects in the same path — an issuer signing with a key
bound before it was loaded, naming itself after the reference deployment
whatever the instance was called, and a verifier naming no audience for the
`aud` the issuer sets, which PyJWT refuses outright. Both stood for a month
behind a green suite, and both were found by reading rather than by running,
because a second implementation of a peer proves the protocol and nothing about
two machines.

Four of the six routes carry no authentication of their own — the MHP token is
the authentication — and two of those write, a directory push and a revocation.
That is the surface being closed until somebody stands up a second hub.

A constant and not a `hub_settings` row, deliberately: a switch in the admin
panel invites an operator to turn on a feature that has never worked between
two machines, where this takes an edit, a deploy, and reading the comment above
it. `/v1/hub/info` reports the state, because the `mhp_version` beside it would
otherwise be a claim the hub does not honour.

The protocol tests open the gate for their own duration and say why; the one
that runs with the flag as it ships asserts all six routes refuse. §7.6 states
the closure, §15.2 carries federation between two hubs as not built.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01UMxEQadpzPkYLFf5CYKhpW
</content>
</entry>
<entry>
<title>fix(hub): MHP binds its audience, and the hub knows its own name</title>
<updated>2026-09-12T14:36:54Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-12T10:14:40Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=903ea022e918a05c7c8cb43d95e46d82368566f1'/>
<id>urn:sha1:903ea022e918a05c7c8cb43d95e46d82368566f1</id>
<content type='text'>
Federation has never worked between two hubs, and the tests said so without
anyone reading it that way.

`federation.py` did `from meshbay_hub.auth import _hub_id, _hub_sk_pem` at
import — which is before `load_hub_keypair` runs. So it held the key as
`None` and the identity as the module default: `_issue_mhp_token` could only
raise, and `/mhp/info`, the directory export and every token announced this
instance as `meshbay.org` whatever it was configured as. Read through
accessors now, at call time.

And `_verify_mhp_token` named no audience while `_issue_mhp_token` sets one.
PyJWT refuses a token carrying `aud` when decode is given none, so every
token this hub issues was rejected by every hub running this code. Naming the
audience fixes that and makes the binding real: a token minted for one peer
is refused by another, which is what stops a captured request being replayed
at a third hub. The comment claiming audience binding was unavailable because
"the sending side is unbuilt" was describing a function four lines below it.

Both were already written down. `test_federation.py` built envelopes by hand
without an `aud`; `test_public_groups_toggle.py` signed its own token with a
comment saying `_issue_mhp_token` "binds `_hub_sk_pem` at import time, before
the lifespan loads it, so it cannot be used from a test", and another saying
PyJWT rejects a token carrying `aud` when decode is given none. Both
observations were exactly right, and both were treated as facts to route
around. When a test has to work around the code to run, the thing it worked
around is the finding. Those helpers now go through the real issuer, and two
tests pin the identity and the audience refusal.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01T4YmK41VsEURWFdop4EEeT
</content>
</entry>
<entry>
<title>fix(hub): constrain what a federated peer hub can do (MHP)</title>
<updated>2026-09-01T17:50:22Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-01T17:50:22Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=24d29e910c8d1649a1cd51969f27b921b94d97e3'/>
<id>urn:sha1:24d29e910c8d1649a1cd51969f27b921b94d97e3</id>
<content type='text'>
A registered peer was trusted with more than "advertise your own
public groups":

- `receive_directory` set `source_hub` from `body.hub_id`, so a peer
  could relay or spoof a third hub's groups into our directory. It is
  now bound to the token's verified `iss`. The push is also capped
  (500 groups/request, 2000/peer), rows are type- and length-checked,
  and a federated id that collides with a local group is refused so it
  cannot shadow one.

- `receive_revocation` forwarded the peer's token to local nodes,
  which reject a token signed by another hub's key — a silent no-op,
  and there is no local node hosting a federated group anyway. It now
  verifies the inner token against the sending peer's key and, for
  `target == "group"`, prunes our copy of the peer's directory entry
  when `source_hub` matches. A peer cannot revoke our users or a group
  it did not advertise.

- The state-changing endpoints (`POST /mhp/directory`, `/mhp/revoke`)
  now reject a replayed `jti` within the token's TTL. Audience binding
  is unavailable — the sending side that would set `aud` is unbuilt —
  and this covers the replay concern in its place; the idempotent
  `GET /mhp/directory` is not affected.

Third security review, finding M4.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_011pG75yGK3NthNfyjH74omG
</content>
</entry>
</feed>
