aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/api/federation.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/api/federation.py')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/federation.py49
1 files changed, 48 insertions, 1 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/federation.py b/packages/meshbay-hub/src/meshbay_hub/api/federation.py
index 327102e..9e252c6 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/federation.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/federation.py
@@ -40,7 +40,54 @@ from meshbay_hub.db.models import FederatedGroup, Group, HubPeer, User
log = logging.getLogger(__name__)
-router = APIRouter(prefix="/mhp", tags=["federation"])
+# ── Federation is closed ──────────────────────────────────────────────────────
+#
+# **Every MHP endpoint refuses, and this flag is the only thing that decides it.**
+#
+# Not because the protocol is wrong, but because nothing has ever run it. Two
+# hubs have never completed a single authenticated request between them: until
+# 2026-09-12 `_issue_mhp_token` bound the hub's signing key at import — before
+# `load_hub_keypair` runs — so this hub signed with `None` and called itself
+# `meshbay.org` whatever it was named, while the verifier named no audience for
+# the `aud` the issuer sets, which PyJWT refuses outright (**AV14**). Both were
+# found by reading, and they were found because a test had written down, in its
+# own words, that the real issuer "cannot be used from a test" and had signed
+# its own tokens instead. What else is in here of that shape is not known, and
+# the way to know is to stand up a second hub — not to leave the door open
+# meanwhile.
+#
+# The surface being closed is worth naming: four of the six routes carry no
+# authentication of their own (the MHP token *is* the authentication), two of
+# those write — a directory push and a revocation — and every one of them is
+# reachable by anyone who can reach the hub.
+#
+# **A constant and not a setting, deliberately.** A row in `hub_settings` and a
+# switch in the admin panel would invite an operator to turn on a feature that
+# has never worked between two machines. This takes an edit and a deploy, by
+# somebody who has read this. The refusal is a stated 503 rather than a 404
+# because a peer hub deserves a reason it can act on, which is §5.6's rule for
+# the wire one level up.
+#
+# **To re-open it:** set this True, stand up a second hub, and run the exchange
+# both ways. `test_federation.py` covers the protocol and proves nothing about
+# two machines; §15.2 carries federation as not built until that has happened.
+FEDERATION_ENABLED = False
+
+
+def _federation_open() -> None:
+ """Refuse every route on this router while federation is closed.
+
+ A router dependency rather than a line in each handler: it covers the six
+ routes that exist and every one anybody adds later. A gate you have to
+ remember to write is the shape **C6** is the standing lesson about.
+ """
+ if not FEDERATION_ENABLED:
+ raise HTTPException(status_code=503,
+ detail="Federation is not enabled on this hub")
+
+
+router = APIRouter(prefix="/mhp", tags=["federation"],
+ dependencies=[Depends(_federation_open)])
# One push may not dump the world, and one peer may not fill the table.
MAX_FEDERATED_GROUPS_PER_PUSH = 500