From aed220d9f0bab42efd57b56851319e840ab8ae26 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Sun, 9 Aug 2026 14:50:22 +0200 Subject: feat: password-based key derivation + operational QUICKSTART MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit keyderive.py: derive Ed25519+X25519 from username+password via Argon2id. Same credentials → same keys on any device. Encrypt/decrypt keypair bundle (AES-256-GCM) for hub storage (web clients). 7/7 tests. Full suite: 81/81. keyderive.js: browser counterpart using PBKDF2-SHA512 + random keypairs encrypted for hub storage. Avoids algorithm mismatch with Python. hub/models.py + users.py: keypair_bundle field added to User, stored on registration, returned in login response for web client key recovery. QUICKSTART.md: fully rewritten. 3 operational scripts in QE/demo-v1/: setup_demo.py — create accounts, group, distribute GEK run_node.py — start HTTP node (watches shared/ directory) download.py — bob login → GEK fetch → decrypt → save All tested locally end-to-end. No invented URLs. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- packages/meshbay-hub/src/meshbay_hub/db/models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'packages/meshbay-hub/src/meshbay_hub/db/models.py') diff --git a/packages/meshbay-hub/src/meshbay_hub/db/models.py b/packages/meshbay-hub/src/meshbay_hub/db/models.py index b76870d..2aeae41 100644 --- a/packages/meshbay-hub/src/meshbay_hub/db/models.py +++ b/packages/meshbay-hub/src/meshbay_hub/db/models.py @@ -44,8 +44,9 @@ class User(Base): pw_salt: Mapped[bytes] = mapped_column(nullable=False) pk_ed25519: Mapped[str] = mapped_column(String(64), nullable=False) # base64 raw 32B pk_x25519: Mapped[str] = mapped_column(String(64), nullable=False) # base64 raw 32B - hub_id: Mapped[str] = mapped_column(String(128), nullable=False) - status: Mapped[str] = mapped_column(String(16), default="active") # active|suspended|revoked + hub_id: Mapped[str] = mapped_column(String(128), nullable=False) + keypair_bundle: Mapped[str | None] = mapped_column(Text) # AES-GCM encrypted, web clients only + status: Mapped[str] = mapped_column(String(16), default="active") # active|suspended|revoked created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now) nodes: Mapped[list["Node"]] = relationship(back_populates="user") -- cgit v1.2.3