{
  "schema": "leumas.docs.page/1",
  "id": "skill:leumas-network",
  "slug": "skills/leumas-network",
  "kind": "tools",
  "bucket": "skill",
  "title": "leumas-network",
  "name": "Leumas Network",
  "eyebrow": null,
  "chip": null,
  "summary": "Scan a network or capture packets from Leumas — nmap, wireshark, tshark, pcap, device discovery, capture filters, Information → Nmap and Information → Wireshark.",
  "keywords": [
    "leumas-network",
    "packets",
    "wireshark",
    "tshark",
    "pcap",
    "nmap",
    "information",
    "scan"
  ],
  "audience": "both",
  "funnel": {
    "product": null,
    "cta": null
  },
  "body": "\n# Looking at the network Leumas is running on\n\nTwo Information tabs over **the operator's own installed binaries** — nothing is bundled, nothing is\ninstalled on anyone's behalf. One SDK underneath, so the Studio surface, an MCP client, an AI\nfunction-call and a workflow node all call the same functions.\n\n```\nInformation UI · MCP tool · AI functioncall · workflow node\n                     ↓  (all four, same functions)\n        shared/engines/adapters/domain/network/     ← HTTP + MCP + functioncall, no router written\n                     ↓\n        @leumas/network-sdk   shared/engines/network/\n        ↓                                   ↓\n   NmapProvider                        TSharkProvider\n        ↓                                   ↓\n   runBounded(bin, args, { shell: false })  ← @leumas/runcode\n```\n\nInventory of every verb, route and limit: [`reference/capabilities.md`](reference/capabilities.md).\n\n## Reach for it when\n\n| You want | Call |\n|---|---|\n| What is on this network | `discover({ target })` — or the Nmap tab, which does it on arrival |\n| What is this one device | `inspect({ target })` · `services({ target })` |\n| Which adapters can capture | `interfaces()` |\n| Watch traffic for a bounded period | `capture({ interfaceIndex })` · `observe({ address })` |\n| Read a saved capture | `analyze({ file })` |\n| What changed since last time | `snapshot(scan)` then `diff(before, after)` |\n| Does this machine even have the tools | `health()` |\n\nHeadless, the same thing: `node ops/tools/run-tool/run-tool.mjs network discover '{\"target\":\"127.0.0.1\"}'`.\n\n## The traps, each of which has already cost a pass\n\n**Detection cannot use `PATH` alone.** Measured: tshark working from `D:\\Wireshark` with `where\ntshark` finding nothing, and nmap under `Program Files (x86)` rather than `Program Files`. A\nPATH-only probe tells somebody running Wireshark that Wireshark is missing — the worst failure this\nfeature can have, because it is indistinguishable from the truth. `detect.js` probes an env override,\nthen `PATH`, then a per-tool table of real install locations. `pnpm check:toolchains` is PATH-only\nand will disagree; that is expected, not a bug.\n\n**`-e ip.src` is empty on IPv6.** Those frames have no `ip.*` field at all and `-T ek` omits absent\nfields rather than emitting a blank. On a dual-stack home network that is *most* traffic, so the\npacket list renders with blank source and destination columns while every IPv4 fixture passes. Use\n`_ws.col.def_src` / `_ws.col.def_dst`.\n\n**`-w` plus `-P -T ek` ignores `-e`.** Writing a pcap and printing summaries at once makes tshark emit\nits column set instead. Reading a file back has no `-w`, so it is *not* in that mode and needs the\nfields named explicitly — miss this and analysis returns the right number of packets with every\nfield empty, while the live path looks perfect.\n\n**`dumpcap -d` exits 0 on an invalid filter** when its output is piped. Read the `Invalid capture\nfilter` text, never the exit code.\n\n**nmap's progress restarts per task.** The ARP sweep reaches 100% and DNS resolution begins again at\n0. One unlabelled bar appears to run backwards; render the task name, or use `ProgressBar`'s\n`stages`/`stage`.\n\n**`routeInfo()` in `@leumas/devices/host` is not usable here.** Measured at over eight seconds for an\nempty answer. `defaultGateway()` in this SDK reads the routing table directly in about a fifth of a\nsecond. Do not \"fix\" one by calling the other.\n\n**Studio CSS tokens are `--color-*`, not `--text-1`/`--surface-2`/`--accent`.** An unknown custom\nproperty is invalid-at-computed-value-time: no warning anywhere, the page just renders flat and grey.\nThis surface shipped that way once. `shared/engines/network/test/theme-tokens.test.js` now fails on\nan unresolvable `var()`.\n\n## The rules that are not negotiable\n\n- **No shell string, ever.** Every argument is a constant or a validated value in an array, spawned\n  `shell: false`. There is no free-text nmap/tshark argument box — the command that ran is *shown*\n  instead, which is what an expert actually wanted from one.\n- **The policy lives in the SDK, not the caller.** `checkPolicy` runs inside every scan verb, so an\n  MCP client is bound exactly as the UI is. Private ranges and this machine's own subnets by default;\n  anything wider needs an explicit `allowPublic`, and an oversize range is refused before a spawn.\n- **Machine-local planes only.** Mounted in `mountNetworkTools` beside `/api/device-hub`. On a hosted\n  plane the network being scanned would be the *server's*, so the routes must be absent, not gated.\n- **A refusal is data.** `unavailable` · `refused` · `cancelled` · `ok` are fields on a resolved\n  object. Nothing throws for an expected outcome, and a cancel is never drawn as an error.\n- **Never state a guess as a fact.** `classifyDevice` scores evidence and the UI renders its hedged\n  `label` — \"Possible router\", never \"Router\" — unless several signals agree. Exactly two titles are\n  allowed to be flat: \"This computer\" and \"Your router\", both from facts. Cleaning up a *name* in\n  `present.js` must never strengthen a *claim*; that is why the two files are separate.\n- **Nothing captured is kept.** A capture writes a bounded pcapng to a temp directory that is removed\n  unless the operator explicitly saves it. Payload bytes are never the default view.\n\n## Changing it\n\nGuards: `pnpm check:adapters check:mcp check:toolchains`, `pnpm smoke:network`, and the package's own\n`node --test test/*.test.js`. Touching the surface adds `check:ui-kit check:theme check:nav`.\n\nThe fixtures under `shared/engines/network/test/fixtures/` came off the real binaries — sanitised, but\nreal shapes. Prefer adding one to inventing a shape by hand; every trap above was found by a fixture\ndisagreeing with an assumption.\n\n## Related\n\n`leumas-guards` for what to run · `leumas-capabilities` for how an adapter pack becomes three\nsurfaces · `leumas-domains` for the Information tab strip itself.\n",
  "source": {
    "path": ".claude/skills/leumas-network/SKILL.md",
    "blobSha": "",
    "commit": "",
    "committedAt": "",
    "provenance": "no-git",
    "bytes": 6209,
    "hash": "d719b35c61578932984c09fe54a8d170cc19698e"
  },
  "urls": {
    "html": "/p/skills/leumas-network",
    "json": "/docs/skills/leumas-network.json",
    "md": "/docs/skills/leumas-network.md"
  },
  "links": {
    "composes": [],
    "usedBy": [],
    "product": [],
    "howTo": [],
    "skills": []
  }
}
