From 8d3693df8c1b54232e85ecc0c57a16e7c60b57f2 Mon Sep 17 00:00:00 2001 From: oyasumi Date: Wed, 19 Aug 2026 19:00:14 +0000 Subject: [PATCH] Expose viewer host option --- README.md | 5 +++++ strix/interface/viewer/cli.py | 6 +++++- tests/test_viewer.py | 26 ++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e4dfa3e..a0668e79 100644 --- a/README.md +++ b/README.md @@ -167,10 +167,15 @@ strix view # ...or open a specific run by name strix view my-run-name + +# Expose the viewer on all IPv4 interfaces at a fixed port +strix view --host 0.0.0.0 --port 8080 --no-open ``` `strix view` starts a lightweight local server (bound to `127.0.0.1` on a random port) and opens your browser to a private, tokened link. Nothing leaves your machine: the dashboard reads the run's files straight off disk, with no cloud account or upload required. The UI ships prebuilt with Strix, so there is no extra install and no JS build step. +Use `--host 0.0.0.0` to make the viewer reachable from other machines. Replace `0.0.0.0` in the printed URL with the server's reachable IP or hostname. The token in that URL grants access to scan history and steering, so only share it with trusted users and restrict the port with your firewall. + ### What's in the dashboard - **Overview**: run status, target, and a severity breakdown of everything found so far. diff --git a/strix/interface/viewer/cli.py b/strix/interface/viewer/cli.py index efef1efd..eb64b8c8 100644 --- a/strix/interface/viewer/cli.py +++ b/strix/interface/viewer/cli.py @@ -45,7 +45,11 @@ def run_view(argv: list[str]) -> None: default=0, help="Port to serve on (default: an available ephemeral port).", ) - parser.add_argument("--host", default="127.0.0.1", help=argparse.SUPPRESS) + parser.add_argument( + "--host", + default="127.0.0.1", + help="Host to bind to (default: 127.0.0.1; use 0.0.0.0 for all IPv4 interfaces).", + ) parser.add_argument( "--no-open", action="store_true", diff --git a/tests/test_viewer.py b/tests/test_viewer.py index eb13cc1f..0191dfae 100644 --- a/tests/test_viewer.py +++ b/tests/test_viewer.py @@ -11,6 +11,7 @@ from typing import TYPE_CHECKING from urllib.parse import urlsplit from strix.core.paths import latest_run_dir, runs_base_dir +from strix.interface.viewer.cli import run_view from strix.interface.viewer.server import serve from strix.interface.viewer.transcript import ( build_run_state, @@ -48,6 +49,31 @@ def test_latest_run_dir_none_when_no_runs(tmp_path: Path, monkeypatch: pytest.Mo assert runs_base_dir() == tmp_path / "strix_runs" +def test_view_cli_help_includes_host(capsys: pytest.CaptureFixture[str]) -> None: + try: + run_view(["--help"]) + except SystemExit as exc: + assert exc.code == 0 + else: + raise AssertionError("--help should exit") + + help_text = capsys.readouterr().out + assert "--host HOST" in help_text + assert "0.0.0.0" in help_text + + +def test_server_can_bind_all_ipv4_interfaces(tmp_path: Path) -> None: + run_dir = _make_run(tmp_path, "remote", status="running", end_time=None) + + httpd, url, _ = serve(run_dir, host="0.0.0.0", open_browser=False) + try: + assert httpd.server_address[0] == "0.0.0.0" + assert url == f"http://0.0.0.0:{httpd.server_address[1]}" + finally: + httpd.shutdown() + httpd.server_close() + + def test_latest_run_dir_picks_newest_by_record_mtime( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: