LinkWatch
README.md
# LinkWatch · Linux preview
A local network diagnostic CLI, MCP server and monochrome dashboard. Built from the selected topology + OpenCode Companion concept. This is an early working prototype, not a complete network management product.
## Start
Requires Node.js 22+ and Linux `ip` (iproute2). No root required for current checks.
```bash
npm install
npm run build
node bin/linkwatch.mjs start
```
Open http://127.0.0.1:8787 on this laptop. The server binds only to loopback. Local HTTP is the default. Optional HTTPS accepts your own certificate and key through LINKWATCH_TLS_CERT and LINKWATCH_TLS_KEY; automatic certificate trust and authenticated LAN/remote access are not implemented. Do not expose this development server publicly.
## CLI
```bash
node bin/linkwatch.mjs status --json
node bin/linkwatch.mjs discover --json
node bin/linkwatch.mjs discover --active --json
node bin/linkwatch.mjs check 192.0.2.93 --port 9100
node bin/linkwatch.mjs monitor --printer 192.0.2.93 --interval 30 --seconds 86400
node bin/linkwatch.mjs incidents
node bin/linkwatch.mjs report > report.json
node bin/linkwatch.mjs doctor
node bin/linkwatch.mjs install
node bin/linkwatch.mjs uninstall
node bin/linkwatch.mjs theme light
```
Exit codes are meaningful: `0` success, `1` runtime failure, `2` invalid usage. A typo in a flag gives 2 and a real network problem gives 1, so scripts can tell them apart.
`monitor` runs in its own terminal; Ctrl+C stops it. It is not installed as a boot service. The previously configured `pos-network-monitor` service is separate and has not been replaced. Monitor records TCP handshakes to the selected printer and gateway web port. Failure of the gateway's web port does not prove the gateway is down. Three consecutive failures open an incident; success closes it. A link that cannot be read is written as an explicit missing observation, which breaks a pending failure streak rather than counting as a failure. Only one writer runs at a time: a lock file prevents a second monitor, a lock left behind by a killed process is reported as stale and taken over, and SIGINT/SIGTERM end the loop cleanly and release the lock. Logs rotate at 8 MiB keeping one previous file, and the report reads both. Retention can truncate incident history. Timestamps are ISO; the dashboard shows Bangkok time.
## Dashboard
Start it with `node bin/linkwatch.mjs start` and open http://127.0.0.1:8787. The server binds only to loopback.
- **Monitoring is off by default and starts only when you press Start recording.** A bar under the map always states whether anything is recording and shows the interval, start time and pid. A monitor started from the dashboard stops when the dashboard closes, so nothing keeps running unseen in the background. For long runs use the CLI, where you can watch it.
- **Scan subnet** sends one ICMP echo per address on the connected /24 or smaller, eight at a time. It sends nothing to any port and never runs automatically.
- **Device notes and service ports** are entered per device. They live in `.data/labels.json`, are kept separate from measurements, and are labelled `operator note, not a measurement`.
- **Declared links** record your own statement that two devices are physically connected. LinkWatch cannot detect switch ports or mesh associations, so nothing is drawn until you declare it. Declared links are dashed, labelled `Declared · wired` or `Declared · wireless`, live in `.data/links.json`, and are never presented as measured. Withdraw them per device.
- **Replay** in the live workspace walks the records actually retained for the selected incident. The demo workspace replay is synthetic and is labelled as such.
## What is real
- Ethernet IPv4/interface/route selection, Linux neighbor-cache discovery, MACs where known.
- Source-address-bound TCP tests with a 2-second timeout and **no payload**, local subnet targets only.
- Laptop interface byte counters and receive/transmit rate sampling, **not all-device bandwidth**.
- Bounded recording, incident classification, JSON report download.
- Operator device notes, service ports and declared wiring, stored apart from observations.
- Theme preference in `.data/theme.json`; browser preference takes priority and persists in localStorage.
- Twelve MCP tools, validated through the SDK client handshake.
## What is illustrative or not implemented
- Demo workspace contains sample topology and sample incidents; it never masquerades as real measurements.
- Dashboard discovery reads the OS neighbor cache. The dashboard's Scan subnet button and CLI `discover --active` perform bounded ICMP discovery on /24 or smaller networks, eight probes at a time. Quiet or filtered devices can still be missing; cache entries can be stale.
- Physical topology links, mesh associations, vendor identification, packet capture and per-device traffic accounting are not automatically available. Dashed links are your own declarations, not measurements.
- Live graphs show no links at all until you declare them, because a neighbor cache entry is not a physical connection.
- The sample chat is not AI output. Real chat requires the separate OpenCode server setup below.
- Automatic diagnosis, automatic TLS trust setup, remote or LAN access, capture and per-device bandwidth remain future work.
## OpenCode MCP
`opencode.json` in this folder points to this checkout and is gitignored, because it contains this machine's home directory and Node path. For other checkouts copy `opencode.example.json` and replace `/ABSOLUTE/PATH/TO/`.
```bash
opencode mcp list
```
Tools: `network_status`, `discover_devices`, `check_device`, `get_topology`, `list_incidents`, `inspect_incident`, `monitor_status`, `list_labels`, `save_label`, `list_links`, `declare_link`, `remove_declared_link`. No arbitrary shell tool, no printer payloads, no router mutations, and nothing that can start or stop a monitor. MCP stdio logs do not pollute stdout. Device information returned through OpenCode may be sent to your chosen hosted model.
## Optional dashboard chat adapter
Configure your hosted model in OpenCode yourself. Start an authenticated loopback OpenCode server in the desired project. Launch LinkWatch with `LINKWATCH_OPENCODE_URL=http://127.0.0.1:4096` and matching `OPENCODE_SERVER_PASSWORD` if authentication is enabled. The API refuses any OpenCode endpoint that is not local HTTP, so evidence cannot be sent to a remote host through it.
The adapter creates a fresh OpenCode session per question and submits only the typed question and the evidence snapshot shown in the dashboard. It is an evidence-explanation adapter, not an agent loop: the message is sent with every tool id the server reports switched off, and the system text states that device names and logs are untrusted data rather than instructions. Verified against the official OpenCode server documentation on 2026-10-04: `POST /session` takes only `parentID` and `title`, and tool restriction is done through the `tools` field on `POST /session/:id/message`. Oversized evidence is dropped with an explanation rather than truncated. Nothing is sent until you press send, and credentials stay server-side.
End-to-end hosted inference has **not** been tested against a real model. The adapter is covered by tests against a local mock OpenCode server. No provider was selected or charged during development.
## Development and tests
```bash
npm test
npm run dev -- --port 5173
```
Start the API with `npm start`; Vite proxies `/api` to port 8787. The production build is served directly by the CLI. Runtime data belongs to `.data/` (override with `LINKWATCH_DATA_DIR`) and is gitignored. Do not commit real diagnostic exports or credentials.
MIT licensed. See [RELEASE-NOTES](docs/RELEASE-NOTES.md) for what each version does and, more importantly, what it does not do. `package.json` keeps `"private": true`, so nothing is published to npm; the MIT license permits it but a global install is a poor fit for a tool that needs a local Linux checkout. Continuous integration runs the tests, the build and the Sites bundle check on Node 22 and 24.
## Continue development in OpenCode
Read [the complete handoff](docs/OPENCODE-HANDOFF.md) for decisions, file map, release milestones, validation, and the continuation prompt. The previous hourly Codex watch and standalone monitoring service were stopped at the owner’s request; leave them stopped.
## Installed CLI and optional TLS
The CLI is installed on this laptop at `~/.local/bin/linkwatch`. Keep this checkout in place. On another machine, run `node bin/linkwatch.mjs install` after installing dependencies and building, and `linkwatch uninstall` to remove the symlink. `linkwatch doctor` checks dependencies, the data directory permissions, whether the CLI is installed, HTTPS configuration and the monitor state without probing the network. `linkwatch mcp-config` emits a configuration with this machine's Node and CLI paths; merge its MCP entry into your existing OpenCode settings.
To use HTTPS, supply a certificate and key that your browser already trusts:
```bash
LINKWATCH_TLS_CERT=/path/to/cert.pem LINKWATCH_TLS_KEY=/path/to/key.pem linkwatch start
```
Both HTTP and HTTPS bind to loopback only. Setting one variable without the other is refused, and a certificate path that does not exist fails loudly rather than quietly falling back to plain HTTP. LinkWatch never modifies router or operating system trust settings, so a self-signed certificate will show a browser warning; that is expected. The integration test asserts that an untrusted certificate is genuinely rejected, so this cannot silently regress into being trusted.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues