feat: TLS nudge in doctor and service install output

When host is set to network access (not 127.0.0.1) and TLS is not
configured, doctor shows 'Run: muxplex setup-tls' and service install
shows a tip line. Hidden on localhost-only setups since clipboard
works without HTTPS there.
This commit is contained in:
Brian Krabach
2026-04-04 03:40:44 -07:00
parent 7db56e600f
commit c92689dab2
4 changed files with 150 additions and 6 deletions
+8 -3
View File
@@ -369,9 +369,14 @@ def doctor() -> None:
else:
print(f" {warn_mark} TLS: configured but cert not readable ({tls_cert})")
else:
print(
f" {warn_mark} TLS: disabled (clipboard requires HTTPS on non-localhost)"
)
# Only show TLS warning if host is not localhost
host = cfg.get("host", "127.0.0.1")
if host != "127.0.0.1":
# Network host without TLS: show nudge
print(
f" {warn_mark} TLS: disabled — clipboard won't work on remote devices"
)
print(" Run: muxplex setup-tls")
# Auth status
pw_path = get_password_path()
+14
View File
@@ -116,6 +116,18 @@ def _prompt_host_if_localhost() -> None:
# ---------------------------------------------------------------------------
def _show_tls_nudge_if_needed() -> None:
"""Show TLS setup nudge if host is network and TLS is not configured."""
from muxplex.settings import load_settings
settings = load_settings()
host = settings.get("host", "127.0.0.1")
tls_cert = settings.get("tls_cert", "")
if host != "127.0.0.1" and not tls_cert:
print(" Tip: Enable HTTPS for clipboard support: muxplex setup-tls")
def _systemd_install() -> None:
muxplex_bin = _resolve_muxplex_bin()
safe_path = os.environ.get("PATH", "/usr/local/bin:/usr/bin:/bin")
@@ -128,6 +140,7 @@ def _systemd_install() -> None:
subprocess.run(["systemctl", "--user", "daemon-reload"], check=True)
subprocess.run(["systemctl", "--user", "enable", "--now", "muxplex"], check=True)
_prompt_host_if_localhost()
_show_tls_nudge_if_needed()
def _systemd_uninstall() -> None:
@@ -179,6 +192,7 @@ def _launchd_install() -> None:
["launchctl", "bootstrap", f"gui/{uid}", str(_LAUNCHD_PLIST_PATH)], check=True
)
_prompt_host_if_localhost()
_show_tls_nudge_if_needed()
def _launchd_uninstall() -> None:
+51 -3
View File
@@ -1618,11 +1618,14 @@ def test_doctor_shows_tls_enabled(tmp_path, monkeypatch, capsys):
def test_doctor_shows_tls_clipboard_warning(tmp_path, monkeypatch, capsys):
"""doctor() mentions clipboard or https when TLS is disabled."""
"""doctor() mentions clipboard or https when TLS is disabled on network host."""
import json
import muxplex.settings as settings_mod
settings_file = tmp_path / "settings.json"
settings_file.write_text("{}")
# Set host to network to trigger the TLS warning (not localhost)
settings_file.write_text(json.dumps({"host": "0.0.0.0"}))
monkeypatch.setattr(settings_mod, "SETTINGS_PATH", settings_file)
from muxplex.cli import doctor
@@ -1632,7 +1635,7 @@ def test_doctor_shows_tls_clipboard_warning(tmp_path, monkeypatch, capsys):
out = capsys.readouterr().out
out_lower = out.lower()
assert "clipboard" in out_lower or "https" in out_lower, (
f"Expected 'clipboard' or 'https' in doctor TLS-disabled output, got: {out!r}"
f"Expected 'clipboard' or 'https' in doctor TLS-disabled output for network host, got: {out!r}"
)
@@ -2005,3 +2008,48 @@ def test_setup_tls_regenerates_on_eof(tmp_path, monkeypatch, capsys):
assert "keeping" in out_lower, (
f"Expected 'keeping' in output after EOFError (default 'n'), got: {out!r}"
)
# ---------------------------------------------------------------------------
# task: TLS nudge hints in doctor and service install
# ---------------------------------------------------------------------------
def test_doctor_tls_nudge_shows_run_command_on_network_host(capsys, tmp_path, monkeypatch):
"""doctor must show 'Run: muxplex setup-tls' when host is network and TLS disabled."""
import json
import muxplex.settings as settings_mod
settings_file = tmp_path / "settings.json"
settings_file.write_text(json.dumps({"host": "0.0.0.0", "tls_cert": "", "tls_key": ""}))
monkeypatch.setattr(settings_mod, "SETTINGS_PATH", settings_file)
from muxplex.cli import doctor
doctor()
out = capsys.readouterr().out
assert "muxplex setup-tls" in out, (
f"Expected 'muxplex setup-tls' in doctor output when host is 0.0.0.0 and TLS disabled, got: {out!r}"
)
def test_doctor_tls_nudge_hidden_on_localhost(capsys, tmp_path, monkeypatch):
"""doctor must NOT show TLS nudge when host is 127.0.0.1."""
import json
import muxplex.settings as settings_mod
settings_file = tmp_path / "settings.json"
settings_file.write_text(json.dumps({"host": "127.0.0.1", "tls_cert": "", "tls_key": ""}))
monkeypatch.setattr(settings_mod, "SETTINGS_PATH", settings_file)
from muxplex.cli import doctor
doctor()
out = capsys.readouterr().out
assert "muxplex setup-tls" not in out, (
f"TLS nudge must NOT appear in doctor output when host is 127.0.0.1, got: {out!r}"
)
+77
View File
@@ -586,3 +586,80 @@ def test_launchd_logs_handles_keyboard_interrupt(monkeypatch):
monkeypatch.setattr(subprocess, "run", mock_run)
# Should not raise
svc._launchd_logs()
# ---------------------------------------------------------------------------
# task: TLS nudge hints in service install
# ---------------------------------------------------------------------------
def test_service_install_shows_tls_tip_on_network_host(capsys, tmp_path, monkeypatch):
"""service install must show TLS tip when host is network and TLS disabled."""
import json
import muxplex.service as svc
import muxplex.settings as settings_mod
# Setup paths
unit_dir = tmp_path / "systemd" / "user"
unit_path = unit_dir / "muxplex.service"
settings_file = tmp_path / "settings.json"
monkeypatch.setattr(svc, "_SYSTEMD_UNIT_DIR", unit_dir)
monkeypatch.setattr(svc, "_SYSTEMD_UNIT_PATH", unit_path)
monkeypatch.setattr(settings_mod, "SETTINGS_PATH", settings_file)
# Setup settings with network host and no TLS
settings_file.write_text(json.dumps({"host": "0.0.0.0", "tls_cert": "", "tls_key": ""}))
# Mock subprocess to avoid actual systemctl calls
calls = []
monkeypatch.setattr(subprocess, "run", lambda cmd, **kw: calls.append(list(cmd)))
# Mock the prompt function
monkeypatch.setattr(svc, "_prompt_host_if_localhost", lambda: None)
from muxplex.service import service_install
service_install()
out = capsys.readouterr().out
assert "muxplex setup-tls" in out, (
f"Expected 'muxplex setup-tls' in service install output when host is 0.0.0.0 and TLS disabled, got: {out!r}"
)
def test_service_install_hides_tls_tip_on_localhost(capsys, tmp_path, monkeypatch):
"""service install must NOT show TLS tip when host is 127.0.0.1."""
import json
import muxplex.service as svc
import muxplex.settings as settings_mod
# Setup paths
unit_dir = tmp_path / "systemd" / "user"
unit_path = unit_dir / "muxplex.service"
settings_file = tmp_path / "settings.json"
monkeypatch.setattr(svc, "_SYSTEMD_UNIT_DIR", unit_dir)
monkeypatch.setattr(svc, "_SYSTEMD_UNIT_PATH", unit_path)
monkeypatch.setattr(settings_mod, "SETTINGS_PATH", settings_file)
# Setup settings with localhost
settings_file.write_text(json.dumps({"host": "127.0.0.1", "tls_cert": "", "tls_key": ""}))
# Mock subprocess
calls = []
monkeypatch.setattr(subprocess, "run", lambda cmd, **kw: calls.append(list(cmd)))
# Mock the prompt function
monkeypatch.setattr(svc, "_prompt_host_if_localhost", lambda: None)
from muxplex.service import service_install
service_install()
out = capsys.readouterr().out
assert "muxplex setup-tls" not in out, (
f"TLS tip must NOT appear in service install output when host is 127.0.0.1, got: {out!r}"
)