From 36fde6583bef6b455d2217d65eaaee2f66c08e81 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Sat, 22 Aug 2026 11:32:46 +0200 Subject: fix: meshbay-node init creates the keystore, status never does MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `init` now writes config AND creates the keystore (interactive password or unlock.key/env var). Idempotent: skips either step if already done. - `status` uses load_keystore instead of load_or_create_keystore — a read-only command should never silently create identity keys. - Stub getpass in test_cli_dispatch to prevent test hangs when no keystore exists. Co-Authored-By: Claude Opus 4.6 --- packages/meshbay-node/src/meshbay_node/daemon.py | 24 ++++++++++++++++++------ packages/meshbay-node/tests/test_cli_dispatch.py | 2 ++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/meshbay-node/src/meshbay_node/daemon.py b/packages/meshbay-node/src/meshbay_node/daemon.py index 385a1da..e11a654 100644 --- a/packages/meshbay-node/src/meshbay_node/daemon.py +++ b/packages/meshbay-node/src/meshbay_node/daemon.py @@ -46,7 +46,7 @@ from meshbay_node.config import Config, DEFAULT_CONFIG_PATH, load_config, write_ from meshbay_node.roots import RootSet, RootError from meshbay_node.hub_client import HubClient, HubConfig from meshbay_node.indexer import DirectoryIndexer -from meshbay_node.keystore import load_or_create_keystore +from meshbay_node.keystore import create_keystore, load_keystore, load_or_create_keystore from meshbay_node.roster import Roster from meshbay_node.transport import ( Denylist, @@ -932,8 +932,20 @@ def main() -> None: ) if args.command == "init": - write_example_config() - print("Example config written. Edit it and run: meshbay-node") + cfg_path = args.config or DEFAULT_CONFIG_PATH + if not cfg_path.exists(): + write_example_config(cfg_path) + print(f"Config written to {cfg_path}") + else: + print(f"Config already exists: {cfg_path}") + cfg = load_config(cfg_path) + if cfg.keystore.path.exists(): + print(f"Keystore already exists: {cfg.keystore.path}") + else: + keys = create_keystore( + path=cfg.keystore.path, unlock_file=cfg.keystore.unlock_file) + print(f"Keystore created: {cfg.keystore.path}") + print(f"Node key: {keys.pk_ed25519_b64}") return if args.command == "calibrate-argon2": @@ -947,12 +959,12 @@ def main() -> None: cfg = load_config(args.config or DEFAULT_CONFIG_PATH) print(f"hub {cfg.hub.url} (user {cfg.hub.username or '—'})") - # Read straight from the keystore: the operator needs this key to link the - # node, and that happens before the daemon can ever stay running. try: - keys = load_or_create_keystore( + keys = load_keystore( path=cfg.keystore.path, unlock_file=cfg.keystore.unlock_file) print(f"node key {keys.pk_ed25519_b64}") + except FileNotFoundError: + print("node key ") except Exception as e: print(f"node key ") diff --git a/packages/meshbay-node/tests/test_cli_dispatch.py b/packages/meshbay-node/tests/test_cli_dispatch.py index 62dfe13..fdcd82e 100644 --- a/packages/meshbay-node/tests/test_cli_dispatch.py +++ b/packages/meshbay-node/tests/test_cli_dispatch.py @@ -74,6 +74,8 @@ def stub_daemon(monkeypatch, tmp_path): # No interactive prompt left to hang on. monkeypatch.setattr("builtins.input", lambda *a: "n") + import getpass + monkeypatch.setattr(getpass, "getpass", lambda *a, **kw: "test-password") # `reload` looks for a real daemon and signals it. Without this the test # SIGHUPs whatever node happens to be running on the machine — which it did, -- cgit v1.2.3