aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/api/federation.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-09 05:31:05 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-09 05:31:05 +0200
commit3b2dd318477eb268e6821fb000aeadfe60d85987 (patch)
tree0c46c3ea663490bbee847a0eec5a41c41ad6561b /packages/meshbay-hub/src/meshbay_hub/api/federation.py
parent6d64da7816aec2cc58bb1a6f0aceb9c9a921129f (diff)
downloadmeshbay-3b2dd318477eb268e6821fb000aeadfe60d85987.tar.gz
feat: Phase 6 complete — chat, multi-group, federation, replication, webcrypto
6.1 Double Ratchet (meshbay_common/ratchet.py): Forward secrecy, break-in recovery, out-of-order delivery. Signal-spec KDF_RK/KDF_CK via HKDF-SHA256. 11/11 tests. 6.2 Multi-group node (config.py): [[groups]] TOML array, per-group ports, back-compat [group]. 6.3 MHP federation persistence (db/models.py FederatedGroup + SwarmSource): receive_directory() now persists to federated_groups table. list_public_groups() includes federated results with source attribution. 6.4 Content replication (node/replication.py + hub SwarmSource): ContentReplicator: fetch-index, download, hash-verify, register-swarm. Hub: POST /v1/swarm/register, GET /v1/swarm/{hash} for multi-source. 6.5 Browser private group (webcrypto.py + static/crypto.js): AES-256-GCM variant of GEK for WebCrypto-compatible groups. crypto.js: SubtleCrypto importGEK + deriveChunkKey + decryptChunk. Keys distinct from ChaCha20 via :aes HKDF info suffix. 4/4 tests. 74/74 tests total. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/api/federation.py')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/federation.py28
1 files changed, 24 insertions, 4 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/federation.py b/packages/meshbay-hub/src/meshbay_hub/api/federation.py
index 5a7f45f..88bb645 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/federation.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/federation.py
@@ -36,6 +36,7 @@ from meshbay_hub.api.deps import get_current_user
from meshbay_hub.auth import _hub_id, _hub_sk_pem, hub_public_key_pem
from meshbay_hub.db.engine import get_db
from meshbay_hub.db.models import Group, User
+from sqlalchemy.ext.asyncio import AsyncSession
log = logging.getLogger(__name__)
@@ -141,19 +142,38 @@ class DirectoryPayload(BaseModel):
async def receive_directory(
body: DirectoryPayload,
authorization: str = Header(...),
+ db: AsyncSession = Depends(get_db),
):
"""
Receive a Mesh Directory update from a peer hub.
- The directory is stored in memory (production: DB table federated_groups).
+ Persists groups to federated_groups table for cross-hub search.
"""
try:
_verify_mhp_token(authorization.removeprefix("Bearer "))
except Exception as e:
raise HTTPException(status_code=401, detail=str(e))
- log.info("Received %d groups from hub %s", len(body.groups), body.hub_id[:16])
- # TODO Phase 5+: persist to federated_groups table, make searchable
- return {"accepted": len(body.groups), "from_hub": body.hub_id}
+ from meshbay_hub.db.models import FederatedGroup
+ from datetime import datetime, timezone
+ now = datetime.now(timezone.utc)
+ count = 0
+ for g in body.groups:
+ existing = await db.get(FederatedGroup, g["id"])
+ if existing:
+ existing.name = g.get("name", existing.name)
+ existing.join_policy = g.get("join_policy", existing.join_policy)
+ existing.updated_at = now
+ else:
+ db.add(FederatedGroup(
+ id=g["id"],
+ name=g.get("name", ""),
+ source_hub=body.hub_id,
+ join_policy=g.get("join_policy", "invite"),
+ ))
+ count += 1
+ await db.commit()
+ log.info("Persisted %d groups from hub %s", count, body.hub_id[:16])
+ return {"accepted": count, "from_hub": body.hub_id}
# ── Revocation propagation ────────────────────────────────────────────────────