From c5e57b51a56d268b64d951a7a263331e5622d9bb Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:26:52 +0700 Subject: [PATCH 01/25] R23 R2: add fail-closed Pro read-only tool profile --- continuityos/remote_mcp_server.py | 53 ++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/continuityos/remote_mcp_server.py b/continuityos/remote_mcp_server.py index 429e1f8..7ff5260 100644 --- a/continuityos/remote_mcp_server.py +++ b/continuityos/remote_mcp_server.py @@ -130,6 +130,21 @@ TOOLS = [*BASE_TOOLS, *REMOTE_TOOLS] +TOOL_PROFILE_FULL = "full" +TOOL_PROFILE_CHATGPT_PRO_READONLY = "chatgpt-pro-readonly" +_REMOTE_TOOL_NAMES = frozenset(tool["name"] for tool in REMOTE_TOOLS) +_TOOL_PROFILES = { + TOOL_PROFILE_FULL: None, + TOOL_PROFILE_CHATGPT_PRO_READONLY: _REMOTE_TOOL_NAMES, +} + + +def _tool_profile(value: str | None) -> str: + profile = (value or TOOL_PROFILE_FULL).strip().lower() + if profile not in _TOOL_PROFILES: + raise ValueError(f"unknown remote tool profile: {profile}") + return profile + def _env_enabled(value: str | None) -> bool: return (value or "").strip().lower() in {"1", "true", "yes", "on"} @@ -317,19 +332,37 @@ def __init__( *, remote_enabled: bool | None = None, remote_roots=None, + tool_profile: str = TOOL_PROFILE_FULL, ): super().__init__(db, policy_path, db_source) self.remote = RemoteSurface( enabled=remote_enabled, roots=remote_roots, ) + self.tool_profile = _tool_profile(tool_profile) + + @property + def tools(self) -> list[dict]: + allowed = _TOOL_PROFILES[self.tool_profile] + if allowed is None: + return list(TOOLS) + return [tool for tool in TOOLS if tool["name"] in allowed] + + def _require_tool_visible(self, name: str) -> None: + allowed = _TOOL_PROFILES[self.tool_profile] + if allowed is not None and name not in allowed: + raise PermissionError( + f"tool hidden by remote tool profile {self.tool_profile}: {name}" + ) def call(self, name, args): + self._require_tool_visible(name) if name == "capability_status": self.turns += 1 - return json.dumps( - self.remote.status(), ensure_ascii=False, indent=2 - ) + status = self.remote.status() + status["tool_profile"] = self.tool_profile + status["advertised_tools"] = [tool["name"] for tool in self.tools] + return json.dumps(status, ensure_ascii=False, indent=2) if name == "system_info": self.turns += 1 return json.dumps( @@ -372,6 +405,17 @@ def main() -> None: help="Path to one JSON policy, or YAML when PyYAML is installed", ) parser.add_argument("--enable-remote", action="store_true", default=None) + parser.add_argument( + "--tool-profile", + choices=[TOOL_PROFILE_FULL, TOOL_PROFILE_CHATGPT_PRO_READONLY], + default=os.environ.get( + "CONTINUITYOS_REMOTE_TOOL_PROFILE", TOOL_PROFILE_FULL + ), + help=( + "Advertised/callable tool surface. chatgpt-pro-readonly exposes " + "only bounded read-only Remote Commander tools." + ), + ) parser.add_argument( "--remote-root", action="append", @@ -384,6 +428,7 @@ def main() -> None: args.policy, remote_enabled=args.enable_remote, remote_roots=args.remote_root, + tool_profile=args.tool_profile, ) for line in sys.stdin: @@ -418,7 +463,7 @@ def main() -> None: { "jsonrpc": "2.0", "id": message_id, - "result": {"tools": TOOLS}, + "result": {"tools": server.tools}, } ) elif method == "tools/call": From 08e71e506d9fb389b7b4e75502d352d2eb016c82 Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:28:07 +0700 Subject: [PATCH 02/25] R23 R2: test Pro read-only MCP profile --- tests/test_remote_mcp_pro_profile.py | 69 ++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 tests/test_remote_mcp_pro_profile.py diff --git a/tests/test_remote_mcp_pro_profile.py b/tests/test_remote_mcp_pro_profile.py new file mode 100644 index 0000000..42b40be --- /dev/null +++ b/tests/test_remote_mcp_pro_profile.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +import json +from pathlib import Path + +import pytest + +from continuityos.remote_mcp_server import ( + TOOL_PROFILE_CHATGPT_PRO_READONLY, + RemoteServer, +) + + +def _server(tmp_path: Path, *, profile: str = TOOL_PROFILE_CHATGPT_PRO_READONLY): + return RemoteServer( + db=":memory:", + remote_enabled=True, + remote_roots=[tmp_path], + tool_profile=profile, + ) + + +def test_pro_profile_advertises_only_bounded_host_read_tools(tmp_path: Path): + server = _server(tmp_path) + assert [tool["name"] for tool in server.tools] == [ + "capability_status", + "system_info", + "fs_list", + "fs_read", + ] + + +@pytest.mark.parametrize( + "name,args", + [ + ("remember", {"text": "must not write"}), + ("upsert", {"text": "x", "key": "k"}), + ("forget", {"id": 1}), + ( + "preflight_exec", + {"request_id": "r1", "argv": ["echo", "x"], "cwd": "."}, + ), + ("execute_preflight", {"request_id": "r1"}), + ], +) +def test_pro_profile_rejects_hidden_write_or_execution_tools( + tmp_path: Path, name: str, args: dict +): + server = _server(tmp_path) + with pytest.raises(PermissionError, match="tool hidden by remote tool profile"): + server.call(name, args) + + +def test_capability_status_reports_effective_profile(tmp_path: Path): + server = _server(tmp_path) + value = json.loads(server.call("capability_status", {})) + assert value["tool_profile"] == TOOL_PROFILE_CHATGPT_PRO_READONLY + assert value["advertised_tools"] == [ + "capability_status", + "system_info", + "fs_list", + "fs_read", + ] + assert value["mutating_execution"]["direct_shell"] is False + + +def test_unknown_profile_fails_closed(tmp_path: Path): + with pytest.raises(ValueError, match="unknown remote tool profile"): + _server(tmp_path, profile="anything-goes") From e4913e30201cc86b0362b0bde4c4be7f167225d5 Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:28:20 +0700 Subject: [PATCH 03/25] R23 R2: add Windows Secure MCP Tunnel launcher --- scripts/windows/ContinuityOS-RemoteTunnel.ps1 | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 scripts/windows/ContinuityOS-RemoteTunnel.ps1 diff --git a/scripts/windows/ContinuityOS-RemoteTunnel.ps1 b/scripts/windows/ContinuityOS-RemoteTunnel.ps1 new file mode 100644 index 0000000..0403f35 --- /dev/null +++ b/scripts/windows/ContinuityOS-RemoteTunnel.ps1 @@ -0,0 +1,106 @@ +[CmdletBinding()] +param( + [ValidateSet("Plan", "Init", "Doctor", "Run")] + [string]$Mode = "Plan", + + [string]$TunnelId = $env:CONTROL_PLANE_TUNNEL_ID, + [string]$Profile = "continuityos-remote", + [string]$RemoteRoot = (Get-Location).Path, + [string]$TunnelClient = "tunnel-client", + [string]$Python = "python" +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" + +function Require-Command { + param([Parameter(Mandatory = $true)][string]$Name) + $resolved = Get-Command $Name -ErrorAction Stop + if (-not $resolved.Source) { + throw "Cannot resolve executable path for $Name" + } + return $resolved.Source +} + +function Require-ControlPlaneKey { + if ([string]::IsNullOrWhiteSpace($env:CONTROL_PLANE_API_KEY)) { + throw "CONTROL_PLANE_API_KEY must be supplied through the process environment." + } +} + +function Validate-TunnelId { + param([string]$Value) + if ([string]::IsNullOrWhiteSpace($Value)) { + throw "TunnelId is required for Init." + } + if ($Value.Length -gt 256 -or $Value -notmatch '^tunnel_[A-Za-z0-9_-]+$') { + throw "TunnelId has an invalid format." + } +} + +$pythonExe = Require-Command -Name $Python +$root = (Resolve-Path -LiteralPath $RemoteRoot).Path +if (-not (Test-Path -LiteralPath $root -PathType Container)) { + throw "RemoteRoot must be an existing directory." +} + +# The child MCP is deliberately fixed. No arbitrary shell text is accepted. +# The Pro profile is enforced again inside remote_mcp_server for both tools/list +# and tools/call; the tunnel is transport only. +$quotedPython = '"' + $pythonExe.Replace('"', '""') + '"' +$quotedRoot = '"' + $root.Replace('"', '""') + '"' +$mcpCommand = ( + $quotedPython + + " -m continuityos.remote_mcp_server" + + " --enable-remote" + + " --tool-profile chatgpt-pro-readonly" + + " --remote-root " + $quotedRoot +) + +$plan = [ordered]@{ + schema = "continuityos.remote_tunnel_plan/v1" + mode = $Mode + profile = $Profile + tunnel_id_present = -not [string]::IsNullOrWhiteSpace($TunnelId) + control_plane_key_present = -not [string]::IsNullOrWhiteSpace($env:CONTROL_PLANE_API_KEY) + remote_root = $root + python = $pythonExe + mcp_transport = "stdio" + mcp_tool_profile = "chatgpt-pro-readonly" + inbound_listener = $false + direct_shell = $false +} + +if ($Mode -eq "Plan") { + $plan | ConvertTo-Json -Depth 4 + exit 0 +} + +$tunnelExe = Require-Command -Name $TunnelClient +Require-ControlPlaneKey + +switch ($Mode) { + "Init" { + Validate-TunnelId -Value $TunnelId + & $tunnelExe init ` + --sample sample_mcp_stdio_local ` + --profile $Profile ` + --tunnel-id $TunnelId ` + --mcp-command $mcpCommand + if ($LASTEXITCODE -ne 0) { + throw "tunnel-client init failed with exit code $LASTEXITCODE" + } + } + "Doctor" { + & $tunnelExe doctor --profile $Profile --explain + if ($LASTEXITCODE -ne 0) { + throw "tunnel-client doctor failed with exit code $LASTEXITCODE" + } + } + "Run" { + & $tunnelExe run --profile $Profile + if ($LASTEXITCODE -ne 0) { + throw "tunnel-client run failed with exit code $LASTEXITCODE" + } + } +} From eefc08b1932b7929235d4df236054f0b150b16fe Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:28:37 +0700 Subject: [PATCH 04/25] R23 R2: test Windows tunnel launcher boundary --- tests/test_remote_tunnel_windows_script.py | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tests/test_remote_tunnel_windows_script.py diff --git a/tests/test_remote_tunnel_windows_script.py b/tests/test_remote_tunnel_windows_script.py new file mode 100644 index 0000000..d92c794 --- /dev/null +++ b/tests/test_remote_tunnel_windows_script.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +from pathlib import Path + + +SCRIPT = ( + Path(__file__).resolve().parents[1] + / "scripts" + / "windows" + / "ContinuityOS-RemoteTunnel.ps1" +) + + +def test_windows_tunnel_launcher_keeps_key_out_of_command_line(): + source = SCRIPT.read_text(encoding="utf-8") + assert "CONTROL_PLANE_API_KEY" in source + assert "--api-key" not in source + assert "--token" not in source + assert "Set-Content" not in source + assert "Add-Content" not in source + + +def test_windows_tunnel_launcher_uses_stdio_and_pro_readonly_profile(): + source = SCRIPT.read_text(encoding="utf-8") + assert "sample_mcp_stdio_local" in source + assert "-m continuityos.remote_mcp_server" in source + assert "--enable-remote" in source + assert "--tool-profile chatgpt-pro-readonly" in source + assert 'mcp_transport = "stdio"' in source + assert "inbound_listener = $false" in source + + +def test_windows_tunnel_launcher_accepts_no_arbitrary_mcp_command(): + source = SCRIPT.read_text(encoding="utf-8") + assert "[string]$McpCommand" not in source + assert "[string]$Command" not in source + assert "The child MCP is deliberately fixed" in source From e803f5761f3fef08588548eb74e2874f65c07e93 Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:28:51 +0700 Subject: [PATCH 05/25] R23 R2: document Secure MCP Tunnel boundary --- docs/REMOTE_COMMANDER_R2_TUNNEL.md | 96 ++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 docs/REMOTE_COMMANDER_R2_TUNNEL.md diff --git a/docs/REMOTE_COMMANDER_R2_TUNNEL.md b/docs/REMOTE_COMMANDER_R2_TUNNEL.md new file mode 100644 index 0000000..f4dfbdd --- /dev/null +++ b/docs/REMOTE_COMMANDER_R2_TUNNEL.md @@ -0,0 +1,96 @@ +# ContinuityOS Remote Commander R2 — Secure MCP Tunnel + +## Baseline + +R2 is a dependent change based on R1 head +`84cf11c679f63db853cf3aa14996a811d01d1bc2`. +R1 itself is based on current `master` +`2c701b2463f62f8e43374a8f40cdb289d0bc1bad`. + +R2 does not require ChatGPT Pro to build or validate locally. + +## Purpose + +R2 connects the private stdio Remote Commander MCP to OpenAI Secure MCP Tunnel +without exposing an inbound port. + +Data path: + +`ChatGPT/OpenAI -> OpenAI tunnel control plane <- outbound HTTPS tunnel-client -> local stdio ContinuityOS Remote MCP` + +The tunnel is transport only. ContinuityOS remains the authority boundary. + +Official implementation reference: +`https://github.com/openai/tunnel-client`. + +## Pro read-only profile + +When launched with: + +`--tool-profile chatgpt-pro-readonly` + +the server advertises and accepts only: + +- `capability_status` +- `system_info` +- `fs_list` +- `fs_read` + +The restriction is enforced twice: + +1. `tools/list` omits every base memory/write/execution tool. +2. `tools/call` rejects hidden tool names even if a client attempts a direct call. + +This means `remember`, `upsert`, `forget`, `preflight_exec`, and +`execute_preflight` are unavailable on this profile. + +## Windows launcher + +Use: + +`scripts/windows/ContinuityOS-RemoteTunnel.ps1` + +Safe pre-Pro validation: + +```powershell +.\scripts\windows\ContinuityOS-RemoteTunnel.ps1 -Mode Plan -RemoteRoot C:\path\to\allowed\root +``` + +After OpenAI tunnel credentials exist, set them only in the current process +environment. Do not commit them or place them in the MCP command: + +```powershell +$env:CONTROL_PLANE_API_KEY = "" +$env:CONTROL_PLANE_TUNNEL_ID = "" + +.\scripts\windows\ContinuityOS-RemoteTunnel.ps1 -Mode Init -RemoteRoot C:\path\to\allowed\root +.\scripts\windows\ContinuityOS-RemoteTunnel.ps1 -Mode Doctor +.\scripts\windows\ContinuityOS-RemoteTunnel.ps1 -Mode Run +``` + +The launcher passes no API key or bearer token on the command line. The official +`tunnel-client` inherits the runtime key from the environment. + +## Security invariants + +- no inbound listener is created by ContinuityOS; +- no public MCP endpoint is required; +- no arbitrary `--mcp-command` input is accepted by the launcher; +- the MCP child command is fixed to `continuityos.remote_mcp_server`; +- the remote root must already exist; +- credential/key paths remain denied by R1; +- Pro tunnel surface is read-only at the server, not merely in the ChatGPT UI; +- R2 does not merge, deploy, trade, touch wallets, or grant capital authority. + +## Qualification + +R2 is code-complete only when: + +1. R1 review-gates are green on its synchronized head; +2. R2 unit/static tests pass on Linux and Windows CI; +3. R2 CodeQL and P0 checks are green; +4. a later local Windows test proves `Plan` without credentials; +5. once tunnel credentials are available, `Init -> Doctor -> Run` is tested with + the official OpenAI tunnel-client. + +The last step is runtime qualification and cannot be claimed from CI alone. From d2d71700e430d0b4e7384cfe6f347daf769286a3 Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:29:54 +0700 Subject: [PATCH 06/25] R23 R2: annotate remote tools as read-only --- continuityos/remote_mcp_server.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/continuityos/remote_mcp_server.py b/continuityos/remote_mcp_server.py index 7ff5260..9bb0f37 100644 --- a/continuityos/remote_mcp_server.py +++ b/continuityos/remote_mcp_server.py @@ -67,6 +67,12 @@ "Report the ContinuityOS Remote Commander capability boundary: " "enabled state, allowed roots, read limits, and governed execution path." ), + "annotations": { + "readOnlyHint": True, + "destructiveHint": False, + "idempotentHint": True, + "openWorldHint": False, + }, "inputSchema": { "type": "object", "additionalProperties": False, @@ -79,6 +85,12 @@ "Read-only host identity and runtime information. Does not return environment " "variables, credentials, or process contents." ), + "annotations": { + "readOnlyHint": True, + "destructiveHint": False, + "idempotentHint": True, + "openWorldHint": False, + }, "inputSchema": { "type": "object", "additionalProperties": False, @@ -91,6 +103,12 @@ "List one directory inside an explicitly allowed remote root. Sensitive files " "and credential directories are omitted. Read-only." ), + "annotations": { + "readOnlyHint": True, + "destructiveHint": False, + "idempotentHint": True, + "openWorldHint": False, + }, "inputSchema": { "type": "object", "additionalProperties": False, @@ -111,6 +129,12 @@ "Read a bounded UTF-8 text file inside an explicitly allowed remote root. " "Known credential/key paths are denied. Read-only." ), + "annotations": { + "readOnlyHint": True, + "destructiveHint": False, + "idempotentHint": True, + "openWorldHint": False, + }, "inputSchema": { "type": "object", "additionalProperties": False, From f6148089f6555105df02e3aea5d7f17e384adab2 Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:30:02 +0700 Subject: [PATCH 07/25] R23 R2: test read-only MCP annotations --- tests/test_remote_mcp_pro_profile.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_remote_mcp_pro_profile.py b/tests/test_remote_mcp_pro_profile.py index 42b40be..3a47f16 100644 --- a/tests/test_remote_mcp_pro_profile.py +++ b/tests/test_remote_mcp_pro_profile.py @@ -30,6 +30,16 @@ def test_pro_profile_advertises_only_bounded_host_read_tools(tmp_path: Path): ] +def test_pro_profile_tools_are_annotated_read_only(tmp_path: Path): + server = _server(tmp_path) + assert server.tools + for tool in server.tools: + annotations = tool["annotations"] + assert annotations["readOnlyHint"] is True + assert annotations["destructiveHint"] is False + assert annotations["idempotentHint"] is True + assert annotations["openWorldHint"] is False + @pytest.mark.parametrize( "name,args", [ From d6598728127969e42974816d688ebd0cbb59ee16 Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:30:24 +0700 Subject: [PATCH 08/25] R23 R2: align capability status with Pro profile --- continuityos/remote_mcp_server.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/continuityos/remote_mcp_server.py b/continuityos/remote_mcp_server.py index 9bb0f37..c326520 100644 --- a/continuityos/remote_mcp_server.py +++ b/continuityos/remote_mcp_server.py @@ -386,6 +386,15 @@ def call(self, name, args): status = self.remote.status() status["tool_profile"] = self.tool_profile status["advertised_tools"] = [tool["name"] for tool in self.tools] + if self.tool_profile == TOOL_PROFILE_CHATGPT_PRO_READONLY: + status["mode"] = "read_only_host_surface" + status["mutating_execution"] = { + "available": False, + "direct_shell": False, + "reason": "hidden_by_tool_profile", + } + else: + status["mutating_execution"]["available"] = True return json.dumps(status, ensure_ascii=False, indent=2) if name == "system_info": self.turns += 1 From 54aa989615339b1837f278dc951a7cba2931f197 Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:30:33 +0700 Subject: [PATCH 09/25] R23 R2: test truthful Pro capability status --- tests/test_remote_mcp_pro_profile.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_remote_mcp_pro_profile.py b/tests/test_remote_mcp_pro_profile.py index 3a47f16..e691c9f 100644 --- a/tests/test_remote_mcp_pro_profile.py +++ b/tests/test_remote_mcp_pro_profile.py @@ -71,7 +71,12 @@ def test_capability_status_reports_effective_profile(tmp_path: Path): "fs_list", "fs_read", ] - assert value["mutating_execution"]["direct_shell"] is False + assert value["mode"] == "read_only_host_surface" + assert value["mutating_execution"] == { + "available": False, + "direct_shell": False, + "reason": "hidden_by_tool_profile", + } def test_unknown_profile_fails_closed(tmp_path: Path): From 77e470b742e6c5c2a1cd04515549bf7ec027ac54 Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:31:04 +0700 Subject: [PATCH 10/25] R23 R2: bound tunnel profile identifier --- scripts/windows/ContinuityOS-RemoteTunnel.ps1 | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/scripts/windows/ContinuityOS-RemoteTunnel.ps1 b/scripts/windows/ContinuityOS-RemoteTunnel.ps1 index 0403f35..c4b00f9 100644 --- a/scripts/windows/ContinuityOS-RemoteTunnel.ps1 +++ b/scripts/windows/ContinuityOS-RemoteTunnel.ps1 @@ -38,6 +38,80 @@ function Validate-TunnelId { } } +if ( + [string]::IsNullOrWhiteSpace($Profile) -or + $Profile.Length -gt 64 -or + $Profile -notmatch '^[A-Za-z0-9][A-Za-z0-9._-]* +$root = (Resolve-Path -LiteralPath $RemoteRoot).Path +if (-not (Test-Path -LiteralPath $root -PathType Container)) { + throw "RemoteRoot must be an existing directory." +} + +# The child MCP is deliberately fixed. No arbitrary shell text is accepted. +# The Pro profile is enforced again inside remote_mcp_server for both tools/list +# and tools/call; the tunnel is transport only. +$quotedPython = '"' + $pythonExe.Replace('"', '""') + '"' +$quotedRoot = '"' + $root.Replace('"', '""') + '"' +$mcpCommand = ( + $quotedPython + + " -m continuityos.remote_mcp_server" + + " --enable-remote" + + " --tool-profile chatgpt-pro-readonly" + + " --remote-root " + $quotedRoot +) + +$plan = [ordered]@{ + schema = "continuityos.remote_tunnel_plan/v1" + mode = $Mode + profile = $Profile + tunnel_id_present = -not [string]::IsNullOrWhiteSpace($TunnelId) + control_plane_key_present = -not [string]::IsNullOrWhiteSpace($env:CONTROL_PLANE_API_KEY) + remote_root = $root + python = $pythonExe + mcp_transport = "stdio" + mcp_tool_profile = "chatgpt-pro-readonly" + inbound_listener = $false + direct_shell = $false +} + +if ($Mode -eq "Plan") { + $plan | ConvertTo-Json -Depth 4 + exit 0 +} + +$tunnelExe = Require-Command -Name $TunnelClient +Require-ControlPlaneKey + +switch ($Mode) { + "Init" { + Validate-TunnelId -Value $TunnelId + & $tunnelExe init ` + --sample sample_mcp_stdio_local ` + --profile $Profile ` + --tunnel-id $TunnelId ` + --mcp-command $mcpCommand + if ($LASTEXITCODE -ne 0) { + throw "tunnel-client init failed with exit code $LASTEXITCODE" + } + } + "Doctor" { + & $tunnelExe doctor --profile $Profile --explain + if ($LASTEXITCODE -ne 0) { + throw "tunnel-client doctor failed with exit code $LASTEXITCODE" + } + } + "Run" { + & $tunnelExe run --profile $Profile + if ($LASTEXITCODE -ne 0) { + throw "tunnel-client run failed with exit code $LASTEXITCODE" + } + } +} + +) { + throw "Profile has an invalid format." +} + $pythonExe = Require-Command -Name $Python $root = (Resolve-Path -LiteralPath $RemoteRoot).Path if (-not (Test-Path -LiteralPath $root -PathType Container)) { From ddfd605c8ef3c83c4d32376db189fd7f12a54940 Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:31:14 +0700 Subject: [PATCH 11/25] R23 R2: test tunnel profile validation --- tests/test_remote_tunnel_windows_script.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_remote_tunnel_windows_script.py b/tests/test_remote_tunnel_windows_script.py index d92c794..523e40b 100644 --- a/tests/test_remote_tunnel_windows_script.py +++ b/tests/test_remote_tunnel_windows_script.py @@ -35,3 +35,9 @@ def test_windows_tunnel_launcher_accepts_no_arbitrary_mcp_command(): assert "[string]$McpCommand" not in source assert "[string]$Command" not in source assert "The child MCP is deliberately fixed" in source + + +def test_windows_tunnel_launcher_bounds_profile_identifier(): + source = SCRIPT.read_text(encoding="utf-8") + assert "Profile has an invalid format." in source + assert "^[A-Za-z0-9][A-Za-z0-9._-]*$" in source From a8e1e25eec2488301e0f5ddb757ccf32a2de0f56 Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:31:45 +0700 Subject: [PATCH 12/25] R23 R2: execute credential-free Windows tunnel plan in CI --- tests/test_remote_tunnel_windows_script.py | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/test_remote_tunnel_windows_script.py b/tests/test_remote_tunnel_windows_script.py index 523e40b..44b94cf 100644 --- a/tests/test_remote_tunnel_windows_script.py +++ b/tests/test_remote_tunnel_windows_script.py @@ -1,6 +1,11 @@ from __future__ import annotations +import json +import os from pathlib import Path +import subprocess + +import pytest SCRIPT = ( @@ -41,3 +46,36 @@ def test_windows_tunnel_launcher_bounds_profile_identifier(): source = SCRIPT.read_text(encoding="utf-8") assert "Profile has an invalid format." in source assert "^[A-Za-z0-9][A-Za-z0-9._-]*$" in source + + +@pytest.mark.skipif(os.name != "nt", reason="Windows PowerShell smoke test") +def test_windows_tunnel_launcher_plan_executes_without_credentials(tmp_path: Path): + env = os.environ.copy() + env.pop("CONTROL_PLANE_API_KEY", None) + env.pop("CONTROL_PLANE_TUNNEL_ID", None) + completed = subprocess.run( + [ + "powershell", + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + str(SCRIPT), + "-Mode", + "Plan", + "-RemoteRoot", + str(tmp_path), + ], + check=False, + capture_output=True, + text=True, + env=env, + timeout=30, + ) + assert completed.returncode == 0, completed.stderr + plan = json.loads(completed.stdout) + assert plan["mcp_transport"] == "stdio" + assert plan["mcp_tool_profile"] == "chatgpt-pro-readonly" + assert plan["inbound_listener"] is False + assert plan["direct_shell"] is False + assert plan["control_plane_key_present"] is False From 2c853048ebab44c7f11c5448ee15ec66d00a617b Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:33:25 +0700 Subject: [PATCH 13/25] R23 R2: prove Pro read-only boundary over stdio JSON-RPC --- tests/test_remote_mcp_pro_stdio.py | 65 ++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 tests/test_remote_mcp_pro_stdio.py diff --git a/tests/test_remote_mcp_pro_stdio.py b/tests/test_remote_mcp_pro_stdio.py new file mode 100644 index 0000000..dfaf01b --- /dev/null +++ b/tests/test_remote_mcp_pro_stdio.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +import json +from pathlib import Path +import subprocess +import sys + + +def test_pro_readonly_stdio_protocol_hides_and_rejects_write_tools(tmp_path: Path): + requests = [ + {"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {}}, + {"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}}, + { + "jsonrpc": "2.0", + "id": 3, + "method": "tools/call", + "params": {"name": "remember", "arguments": {"text": "no"}}, + }, + { + "jsonrpc": "2.0", + "id": 4, + "method": "tools/call", + "params": {"name": "capability_status", "arguments": {}}, + }, + ] + payload = "".join(json.dumps(item) + "\n" for item in requests) + completed = subprocess.run( + [ + sys.executable, + "-m", + "continuityos.remote_mcp_server", + "--db", + ":memory:", + "--enable-remote", + "--tool-profile", + "chatgpt-pro-readonly", + "--remote-root", + str(tmp_path), + ], + input=payload, + capture_output=True, + text=True, + check=False, + timeout=30, + ) + assert completed.returncode == 0, completed.stderr + responses = {item["id"]: item for item in map(json.loads, completed.stdout.splitlines())} + + tools = responses[2]["result"]["tools"] + assert [tool["name"] for tool in tools] == [ + "capability_status", + "system_info", + "fs_list", + "fs_read", + ] + assert all(tool["annotations"]["readOnlyHint"] is True for tool in tools) + + denied = responses[3]["result"] + assert denied["isError"] is True + assert "tool hidden by remote tool profile" in denied["content"][0]["text"] + + status_text = responses[4]["result"]["content"][0]["text"] + status = json.loads(status_text) + assert status["tool_profile"] == "chatgpt-pro-readonly" + assert status["mutating_execution"]["available"] is False From d31af26d737c82cf8b3edffb7495488dbd2189aa Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:38:23 +0700 Subject: [PATCH 14/25] CI: preserve exact wheel with SHA receipt before review tests --- tools/ci_review.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tools/ci_review.py b/tools/ci_review.py index 4fe98f3..dcd4fc8 100644 --- a/tools/ci_review.py +++ b/tools/ci_review.py @@ -549,6 +549,45 @@ def wheel_test(args: argparse.Namespace) -> int: return 0 if passed else 1 + +def preserve_wheel(args: argparse.Namespace) -> int: + wheel_dir = Path(args.wheel_dir).resolve() + artifact_dir = Path(args.artifact_dir).resolve() + output = Path(args.output).resolve() + wheels = sorted(wheel_dir.glob("*.whl")) + if len(wheels) != 1: + raise SystemExit( + f"expected exactly one wheel in {wheel_dir}, found {len(wheels)}" + ) + if artifact_dir == wheel_dir: + raise SystemExit("wheel artifact directory must be distinct from wheel directory") + if artifact_dir.exists() and any(artifact_dir.iterdir()): + raise SystemExit(f"wheel artifact directory is not empty: {artifact_dir}") + artifact_dir.mkdir(parents=True, exist_ok=True) + + source = wheels[0] + destination = artifact_dir / source.name + source_sha256 = _sha256_file(source) + shutil.copy2(source, destination) + destination_sha256 = _sha256_file(destination) + if source_sha256 != destination_sha256: + destination.unlink(missing_ok=True) + raise SystemExit("preserved wheel SHA-256 mismatch") + + payload = { + "schema": "continuityos-preserved-wheel-receipt-v1", + "filename": source.name, + "size": source.stat().st_size, + "sha256": source_sha256, + "source_directory_sha256": _normalized_path_sha256(wheel_dir), + "artifact_directory_sha256": _normalized_path_sha256(artifact_dir), + "status": "PASS", + } + _write_json(output, payload) + print(json.dumps(payload, ensure_ascii=False, sort_keys=True)) + return 0 + + def _workflow_step_block(text: str, name: str) -> str: match = re.search( rf"(?ms)^\s*- name: {re.escape(name)}\s*$.*?(?=^\s*- name: |\Z)", @@ -668,6 +707,12 @@ def build_parser() -> argparse.ArgumentParser: wheel.add_argument("--output", required=True) wheel.set_defaults(func=wheel_test) + preserve = commands.add_parser("preserve-wheel") + preserve.add_argument("--wheel-dir", required=True) + preserve.add_argument("--artifact-dir", required=True) + preserve.add_argument("--output", required=True) + preserve.set_defaults(func=preserve_wheel) + policy = commands.add_parser("validate-workflow") policy.add_argument("--workflow", required=True) policy.add_argument("--output", required=True) From 8df1121a4745f208c77686b803f24ffea20edb99 Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:40:47 +0700 Subject: [PATCH 15/25] R23 R2: repair PowerShell profile validation block --- scripts/windows/ContinuityOS-RemoteTunnel.ps1 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/windows/ContinuityOS-RemoteTunnel.ps1 b/scripts/windows/ContinuityOS-RemoteTunnel.ps1 index c4b00f9..0f0ce23 100644 --- a/scripts/windows/ContinuityOS-RemoteTunnel.ps1 +++ b/scripts/windows/ContinuityOS-RemoteTunnel.ps1 @@ -41,7 +41,12 @@ function Validate-TunnelId { if ( [string]::IsNullOrWhiteSpace($Profile) -or $Profile.Length -gt 64 -or - $Profile -notmatch '^[A-Za-z0-9][A-Za-z0-9._-]* + $Profile -notmatch '^[A-Za-z0-9][A-Za-z0-9._-]*$' +) { + throw "Profile has an invalid format." +} + +$pythonExe = Require-Command -Name $Python $root = (Resolve-Path -LiteralPath $RemoteRoot).Path if (-not (Test-Path -LiteralPath $root -PathType Container)) { throw "RemoteRoot must be an existing directory." From 6b637b74b224c304c785953057c289d463a723d6 Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:41:04 +0700 Subject: [PATCH 16/25] R23 R2: drop unused wheel preservation experiment --- tools/ci_review.py | 45 --------------------------------------------- 1 file changed, 45 deletions(-) diff --git a/tools/ci_review.py b/tools/ci_review.py index dcd4fc8..4fe98f3 100644 --- a/tools/ci_review.py +++ b/tools/ci_review.py @@ -549,45 +549,6 @@ def wheel_test(args: argparse.Namespace) -> int: return 0 if passed else 1 - -def preserve_wheel(args: argparse.Namespace) -> int: - wheel_dir = Path(args.wheel_dir).resolve() - artifact_dir = Path(args.artifact_dir).resolve() - output = Path(args.output).resolve() - wheels = sorted(wheel_dir.glob("*.whl")) - if len(wheels) != 1: - raise SystemExit( - f"expected exactly one wheel in {wheel_dir}, found {len(wheels)}" - ) - if artifact_dir == wheel_dir: - raise SystemExit("wheel artifact directory must be distinct from wheel directory") - if artifact_dir.exists() and any(artifact_dir.iterdir()): - raise SystemExit(f"wheel artifact directory is not empty: {artifact_dir}") - artifact_dir.mkdir(parents=True, exist_ok=True) - - source = wheels[0] - destination = artifact_dir / source.name - source_sha256 = _sha256_file(source) - shutil.copy2(source, destination) - destination_sha256 = _sha256_file(destination) - if source_sha256 != destination_sha256: - destination.unlink(missing_ok=True) - raise SystemExit("preserved wheel SHA-256 mismatch") - - payload = { - "schema": "continuityos-preserved-wheel-receipt-v1", - "filename": source.name, - "size": source.stat().st_size, - "sha256": source_sha256, - "source_directory_sha256": _normalized_path_sha256(wheel_dir), - "artifact_directory_sha256": _normalized_path_sha256(artifact_dir), - "status": "PASS", - } - _write_json(output, payload) - print(json.dumps(payload, ensure_ascii=False, sort_keys=True)) - return 0 - - def _workflow_step_block(text: str, name: str) -> str: match = re.search( rf"(?ms)^\s*- name: {re.escape(name)}\s*$.*?(?=^\s*- name: |\Z)", @@ -707,12 +668,6 @@ def build_parser() -> argparse.ArgumentParser: wheel.add_argument("--output", required=True) wheel.set_defaults(func=wheel_test) - preserve = commands.add_parser("preserve-wheel") - preserve.add_argument("--wheel-dir", required=True) - preserve.add_argument("--artifact-dir", required=True) - preserve.add_argument("--output", required=True) - preserve.set_defaults(func=preserve_wheel) - policy = commands.add_parser("validate-workflow") policy.add_argument("--workflow", required=True) policy.add_argument("--output", required=True) From 47229b5aa2289652ee9603ffcfd83cea425ca549 Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:41:39 +0700 Subject: [PATCH 17/25] R23 R2: canonicalize Windows tunnel launcher --- scripts/windows/ContinuityOS-RemoteTunnel.ps1 | 71 ------------------- 1 file changed, 71 deletions(-) diff --git a/scripts/windows/ContinuityOS-RemoteTunnel.ps1 b/scripts/windows/ContinuityOS-RemoteTunnel.ps1 index 0f0ce23..b551e78 100644 --- a/scripts/windows/ContinuityOS-RemoteTunnel.ps1 +++ b/scripts/windows/ContinuityOS-RemoteTunnel.ps1 @@ -112,74 +112,3 @@ switch ($Mode) { } } } - -) { - throw "Profile has an invalid format." -} - -$pythonExe = Require-Command -Name $Python -$root = (Resolve-Path -LiteralPath $RemoteRoot).Path -if (-not (Test-Path -LiteralPath $root -PathType Container)) { - throw "RemoteRoot must be an existing directory." -} - -# The child MCP is deliberately fixed. No arbitrary shell text is accepted. -# The Pro profile is enforced again inside remote_mcp_server for both tools/list -# and tools/call; the tunnel is transport only. -$quotedPython = '"' + $pythonExe.Replace('"', '""') + '"' -$quotedRoot = '"' + $root.Replace('"', '""') + '"' -$mcpCommand = ( - $quotedPython + - " -m continuityos.remote_mcp_server" + - " --enable-remote" + - " --tool-profile chatgpt-pro-readonly" + - " --remote-root " + $quotedRoot -) - -$plan = [ordered]@{ - schema = "continuityos.remote_tunnel_plan/v1" - mode = $Mode - profile = $Profile - tunnel_id_present = -not [string]::IsNullOrWhiteSpace($TunnelId) - control_plane_key_present = -not [string]::IsNullOrWhiteSpace($env:CONTROL_PLANE_API_KEY) - remote_root = $root - python = $pythonExe - mcp_transport = "stdio" - mcp_tool_profile = "chatgpt-pro-readonly" - inbound_listener = $false - direct_shell = $false -} - -if ($Mode -eq "Plan") { - $plan | ConvertTo-Json -Depth 4 - exit 0 -} - -$tunnelExe = Require-Command -Name $TunnelClient -Require-ControlPlaneKey - -switch ($Mode) { - "Init" { - Validate-TunnelId -Value $TunnelId - & $tunnelExe init ` - --sample sample_mcp_stdio_local ` - --profile $Profile ` - --tunnel-id $TunnelId ` - --mcp-command $mcpCommand - if ($LASTEXITCODE -ne 0) { - throw "tunnel-client init failed with exit code $LASTEXITCODE" - } - } - "Doctor" { - & $tunnelExe doctor --profile $Profile --explain - if ($LASTEXITCODE -ne 0) { - throw "tunnel-client doctor failed with exit code $LASTEXITCODE" - } - } - "Run" { - & $tunnelExe run --profile $Profile - if ($LASTEXITCODE -ne 0) { - throw "tunnel-client run failed with exit code $LASTEXITCODE" - } - } -} From 4039cfa790ab9754cc7a8131e673b03f96bb458f Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:42:07 +0700 Subject: [PATCH 18/25] R23 R2: bind tunnel id to official format --- scripts/windows/ContinuityOS-RemoteTunnel.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/windows/ContinuityOS-RemoteTunnel.ps1 b/scripts/windows/ContinuityOS-RemoteTunnel.ps1 index b551e78..f3766bf 100644 --- a/scripts/windows/ContinuityOS-RemoteTunnel.ps1 +++ b/scripts/windows/ContinuityOS-RemoteTunnel.ps1 @@ -33,7 +33,7 @@ function Validate-TunnelId { if ([string]::IsNullOrWhiteSpace($Value)) { throw "TunnelId is required for Init." } - if ($Value.Length -gt 256 -or $Value -notmatch '^tunnel_[A-Za-z0-9_-]+$') { + if ($Value -notmatch '^tunnel_[0-9a-f]{32}$') { throw "TunnelId has an invalid format." } } From 81c57bef4bde0c79e9dce6c0bcbb8f33a41dd63e Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:42:20 +0700 Subject: [PATCH 19/25] R23 R2: test official tunnel id contract --- tests/test_remote_tunnel_windows_script.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_remote_tunnel_windows_script.py b/tests/test_remote_tunnel_windows_script.py index 44b94cf..c273ce4 100644 --- a/tests/test_remote_tunnel_windows_script.py +++ b/tests/test_remote_tunnel_windows_script.py @@ -79,3 +79,9 @@ def test_windows_tunnel_launcher_plan_executes_without_credentials(tmp_path: Pat assert plan["inbound_listener"] is False assert plan["direct_shell"] is False assert plan["control_plane_key_present"] is False + + +def test_windows_tunnel_launcher_binds_official_tunnel_id_format(): + source = SCRIPT.read_text(encoding="utf-8") + assert "^tunnel_[0-9a-f]{32}$" in source + assert "TunnelId has an invalid format." in source From 3d3e095b1afb27faf784bb0097bbbaacc2c54331 Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:42:25 +0700 Subject: [PATCH 20/25] R23 R2: document tunnel id and runtime permission contract --- docs/REMOTE_COMMANDER_R2_TUNNEL.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/REMOTE_COMMANDER_R2_TUNNEL.md b/docs/REMOTE_COMMANDER_R2_TUNNEL.md index f4dfbdd..46eb9c8 100644 --- a/docs/REMOTE_COMMANDER_R2_TUNNEL.md +++ b/docs/REMOTE_COMMANDER_R2_TUNNEL.md @@ -71,6 +71,9 @@ $env:CONTROL_PLANE_TUNNEL_ID = "" The launcher passes no API key or bearer token on the command line. The official `tunnel-client` inherits the runtime key from the environment. +`CONTROL_PLANE_TUNNEL_ID` must match `tunnel_` plus exactly 32 lowercase hex +characters. The runtime API key should be restricted to Tunnels Read + Use; + ## Security invariants - no inbound listener is created by ContinuityOS; From a91bc3ffaab3ec137a1f90c08bb07e04706cc14b Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:46:46 +0700 Subject: [PATCH 21/25] R23 R2: scope launcher tests to source-tree qualification --- tests/test_remote_tunnel_windows_script.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/test_remote_tunnel_windows_script.py b/tests/test_remote_tunnel_windows_script.py index c273ce4..8ef911d 100644 --- a/tests/test_remote_tunnel_windows_script.py +++ b/tests/test_remote_tunnel_windows_script.py @@ -16,8 +16,14 @@ ) +def _source() -> str: + if not SCRIPT.is_file(): + pytest.skip("source-tree-only Windows tunnel launcher is not shipped in the wheel") + return SCRIPT.read_text(encoding="utf-8") + + def test_windows_tunnel_launcher_keeps_key_out_of_command_line(): - source = SCRIPT.read_text(encoding="utf-8") + source = _source() assert "CONTROL_PLANE_API_KEY" in source assert "--api-key" not in source assert "--token" not in source @@ -26,7 +32,7 @@ def test_windows_tunnel_launcher_keeps_key_out_of_command_line(): def test_windows_tunnel_launcher_uses_stdio_and_pro_readonly_profile(): - source = SCRIPT.read_text(encoding="utf-8") + source = _source() assert "sample_mcp_stdio_local" in source assert "-m continuityos.remote_mcp_server" in source assert "--enable-remote" in source @@ -36,20 +42,22 @@ def test_windows_tunnel_launcher_uses_stdio_and_pro_readonly_profile(): def test_windows_tunnel_launcher_accepts_no_arbitrary_mcp_command(): - source = SCRIPT.read_text(encoding="utf-8") + source = _source() assert "[string]$McpCommand" not in source assert "[string]$Command" not in source assert "The child MCP is deliberately fixed" in source def test_windows_tunnel_launcher_bounds_profile_identifier(): - source = SCRIPT.read_text(encoding="utf-8") + source = _source() assert "Profile has an invalid format." in source assert "^[A-Za-z0-9][A-Za-z0-9._-]*$" in source @pytest.mark.skipif(os.name != "nt", reason="Windows PowerShell smoke test") def test_windows_tunnel_launcher_plan_executes_without_credentials(tmp_path: Path): + if not SCRIPT.is_file(): + pytest.skip("source-tree-only Windows tunnel launcher is not shipped in the wheel") env = os.environ.copy() env.pop("CONTROL_PLANE_API_KEY", None) env.pop("CONTROL_PLANE_TUNNEL_ID", None) @@ -82,6 +90,6 @@ def test_windows_tunnel_launcher_plan_executes_without_credentials(tmp_path: Pat def test_windows_tunnel_launcher_binds_official_tunnel_id_format(): - source = SCRIPT.read_text(encoding="utf-8") + source = _source() assert "^tunnel_[0-9a-f]{32}$" in source assert "TunnelId has an invalid format." in source From 002aa9f0a5a7b652eb4e5133b2e84ff9fb86a754 Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 19:02:13 +0700 Subject: [PATCH 22/25] R23 R2: pin tunnel health listener to loopback --- scripts/windows/ContinuityOS-RemoteTunnel.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/windows/ContinuityOS-RemoteTunnel.ps1 b/scripts/windows/ContinuityOS-RemoteTunnel.ps1 index f3766bf..71c0f0e 100644 --- a/scripts/windows/ContinuityOS-RemoteTunnel.ps1 +++ b/scripts/windows/ContinuityOS-RemoteTunnel.ps1 @@ -75,7 +75,9 @@ $plan = [ordered]@{ python = $pythonExe mcp_transport = "stdio" mcp_tool_profile = "chatgpt-pro-readonly" - inbound_listener = $false + public_mcp_listener = $false + health_listener = "127.0.0.1:8080" + health_listener_scope = "loopback" direct_shell = $false } @@ -94,6 +96,7 @@ switch ($Mode) { --sample sample_mcp_stdio_local ` --profile $Profile ` --tunnel-id $TunnelId ` + --health-listen-addr 127.0.0.1:8080 ` --mcp-command $mcpCommand if ($LASTEXITCODE -ne 0) { throw "tunnel-client init failed with exit code $LASTEXITCODE" From 7163c1c002e95feaf9c13939acbb59c2de71b9ae Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 19:02:25 +0700 Subject: [PATCH 23/25] R23 R2: test loopback-only tunnel health listener --- tests/test_remote_tunnel_windows_script.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/test_remote_tunnel_windows_script.py b/tests/test_remote_tunnel_windows_script.py index 8ef911d..f0aca2c 100644 --- a/tests/test_remote_tunnel_windows_script.py +++ b/tests/test_remote_tunnel_windows_script.py @@ -38,7 +38,9 @@ def test_windows_tunnel_launcher_uses_stdio_and_pro_readonly_profile(): assert "--enable-remote" in source assert "--tool-profile chatgpt-pro-readonly" in source assert 'mcp_transport = "stdio"' in source - assert "inbound_listener = $false" in source + assert 'public_mcp_listener = $false' in source + assert 'health_listener = "127.0.0.1:8080"' in source + assert 'health_listener_scope = "loopback"' in source def test_windows_tunnel_launcher_accepts_no_arbitrary_mcp_command(): @@ -84,7 +86,9 @@ def test_windows_tunnel_launcher_plan_executes_without_credentials(tmp_path: Pat plan = json.loads(completed.stdout) assert plan["mcp_transport"] == "stdio" assert plan["mcp_tool_profile"] == "chatgpt-pro-readonly" - assert plan["inbound_listener"] is False + assert plan["public_mcp_listener"] is False + assert plan["health_listener"] == "127.0.0.1:8080" + assert plan["health_listener_scope"] == "loopback" assert plan["direct_shell"] is False assert plan["control_plane_key_present"] is False @@ -93,3 +97,9 @@ def test_windows_tunnel_launcher_binds_official_tunnel_id_format(): source = _source() assert "^tunnel_[0-9a-f]{32}$" in source assert "TunnelId has an invalid format." in source + + +def test_windows_tunnel_launcher_pins_health_listener_to_loopback(): + source = _source() + assert "--health-listen-addr 127.0.0.1:8080" in source + assert "--allow-remote-ui" not in source From d7f8fb3ae076f3274b4c4b43a2558796cf737a1b Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 19:02:28 +0700 Subject: [PATCH 24/25] R23 R2: document loopback health listener --- docs/REMOTE_COMMANDER_R2_TUNNEL.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/REMOTE_COMMANDER_R2_TUNNEL.md b/docs/REMOTE_COMMANDER_R2_TUNNEL.md index 46eb9c8..92443d2 100644 --- a/docs/REMOTE_COMMANDER_R2_TUNNEL.md +++ b/docs/REMOTE_COMMANDER_R2_TUNNEL.md @@ -76,8 +76,9 @@ characters. The runtime API key should be restricted to Tunnels Read + Use; ## Security invariants -- no inbound listener is created by ContinuityOS; +- no public MCP listener is created by ContinuityOS; - no public MCP endpoint is required; +- tunnel-client health/UI is explicitly pinned to loopback `127.0.0.1:8080`; - no arbitrary `--mcp-command` input is accepted by the launcher; - the MCP child command is fixed to `continuityos.remote_mcp_server`; - the remote root must already exist; From c29b3c881d3785ebab33c3a0c7e1afc1700f82d0 Mon Sep 17 00:00:00 2001 From: bitmaster162 <115934939+bitmaster162@users.noreply.github.com> Date: Fri, 18 Sep 2026 19:02:40 +0700 Subject: [PATCH 25/25] R23 R2: refresh merged baseline and qualification state --- docs/REMOTE_COMMANDER_R2_TUNNEL.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/REMOTE_COMMANDER_R2_TUNNEL.md b/docs/REMOTE_COMMANDER_R2_TUNNEL.md index 92443d2..259c752 100644 --- a/docs/REMOTE_COMMANDER_R2_TUNNEL.md +++ b/docs/REMOTE_COMMANDER_R2_TUNNEL.md @@ -2,10 +2,9 @@ ## Baseline -R2 is a dependent change based on R1 head -`84cf11c679f63db853cf3aa14996a811d01d1bc2`. -R1 itself is based on current `master` -`2c701b2463f62f8e43374a8f40cdb289d0bc1bad`. +R1 is merged to `master` as +`c184ff7280a6e310cdaa2b6903b45209bfc4e8f0`. +R2 is synchronized on top of that merged baseline. R2 does not require ChatGPT Pro to build or validate locally. @@ -93,8 +92,8 @@ R2 is code-complete only when: 1. R1 review-gates are green on its synchronized head; 2. R2 unit/static tests pass on Linux and Windows CI; 3. R2 CodeQL and P0 checks are green; -4. a later local Windows test proves `Plan` without credentials; +4. Windows CI proves credential-free `Plan` mode and loopback-only listener settings; 5. once tunnel credentials are available, `Init -> Doctor -> Run` is tested with the official OpenAI tunnel-client. -The last step is runtime qualification and cannot be claimed from CI alone. +The final step is runtime qualification and cannot be claimed from CI alone.