From 5cccceb4f69feb227a6ffd4cfb00b27f2f46c9bb Mon Sep 17 00:00:00 2001 From: Jer Miller Date: Mon, 27 Jul 2026 22:46:45 -0600 Subject: [PATCH] refactor(link): retire Python caller CLI Remove the caller-side Python sol link modules now that join and serve dispatch natively. The solstone.think.link package remains for home-side modules, but it no longer re-exports a caller CLI entry point and the compat inventory drops only the link line. Remove journal link from the journal-host command surface and its generated command expectations. This is a real user-visible removal, not cleanup; expected-differs. Delete the caller-side link CLI tests with the retired modules, relocate the CSR helper into the surviving certless test helpers, and repair the remaining TLS and pairing tests without compatibility shims. Co-Authored-By: Claude Opus 5 (1M context) --- .../build_native_sol_journal_host_commands.py | 2 +- solstone/think/link/__init__.py | 12 +- solstone/think/link/__main__.py | 9 - solstone/think/link/cli.py | 46 - solstone/think/link/direct_admission.py | 33 - solstone/think/link/join_cli.py | 983 --------------- solstone/think/link/observer_paths.py | 19 - solstone/think/link/serve_cli.py | 420 ------- solstone/think/sol_cli.py | 1 - solstone/think/sol_compat_inventory.py | 1 - tests/link/certless_helpers.py | 387 +++++- tests/link/test_certless_pair.py | 4 +- tests/link/test_direct_admission.py | 55 - tests/link/test_link_cli_namespace.py | 101 -- tests/link/test_link_join_existing_dir.py | 155 --- tests/link/test_link_join_label_validation.py | 87 -- tests/link/test_link_join_observer.py | 65 - tests/link/test_link_join_pairing.py | 1115 ----------------- .../link/test_link_join_peer_existing_dir.py | 128 -- tests/link/test_link_join_peer_happy.py | 102 -- ...t_link_join_peer_instance_id_validation.py | 119 -- .../test_link_join_peer_sends_instance_id.py | 4 - tests/link/test_link_join_publication.py | 92 -- tests/link/test_link_join_refusals.py | 235 ---- tests/link/test_link_join_relay.py | 309 ----- tests/link/test_link_serve.py | 441 ------- .../link/test_secure_listener_pairing_e2e.py | 35 +- tests/link/test_tls_contexts.py | 6 +- tests/spl/test_service.py | 10 - .../test_native_sol_journal_host_commands.py | 6 +- 30 files changed, 418 insertions(+), 4564 deletions(-) delete mode 100644 solstone/think/link/__main__.py delete mode 100644 solstone/think/link/cli.py delete mode 100644 solstone/think/link/direct_admission.py delete mode 100644 solstone/think/link/join_cli.py delete mode 100644 solstone/think/link/observer_paths.py delete mode 100644 solstone/think/link/serve_cli.py delete mode 100644 tests/link/test_direct_admission.py delete mode 100644 tests/link/test_link_cli_namespace.py delete mode 100644 tests/link/test_link_join_existing_dir.py delete mode 100644 tests/link/test_link_join_label_validation.py delete mode 100644 tests/link/test_link_join_observer.py delete mode 100644 tests/link/test_link_join_pairing.py delete mode 100644 tests/link/test_link_join_peer_existing_dir.py delete mode 100644 tests/link/test_link_join_peer_happy.py delete mode 100644 tests/link/test_link_join_peer_instance_id_validation.py delete mode 100644 tests/link/test_link_join_peer_sends_instance_id.py delete mode 100644 tests/link/test_link_join_publication.py delete mode 100644 tests/link/test_link_join_refusals.py delete mode 100644 tests/link/test_link_join_relay.py delete mode 100644 tests/link/test_link_serve.py diff --git a/scripts/build_native_sol_journal_host_commands.py b/scripts/build_native_sol_journal_host_commands.py index ed9519fc6..7f1e977ae 100644 --- a/scripts/build_native_sol_journal_host_commands.py +++ b/scripts/build_native_sol_journal_host_commands.py @@ -15,7 +15,7 @@ OUTPUT = ( REPO_ROOT / "core/crates/solstone-core-sol/src/generated/journal_host_commands.rs" ) EXPECTED_SERVICE_COMMANDS_COUNT = 42 -EXPECTED_UNIVERSAL_COMMANDS = frozenset({"doctor", "check", "contract", "link"}) +EXPECTED_UNIVERSAL_COMMANDS = frozenset({"doctor", "check", "contract"}) EXPECTED_SERVICE_ALIASES = frozenset({"up", "down"}) EXPECTED_UNIVERSAL_ALIASES = frozenset() SERVICE_SENTINELS = frozenset({"think", "setup"}) diff --git a/solstone/think/link/__init__.py b/solstone/think/link/__init__.py index ec2406492..82a0ffffd 100644 --- a/solstone/think/link/__init__.py +++ b/solstone/think/link/__init__.py @@ -1,15 +1,11 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright (c) 2026 sol pbc -"""Caller-side link commands and shared pairing/runtime helpers. +"""Shared private-link pairing/runtime helpers. -`sol link` dispatches caller-side commands such as `join` and `serve`. The -supervised home-side spl rendezvous daemon lives in `solstone.think.spl` and -runs as `journal spl`. +Public caller-side `sol link` commands are native. This package still owns +home-side and shared link state, pairing, tunnel, and runtime helpers used by +Convey, observe, and SPL. """ __version__ = "0.1.0" - -from .cli import main # noqa: E402 — re-exported so `sol link` can import it - -__all__ = ["main"] diff --git a/solstone/think/link/__main__.py b/solstone/think/link/__main__.py deleted file mode 100644 index c868068d6..000000000 --- a/solstone/think/link/__main__.py +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-only -# Copyright (c) 2026 sol pbc - -"""python -m solstone.think.link — entry point for caller-side `sol link`.""" - -from solstone.think.link.cli import main - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/solstone/think/link/cli.py b/solstone/think/link/cli.py deleted file mode 100644 index df357f77f..000000000 --- a/solstone/think/link/cli.py +++ /dev/null @@ -1,46 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-only -# Copyright (c) 2026 sol pbc - -"""Caller-side `sol link` command namespace.""" - -from __future__ import annotations - -import argparse -import sys - -from solstone.think.link import join_cli, serve_cli - - -def _build_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser(description="solstone link access commands") - subparsers = parser.add_subparsers( - dest="command", - metavar="{join,serve}", - title="commands", - ) - join_parser = subparsers.add_parser( - "join", - help="join a solstone with a short code or pair link", - ) - join_cli.add_arguments(join_parser) - serve_parser = subparsers.add_parser( - "serve", - help="serve a loopback proxy over a link tunnel", - ) - serve_cli.add_arguments(serve_parser) - return parser - - -def main(argv: list[str] | None = None) -> int: - """CLI entry point for `sol link`.""" - args = list(sys.argv[1:] if argv is None else argv) - parser = _build_parser() - namespace = parser.parse_args(args) - if namespace.command is None: - parser.print_help() - return 0 - if namespace.command == "join": - return join_cli.main(namespace) - if namespace.command == "serve": - return serve_cli.main(namespace) - return 0 diff --git a/solstone/think/link/direct_admission.py b/solstone/think/link/direct_admission.py deleted file mode 100644 index 92227a177..000000000 --- a/solstone/think/link/direct_admission.py +++ /dev/null @@ -1,33 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-only -# Copyright (c) 2026 sol pbc - -"""Direct pair-link consumer address admission. - -This is deliberately not shared with ``pairing.config.is_usable_ipv4`` or -``interface_watcher._classify``. Those modules decide what the home should -advertise from configured or observed interface state; this module decides what -the joining process will dial from an operator-pasted direct pair-link. The -authoritative allow-list is ``DIRECT_PAIR_ALLOWED_NETWORKS``. -""" - -from __future__ import annotations - -import ipaddress - -DIRECT_PAIR_ALLOWED_NETWORKS: tuple[ipaddress.IPv4Network, ...] = ( - ipaddress.IPv4Network("10.0.0.0/8"), - ipaddress.IPv4Network("172.16.0.0/12"), - ipaddress.IPv4Network("192.168.0.0/16"), - ipaddress.IPv4Network("169.254.0.0/16"), - ipaddress.IPv4Network("100.64.0.0/10"), - ipaddress.IPv4Network("127.0.0.0/8"), -) - - -def is_direct_pair_candidate_allowed(ipv4: ipaddress.IPv4Address) -> bool: - """Return whether a decoded direct pair-link IPv4 candidate may be dialed.""" - - return any(ipv4 in network for network in DIRECT_PAIR_ALLOWED_NETWORKS) - - -__all__ = ["DIRECT_PAIR_ALLOWED_NETWORKS", "is_direct_pair_candidate_allowed"] diff --git a/solstone/think/link/join_cli.py b/solstone/think/link/join_cli.py deleted file mode 100644 index c59e6dabf..000000000 --- a/solstone/think/link/join_cli.py +++ /dev/null @@ -1,983 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-only -# Copyright (c) 2026 sol pbc - -"""Caller-side `sol link join` implementation. - -The direct pair-link forms (0x04 and 0x05) decode to an ordered set of local -candidate endpoints that share one nonce and one CA-cert-DER pin. The whole -embedded set must satisfy `direct_admission` before key generation or dialing; -`--home`, when supplied, is only the operator-chosen dial override after that -structural and policy check and may legitimately name a host instead of an -IPv4 address. The selected target receives one framed mTLS POST to -`/app/network/pair?token=`. The request invocation is the commit point: -once it starts, no later candidate is tried. - -The relay pair-window form (0x06) dials the relay, then runs the same inner -pairing request through the pinned TLS tunnel. - -Role-less linked-system credentials are written under -`$XDG_CONFIG_HOME/solstone-observer/spl/