# Architecture

English | 简体中文

## Architecture diagrams ### 0. File storage layer — what the plugin writes and how long it keeps it ```mermaid flowchart TB subgraph DATA_DIR["~/.dsh/dsh-network-settings/"] LR["last-report.json
~25KB · overwritten on every check
redacted before write"] AH["action-history.json
last 50 entries · append-only
used for 'recently applied' badges"] DSH_CFG["dsh-config.json
DSH process proxy persistence"] SNAP["snapshots/
one JSON per repair
pruned to 50 (oldest removed)
redacted, atomic write"] end BAK["system file backups
hosts → .dsh-network-settings.bak
shell profiles → .bak (sed)"] CHECK["run check"] -->|"overwrite"| LR REPAIR["apply repair"] -->|"create"| SNAP SNAP -->|"prune >50"| SNAP REPAIR --> BAK ADVANCED["advanced action"] -->|"append, trim to 50"| AH DSH_PROXY["dsh.process configure"] --> DSH_CFG ``` Retention policy: `last-report.json` is single-slot (always the latest); action history is capped at 50; snapshots are pruned to 50 after each save; system file backups (`.bak`) sit next to the original file and are left for the user to clean. ### 1. System layers — two halves, one RPC channel ```mermaid flowchart LR subgraph CLIENT["Client half · src/client (React, platform-free)"] direction TB UI["Settings UI
NetworkTab · NetworkGraph · NetworkConfig
RepairSection · AdvancedSection"] SVC["service.ts
typed RPC client"] REP["report.ts
agent report builder"] end subgraph HOST["Host half · src/host (DSH Node process)"] direction TB RPC["index.ts
RPC switch · authority: loopback"] CORE["Network Core
inspection · graph · diagnosis · repair"] end UI --> SVC SVC -- "Connection RPC /dsh-network-settings" --> RPC RPC --> CORE CORE -- "redacted JSON" --> RPC SVC --> REP ``` The client never executes platform commands; every system action crosses the RPC boundary. The wire contract mirrors `src/host/network/types.ts` on the client as `src/client/contract.ts`. ### 2. Detection pipeline — data contracts on every edge ```mermaid flowchart TB RT["network/runtime.ts
detectRuntime()"] INS["inspect.ts · inspectNetwork()
ONE hard deadline (45–60s)"] WIN["windows/inspect.ts
one PowerShell sweep"] WSL["wsl/inspect.ts
wsl.exe discovery + local /bin/sh facts"] MAC["mac/inspect.ts
scutil · networksetup · route · lsof
+ shell-profile residue scan"] PRB["probe/* · DNS → TCP → TLS → HTTP
node-side + in-distro, layer timeouts"] INSPECTION["NetworkInspection
(serializable data contract)"] GRAPH["network/build-{windows,wsl}.ts
+ network/shared.ts vocabulary"] DRIFT["network/drift.ts
5 drift rules"] RULES["diagnose/rules.ts
9 deterministic rules"] GATE["repair/catalog.ts
confidence ≥ 0.85 + whitelist
+ platform filter"] REPORT["BuiltNetworkReport
graph · diagnosis · summary · targets"] RT -->|"DetectedRuntime"| INS INS --> WIN --> INSPECTION INS --> WSL --> INSPECTION INS --> MAC --> INSPECTION INS --> PRB --> INSPECTION INSPECTION --> GRAPH GRAPH -->|"NetworkPathGraph"| DRIFT INSPECTION --> RULES GRAPH --> REPORT DRIFT --> REPORT RULES --> REPORT REPORT --> GATE ``` ### 3. Module dependency layers (host half) ```mermaid flowchart TD subgraph L0["L0 · infrastructure (effectful primitives)"] CMD["runtime/command"]:::inf PS["runtime/powershell"]:::inf STORE["runtime/store"]:::inf REDACT["redact.ts"]:::inf end subgraph L1["L1 · collectors (platform facts, effectful)"] WINI["windows/inspect"]:::col WSLI["wsl/*"]:::col MACI["mac/inspect"]:::col PROXY["proxy/*"]:::col end subgraph L2["L2 · probes (effectful, time-bounded)"] PNET["probe/net · pure Node"]:::probe PWSL["probe/wsl · distro scripts"]:::probe end subgraph L3["L3 · core (pure over data contracts)"] SHARED["network/shared · vocabulary"]:::core BUILD["network/build-*"]:::core DRIFTM["network/drift"]:::core RULESM["diagnose/rules"]:::core CATM["repair/catalog"]:::core end subgraph L4["L4 · effects (persistent changes)"] CONF["configure/*"]:::eff REPM["repair/* · advanced/hosts/wsl-proxy"]:::eff SNAP["snapshot/* · diff + store"]:::eff end ENTRY["index.ts · RPC entry"]:::entry INSPECT["inspect.ts · orchestration"]:::entry NETIDX["network/index · report assembly"]:::entry ENTRY --> INSPECT & NETIDX & CONF & REPM & CATM INSPECT --> WINI & WSLI & MACI & PROXY & PNET & PWSL & NETIDX NETIDX --> SHARED --> BUILD --> DRIFTM NETIDX --> RULESM DRIFTM & RULESM --> CATM CONF & REPM --> SNAP WINI & WSLI & PWSL & PS & CONF & REPM --> CMD WINI & CONF & REPM --> PS SNAP & STORE & ENTRY --> REDACT classDef inf fill:#eee classDef col fill:#dfd classDef probe fill:#ddf classDef core fill:#fdd classDef eff fill:#fed classDef entry fill:#fff ``` Rules of the layering: L3 is pure (no spawn, no fs) and unit-tested with recorded fixtures; L1/L2 wrap every platform command behind one facade function; L4 is the only place that mutates the system and always goes through snapshots. **Deep Module annotations** — modules with narrow interfaces hiding significant complexity (Ousterhout): | Module | Interface | Hidden complexity | |---|---|---| | `runtime/command.ts` | `runCommand(file, args, opts)` | timeout, abort, SIGKILL escalation, output caps, encoding | | `windows/inspect.ts` | `inspectWindowsFacts()` | one PowerShell script, UTF-8 contract, netsh parsing | | `probe/probe.ts` | `probeTarget(target, path, opts)` | 4-layer progression, CONNECT tunnels, proxy DNS bypass, sampling | | `mac/inspect.ts` | `inspectMacFacts()` | scutil, networksetup, route, lsof, sw_vers, shell-profile scan | | `inspect.ts` | `inspectNetwork()` | hard deadline, model-driven collection, endpoint merge, listener annotation | Modules that are intentionally **not deep** (thin data transformers): `network/shared.ts` (vocabulary, not logic), `redact.ts` (pure function), `snapshot/diff.ts` (JSON diff). ### 4. Runtime model selection ```mermaid flowchart TB PLAT{"process.platform"} PLAT -->|win32| WN["WINDOWS_NATIVE"] PLAT -->|linux| K{"WSL kernel in
/proc/version?"} K -->|"microsoft + WSL_DISTRO_NAME"| WD["WSL_DISTRIBUTION
(facts: local /bin/sh + interop)"] K -->|"container cgroup"| UNS["UNSUPPORTED_RUNTIME"] K -->|"plain linux"| UNS PLAT -->|darwin| MAC["MACOS_NATIVE
(facts: scutil + shell profiles)"] WN --> BW["build-windows.ts"] WD --> BWS["build-wsl.ts"] MAC --> BWM["build-mac.ts"] ``` ### 5. Repair recommendation gating and lifecycle ```mermaid flowchart LR D["Diagnosis
(code · severity · confidence · actions)"] T{"confidence ≥
RECOMMEND_CONFIDENCE_THRESHOLD
(0.85)?"} M["diagnosisActionOperations()
scope-exact mapping"] W{"operation in the
common whitelist?"} REC["Recommended button
(flush-dns · clear env vars ·
clear system proxy · clear DSH env)"] MAN["Manual catalog only
(admin · reboot · non-recoverable)"] PREV["preview diff"] --> CONFIRM["user confirm"] --> APPLY["apply"] --> SNAP["snapshot"] --> RERUN["re-detect"] --> VER["verify"] D --> T -->|yes| M --> W T -->|no| MAN W -->|yes| REC --> PREV W -->|no| MAN ``` ### 6. Testing seams ```mermaid flowchart LR FL["scripts/fault-lab.ts
in-process env injection
(interrupt-safe, zero residue)"] PIPE["real pipeline
inspect → graph → diagnosis → gating"] A["assert diagnosis codes
+ egress mode + recommended ops"] UT["tests/unit/*
recorded fixtures
(Windows · WSL · macOS)"] PARSERS["exported parsers
(documented test seams)"] FL --> PIPE --> A UT --> PARSERS UIT["tests/ui/*
mocked primitives + service"] E2E["tests/e2e · live DSH via Playwright"] ``` ## Overview ```text DSH Settings (React) ←── platform-free, no system commands │ typed RPC (/dsh-network-settings, authority: loopback) ▼ Host half (DSH Node process) │ ├─ Runtime detection: WINDOWS_NATIVE / WSL_DISTRIBUTION / MACOS_NATIVE ├─ Platform collectors (L1): windows/wsl/mac — one facade each ├─ Layered probes (L2): DNS → TCP → TLS → HTTP, hard timeouts ├─ Pure core (L3): graph builders + diagnosis rules + repair catalog ├─ Effects (L4): configure + repair + snapshot — the only mutation layer └─ File storage: last-report (single-slot) + snapshots (pruned to 50) ``` The client never executes platform commands. Every system action goes through the host RPC channel with `authority: loopback`. ## Modules ### `src/host/network` | File | Responsibility | |---|---| | `types.ts` | JSON-safe graph types: `NetworkPathGraph`, `NetworkPath`, `PathNode`, `PathEdge`, `Evidence`, `ProxyConfiguration`, `ProxyEndpoint`, `NetworkDiagnostic`, `NetworkPathSummary` | | `runtime.ts` | Static runtime detection from `process.platform`, `/proc/version`, `WSL_DISTRO_NAME`, `/etc/os-release`, cgroup | | `survey.ts` | Read-only survey consumed by builders | | `build-windows.ts` | `WINDOWS_NATIVE` DSH path builder | | `build-wsl.ts` | `WSL_DISTRIBUTION` DSH path builder | | `build-mac.ts` | `MACOS_NATIVE` DSH path builder (direct + proxy) | | `drift.ts` | Configuration Drift diagnostics + repair hints | | `index.ts` | Orchestration: target list, graph building, summaries | ### `src/host` | File/Area | Responsibility | |---|---| | `windows/inspect.ts` | PowerShell inspection: adapters, routes, WinINet, WinHTTP, env, listeners, Hosts, gateway ICMP/neighbor | | `mac/inspect.ts` | macOS facts: scutil proxy, networksetup adapters, route, lsof listeners, shell-profile residue scan | | `wsl/*` | `wsl.exe` list parsing, `.wslconfig`, `/etc/wsl.conf`, distribution facts | | `probe/net.ts` | DNS/TCP/TLS/HTTP probes, repeated sampling for stability mode | | `probe/probe.ts` | DIRECT/PROXY orchestration and first-failure layer mapping | | `probe/wsl.ts` | In-distribution probes over `runWslScript` (local `/bin/sh` for the current distro, `wsl.exe` for others) | | `network/shared.ts` | Shared graph helpers: proxy resolution, endpoint/listener matching, adapter selection (egress + physical uplink), gateway evidence | | `diagnose/rules.ts` | Deterministic diagnosis rules | | `configure/*` | Scoped configuration with preview/snapshot/apply | | `repair/*` | Operation catalog, recommendations, WSL/Hosts repairs, advanced actions | | `redact.ts` | Secret redaction for reports and snapshots | ### `src/client` | File | Responsibility | |---|---| | `NetworkTab.tsx` | Settings tab entry, actions, target switcher, report copy | | `NetworkGraph.tsx` | Diagnosis summary, first-failure details, DSH path lane | | `NetworkConfig.tsx` | Hierarchical configuration with progressive disclosure | | `RepairSection.tsx` | Recommended repairs + manual operations + rollback/history | | `AdvancedSection.tsx` | High-risk system recovery actions with explicit confirmation | | `service.ts` | Typed RPC client over the DSH Connection channel | | `contract.ts` | Client-side wire types | ## Three runtime models ```text WINDOWS_NATIVE DSH → Windows → [Proxy] → Adapter (TUN/VPN or physical) → [Physical uplink] → Gateway → Internet → Target WSL_DISTRIBUTION DSH → Distribution → WSL Network (NAT/Mirrored/…) → Windows Host → [Proxy] → Adapter (TUN/VPN or physical) → [Physical uplink] → Gateway → Internet → Target MACOS_NATIVE DSH → macOS → [Proxy] → Adapter (en0/utun) → Gateway → Internet → Target ``` Distribution identity and WSL network layer are separate concepts. NAT is an edge translation semantic, never drawn as a fake server. Mirrored/Bridged/ VirtioProxy keep their own edge relations. When a TUN/VPN adapter owns the default route (e.g. a proxy client's `198.18.0.0/15` virtual network), it stays the egress adapter — traffic really flows through it — but the graph also chains the physical uplink NIC and the physical gateway behind it, and the Windows host node shows the physical IP. ICMP/neighbor gateway evidence is only claimed for the gateway it was actually measured against. ### Command execution per model - `WINDOWS_NATIVE`: Windows facts via one PowerShell invocation; WSL facts via `wsl.exe`. - `WSL_DISTRIBUTION`: the current distribution's probes and facts run through local `/bin/sh` (no interop round-trip, no re-entry hang); other distributions via `cmd.exe → wsl.exe`. Windows-side operations (WinINet, WinHTTP, env vars, DNS cache) still require Windows interop; when interop is unavailable they fail with an actionable message instead of raw ENOENT, and the current distribution is synthesized from local files so the graph keeps working. ## Probe layers ```text DNS ↓ success TCP (3 attempts in stability mode) ↓ success TLS ↓ success HTTP HEAD ``` Proxy paths delegate DNS to the proxy and use CONNECT for HTTPS targets. Timeouts are enforced at every level: each layer carries its own budget (DNS 4s, TCP 4s, TLS 6s, HTTP 8s — covering both response headers and body), canceled probes resolve instead of hanging (DNS uses a cancellable Resolver), and the whole inspection runs under one hard deadline (60s from the RPC entry, 45s by default) so a broken network can never stall a check indefinitely. ## Configuration Drift A difference is not an error. Drift becomes a diagnostic only when: - the DSH proxy endpoint is demonstrably gone/unreachable; - WSL can reach Windows Host but not the configured proxy; - WinHTTP still points at a port with no listener. Healthy configuration differences are reported as `info`. ## Repair recommendation policy A repair button is marked "recommended" only when all three hold: - the driving diagnosis has confidence ≥ 0.85 (`RECOMMEND_CONFIDENCE_THRESHOLD`), - the mapped operation is in the common-operation whitelist, - the operation's platform tag matches the current runtime (`operationsForPlatform(process.platform)` filters the catalog and recommendations — Windows-only ops are invisible on macOS and vice versa). The whitelist includes platform-neutral ops (`clear-dsh-process-proxy`, `flush-dns`/`mac-flush-dns`) and platform-specific ops that are only recommended on their own platform (`clear-user-env-proxy` on Windows, `mac-clear-shell-proxy`/`mac-clear-scutil-proxy` on macOS). Admin/UAC, reboot-requiring and non-recoverable operations (machine env, WinHTTP machine reset, Winsock/TCP-IP resets, `wsl-autoproxy-enable`) never appear as recommendations; they stay in the manual catalog. Duplicate operations across diagnoses are deduplicated server-side. ## Repair guarantees Every persistent change: ```text read current value → snapshot → diff preview → user confirmation → apply → re-detect → verify ``` A successful command is never treated as a successful network repair. ### File retention | File | Strategy | Cap | |---|---|---| | `last-report.json` | Overwritten on every check | 1 file (~25KB) | | `action-history.json` | Append, trim oldest | 50 entries | | `snapshots/*.json` | One per repair, pruned after each save | 50 files | | System `.bak` files | Left next to the original | User-managed | All files are redacted before write; snapshots use atomic write (tmp + rename).