aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/api/users.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-30 15:06:14 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-30 15:06:14 +0200
commit91297944791a36f30302ef8c86dd69ebeb177671 (patch)
tree568188114baf438059458f1bc87903f4894cec90 /packages/meshbay-hub/src/meshbay_hub/api/users.py
parenta55d40b74bda77dff6ec565abdd551607fc665d6 (diff)
downloadmeshbay-91297944791a36f30302ef8c86dd69ebeb177671.tar.gz
feat: bundles sealed per node under the passphrase and the hub's pepper
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 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/api/users.py')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/users.py9
1 files changed, 3 insertions, 6 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/users.py b/packages/meshbay-hub/src/meshbay_hub/api/users.py
index 72ac68f..88e6d9e 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/users.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/users.py
@@ -368,8 +368,9 @@ async def _login_failed(db: AsyncSession, username: str, ip: str,
#
# Half of what opens this account's keypair bundles on nodes; the passphrase is
# the other half. Handed out only where the caller proved the passphrase or a
-# device key — sign-in, device sign-in, a passphrase change — or already holds a
-# session that did (`GET /me/bundle-pepper`). Never on a token refresh, which
+# device key — sign-in, device sign-in — or already holds a session that did
+# (`GET /me/bundle-pepper`, which a passphrase change uses: it re-seals every
+# bundle before the hub is asked to accept the new passphrase). Never on a token refresh, which
# proves only possession of a refresh token; never to a node token; never in a
# token or a log line.
@@ -1084,9 +1085,6 @@ async def change_password(
))
db.add(IPLog(user_id=current_user.id, event="password_change",
ip_address=client_ip(request)))
- # The new passphrase makes a new bundle key, and the client does not keep
- # the pepper; this call proved the old passphrase, so it carries it.
- pepper = _bundle_pepper(current_user)
await db.commit()
return {
@@ -1095,7 +1093,6 @@ async def change_password(
"refresh_token": raw_rt,
"token_type": "bearer",
"expires_in": _ttl(),
- **pepper,
}