Multi-Hoster-Upload/docs/remote-diagnostics-setup.md
Administrator cfd5ca07ec chore(gateway/tooling): verify harnesses, Windows token ACL, ESM lint, honest transport docs
Repo-side hardening and tooling from the intensive test round (none of this ships
in the app installer).

- gateway: registry.json (holds bearer tokens) now gets a best-effort owner-only
  NTFS ACL on Windows via `icacls /inheritance:r /grant:r <user>:F` (the chmod
  0600 is a no-op on NTFS); verified the file ends up <user>:(F) only.
- gateway: connect_server now reads the app version from the real get_system_info
  shape (data.app.version / data.agent.version), so "connected to vX.Y.Z" works.
- gateway: read_log tool description documents grep as a case-insensitive substring
  filter with "|" alternation (not a regex), matching the agent-side change.
- gateway: standalone verification harnesses moved to gateway/verify/ (so
  `node --test` only sweeps real unit tests) and exposed via `npm run verify`:
  e2e-verify, integration-mcp (live gateway-MCP <-> agent, all 14 tools), and
  adversarial-probe (redaction fuzz + ReDoS + lockout). `npm test` runs the units.
- eslint: gateway/** now lints as ESM (sourceType module) via a dedicated block;
  global ignores fixed so `eslint .` is clean across the whole project (0 errors).
- docs/remote-diagnostics-setup.md: made the transport story honest — the agent
  speaks plaintext ws:// over enforced loopback; the SSH/WireGuard tunnel is the
  ONLY confidentiality layer (wss/TLS + cert-pin is a documented future mode, not
  active). Removed the stale "bind to a LAN/VPN IP" guidance (loopback is enforced).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 18:41:21 +02:00

5.4 KiB

Remote Diagnostics Setup (read-only)

This guide explains how to let Claude Code run read-only diagnostics against a Multi-Hoster-Uploader instance running on a remote Windows server, through the local stdio MCP gateway in gateway/.

The gateway is an MCP server to Claude and a WebSocket client to a diagnostic agent inside the app. It never touches the screen, never injects input, and never writes anything on the server.


1. Enable diagnostics on the server and copy the code

On the server (the machine running the app):

  1. Open the app's Settings.
  2. Enable "Diagnose-Zugriff".
  3. Copy the connection code. It looks like mhu1_<base64url...>.

The code carries a one-time auth token. It does not carry a host — you supply the host yourself when you connect. Treat the code as a secret: anyone with the code and network reach to the agent can read diagnostics.

The agent always binds to 127.0.0.1 (loopback only) — this is enforced; there is no setting to expose it on a LAN/Internet interface. You reach it through a tunnel (next step).

Transport note: the agent speaks plaintext ws:// over loopback. The token and the diagnostic data are not encrypted on the wire by the agent itself — confidentiality comes entirely from the tunnel (SSH/WireGuard) you put in front of it. The loopback bind means the plaintext traffic never leaves the host except inside that encrypted tunnel. (A future build may add wss/TLS with cert pinning via an fp field in the code; it is not active today.)


2. Reach the agent over a tunnel (SSH local port-forward or WireGuard)

Because the agent binds to 127.0.0.1 on the server, open a tunnel from your machine to the server's loopback. The agent's default port is 9110.

ssh -L 9110:127.0.0.1:9110 user@server

Leave that session open. Now 127.0.0.1:9110 on your machine is forwarded to 127.0.0.1:9110 on the server. You connect Claude to 127.0.0.1 (your local end of the tunnel), not the server's public IP.

WireGuard (alternative)

Bring up a WireGuard tunnel to the server, then connect to the server's WireGuard address (or, if you forward loopback over the tunnel, 127.0.0.1). Use whichever address resolves to the agent's 127.0.0.1:9110 on the server.

The agent cannot be bound directly to a LAN/Internet IP in this build (loopback is enforced), so the tunnel is the only way to reach it remotely — and the only thing encrypting the transport. Keep the tunnel (SSH/WireGuard) up for the whole session.


3. Install the gateway and register it with Claude (one time)

On the machine running Claude Code:

cd gateway
npm install

Register the gateway as an MCP server (one time):

claude mcp add --transport stdio mhu-diag -- node "C:\Users\ploet\Desktop\Claude Projekte\multi-hoster-uploader\gateway\index.js"

Adjust the absolute path if your checkout lives elsewhere.


4. Usage

With the tunnel up, tell Claude:

server prod-3 at 127.0.0.1, code mhu1_<...>

Claude will:

  1. call connect_server(code:"mhu1_...", host:"127.0.0.1"),
  2. immediately read the app version via get_system_info,
  3. remember the server under its label in registry.json.

Next time you can just say:

connect_server(label:"prod-3")

with no code — the gateway dials it from the registry. Then ask Claude "what's wrong?" — it will typically start with server_health, the one-shot hub that aggregates errors, queue, rotation and system info in a single call. Other read-only tools: read_log, list_logs, list_errors, get_queue_state, get_history, get_config_redacted, get_rotation_state, get_app_events.


5. Failure → meaning

When a connect or a request fails, the gateway returns a human-readable cause. Quick reference:

Symptom / signal What it means / what to do
ECONNREFUSED app not running / wrong port / inbound firewall closed
ETIMEDOUT firewall DROP / NAT not forwarded / tunnel down
close code 4001 auth timeout, retry
close code 4002 stale or rotated code — re-copy the current code from the server
close code 4003 brute-force lockout, wait 60s
connected but no auth-ok old app version without the diagnostic agent — update that server
wss fingerprint mismatch reserved for the future TLS mode (not active today) — re-copy the code

If you tunnel and still get ECONNREFUSED, check that the SSH session is up and that the agent is actually listening on 127.0.0.1:9110 on the server (Diagnose-Zugriff enabled).


6. What this is — and is not

  • It is READ-ONLY. It reads logs, errors, queue/history, redacted config, rotation and system info. No screen capture. No input injection. No writes or config changes.
  • The code is a secret (it carries the auth token). Don't paste it into public channels.
  • gateway/registry.json stores tokens for remembered servers and is git-ignored — never commit it.