diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-10-14 22:20:15 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-10-14 22:20:15 +0200 |
| commit | 422647e7898b8df67cd11f8fb060aec23dec84aa (patch) | |
| tree | 7ecca0e8df1bc8a7d26cf4b471bd44906dd9cfd1 /packages/meshbay-node | |
| parent | 28c88908b88c58cba4de08710f6bb268ede02d57 (diff) | |
| download | meshbay-422647e7898b8df67cd11f8fb060aec23dec84aa.tar.gz | |
feat(cli): meshbay-cli, a command-line client for members
Signs in to the hub, lists the account's groups and their folders, and
downloads and uploads files directly with each group's node, end-to-end
encrypted. With no argument it opens a two-pane interactive mode (Textual).
Session and device keys are kept encrypted under the system's key storage,
or a 0600 key file where there is none; node keys are pinned at first
contact; a new machine links as a device.
Packaged on its own on Linux (deb and rpm, on meshbay-common's venv, with a
man page generated from the parser) and frozen next to meshbay-node in the
full Windows installer, not the light one, with a self-test the build runs.
client/keys.py ports keyderive.js and is LGPL like it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node')
| -rw-r--r-- | packages/meshbay-node/tests/test_licensing.py | 34 | ||||
| -rw-r--r-- | packages/meshbay-node/tests/test_packaging_win.py | 5 |
2 files changed, 34 insertions, 5 deletions
diff --git a/packages/meshbay-node/tests/test_licensing.py b/packages/meshbay-node/tests/test_licensing.py index 239633d..0cb85cc 100644 --- a/packages/meshbay-node/tests/test_licensing.py +++ b/packages/meshbay-node/tests/test_licensing.py @@ -11,6 +11,7 @@ LGPL. import importlib.util import json import re +import sys import tomllib from importlib import metadata from pathlib import Path @@ -22,7 +23,9 @@ VENDOR = STATIC / "vendor" DESKTOP = PACKAGES / "meshbay-client" / "src" ANDROID = PACKAGES / "meshbay-android" / "app" / "src" / "main" / "kotlin" KEYS = ANDROID / "org" / "meshbay" / "client" / "keys" +CLI = PACKAGES / "meshbay-cli" / "src" / "meshbay_cli" SPDX_LGPL = "// SPDX-License-Identifier: LGPL-3.0-or-later\n" +SPDX_LGPL_PY = "# SPDX-License-Identifier: LGPL-3.0-or-later\n" # The protocol layer in the clients (README, "Licence"): what a program needs to # speak to a hub and a node, under the LGPL in every language it exists in. LGPL_FILES = sorted( @@ -31,11 +34,13 @@ LGPL_FILES = sorted( + sorted(STATIC.glob("transport-*.js")) + [DESKTOP / f for f in ("keyring.js", "transcripts.js", "argon2-wasm.js")] + [KEYS / f for f in ("Kdf.kt", "Keyring.kt", "Transcripts.kt")] + + [CLI / "client" / "keys.py"] ) EXPECTED = { "meshbay-common": ("LGPL-3.0-or-later", ["COPYING", "COPYING.LESSER"]), "meshbay-hub": ("AGPL-3.0-or-later", ["LICENSE"]), "meshbay-node": ("AGPL-3.0-or-later", ["LICENSE"]), + "meshbay-cli": ("AGPL-3.0-or-later", ["LICENSE"]), } @@ -63,7 +68,7 @@ def test_licence_texts_are_the_right_ones(): agpl = (ROOT / "LICENSE").read_text(encoding="utf-8") assert "GNU AFFERO GENERAL PUBLIC LICENSE" in agpl and "Version 3" in agpl # Copies, because a wheel's license-files cannot reach outside its package. - for pkg in ("meshbay-hub", "meshbay-node"): + for pkg in ("meshbay-hub", "meshbay-node", "meshbay-cli"): assert (PACKAGES / pkg / "LICENSE").read_text(encoding="utf-8") == agpl, pkg common = PACKAGES / "meshbay-common" assert "GNU LESSER GENERAL PUBLIC LICENSE" in (common / "COPYING.LESSER").read_text( @@ -73,7 +78,8 @@ def test_licence_texts_are_the_right_ones(): def test_rpm_specs_and_the_client_agree_with_the_packages(): - for pkg in ("meshbay-common", "meshbay-hub", "meshbay-node", "meshbay-client"): + for pkg in ("meshbay-common", "meshbay-hub", "meshbay-node", "meshbay-cli", + "meshbay-client"): spec = (ROOT / "packaging" / "rpm" / f"{pkg}.spec").read_text(encoding="utf-8") want = EXPECTED.get(pkg, ("AGPL-3.0-or-later",))[0] assert re.search(rf"^License:\s+{re.escape(want)}\s*$", spec, re.M), pkg @@ -136,14 +142,18 @@ def test_every_vendored_file_has_its_provenance_and_licence(): def _sources(): - for tree, pattern in ((STATIC, "*.js"), (DESKTOP, "*.js"), (ANDROID, "**/*.kt")): + for tree, pattern in ((STATIC, "*.js"), (DESKTOP, "*.js"), (ANDROID, "**/*.kt"), + (CLI, "**/*.py")): for f in tree.glob(pattern): if "vendor" not in f.parts and "locales" not in f.parts: yield f def test_the_lgpl_files_are_exactly_the_ones_that_say_so(): - marked = sorted(f for f in _sources() if f.read_text(encoding="utf-8").startswith(SPDX_LGPL)) + def spdx(f: Path) -> str: + return SPDX_LGPL_PY if f.suffix == ".py" else SPDX_LGPL + + marked = sorted(f for f in _sources() if f.read_text(encoding="utf-8").startswith(spdx(f))) assert marked == LGPL_FILES @@ -202,6 +212,22 @@ def test_the_lgpl_layer_depends_on_nothing_under_the_agpl(): } for f in LGPL_FILES: src = f.read_text(encoding="utf-8") + if f.suffix == ".py": + # The standard library, meshbay-common (the LGPL layer itself) and + # permissive packages; never the client package it sits in. + for mod in re.findall(r"^\s*(?:from|import)\s+([\w.]+)", src, re.M): + top_name = mod.split(".")[0] + assert top_name and top_name != "meshbay_cli" and not mod.startswith("."), ( + f"{f.name} imports {mod}") + if top_name in sys.stdlib_module_names or top_name == "meshbay_common": + continue + dist = metadata.packages_distributions()[top_name][0] + md = metadata.distribution(dist).metadata + label = " ".join([md.get("License-Expression") or "", md.get("License") or ""] + + (md.get_all("Classifier") or [])) + assert "GPL" not in label, f"{f.name} imports {mod} ({dist}: {label[:80]})" + assert not re.search(r"^\s*from\s+\.", src, re.M), f"{f.name} has a relative import" + continue if f.suffix == ".kt": for imp in re.findall(r"^import (org\.meshbay\.[\w.]+)", src, re.M): owner = ( diff --git a/packages/meshbay-node/tests/test_packaging_win.py b/packages/meshbay-node/tests/test_packaging_win.py index 69eefc8..ed8104f 100644 --- a/packages/meshbay-node/tests/test_packaging_win.py +++ b/packages/meshbay-node/tests/test_packaging_win.py @@ -173,7 +173,10 @@ def test_the_frozen_exe_carries_a_version_resource(): spec = (WIN / "meshbay-node.spec").read_text(encoding="utf-8") assert "VSVersionInfo" in spec assert "version=_version_info" in spec, "EXE() is not given the version resource" - assert 'StringStruct("ProductName", "MeshBay Node")' in spec + assert '_version_info = _version_resource("meshbay-node", "MeshBay Node")' in spec + assert 'StringStruct("ProductName", description)' in spec + # The command-line client beside it, under its own name. + assert 'version=_version_resource("meshbay-cli", "MeshBay command-line client")' in spec assert '_pkg_version("meshbay-node")' in spec, "version is hardcoded, not read from the package" |