@mark1russell7/netdiag 0.0.0-bootstrap.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mark Russell
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,93 @@
1
1
  # @mark1russell7/netdiag
2
2
 
3
- This version is a placeholder. The release workflow of https://github.com/mark1russell7/netdiag publishes the real versions. Use a later version.
3
+ This package has the core model of netdiag. It has the typed facts at each layer of the stack, the contract of a probe and the scope rule. It also has the catalog of faults and the probability model.
4
+
5
+ **State: ND-1 and the model of ND-4 of the [roadmap](../../docs/ROADMAP.md).** The native probes (ND-2) come next. Then the readers change their facts into evidence for the model.
6
+
7
+ ## Facts
8
+
9
+ A fact has a layer, a time, a source and a value. Its kind names the type of its value, for example `tcp.connect`.
10
+
11
+ | Field | What it is |
12
+ | --- | --- |
13
+ | `id` | An id that is unique in the facts of one runner, for example `f12`. |
14
+ | `kind` | The name of the fact kind. The kind has the check of the value. |
15
+ | `layer` | One of `link`, `wifi`, `ip`, `route`, `path`, `dns`, `tcp`, `tls` and `http`. |
16
+ | `subject` | The target of the fact: a host and an optional port. |
17
+ | `time` | The start and the end, in milliseconds on the monotonic clock of the runner. |
18
+ | `source` | The probe, its version, and the outside tool when the probe starts one. |
19
+ | `value` | The value. It passes the check of its kind. |
20
+
21
+ A fact is plain data, so a record of facts is JSON.
22
+
23
+ ## Probes
24
+
25
+ A probe is an adapter. It tells if it is available, it does its work, and it parses its raw output into observations.
26
+
27
+ - `peers(input)` gives each host that the probe sends packets to. A passive probe gives no peers.
28
+ - `run(input, context)` does the effects and gives the raw output.
29
+ - `parse(raw, input)` is a pure function. A record of a raw output thus gives the same facts again.
30
+
31
+ A network failure is a fact, not an error. For example, a TCP connect that times out gives the outcome `timeout`.
32
+
33
+ ## The scope rule
34
+
35
+ An active probe sends packets only to a peer that the user owns or names. The runner examines each peer before the probe starts.
36
+
37
+ | Basis | Why the target is in the scope |
38
+ | --- | --- |
39
+ | `named` | The user named the target for this diagnosis. |
40
+ | `owned` | The user declares that the user owns the network or the host. |
41
+ | `configured` | The configuration of the computer names it, for example the gateway or the DNS resolver. |
42
+ | `resolved` | A DNS answer for a granted name gave this address. |
43
+
44
+ The scope also limits the number of ports of each host. The default limit is 8. Thus netdiag does no port scan.
45
+
46
+ ## Diagnosis
47
+
48
+ The entry `@mark1russell7/netdiag/diagnosis` has the probability model. It imports no `node:` module, so a browser can also use it.
49
+
50
+ - A feature is a discrete observation. For example, `tcp.connect` has the values `connected`, `refused`, `timeout`, `unreachable` and `reset`.
51
+ - Each feature has a healthy distribution: the probability of each value when there is no fault.
52
+ - A fault names a part of the network and the feature values that it predicts. The other features keep their healthy distributions.
53
+ - The hypotheses are `none` (no fault), each fault of the catalog, and `unknown` (a cause that the catalog does not have).
54
+ - The hypothesis `unknown` gives the same probability to each value. Thus it wins when no fault of the catalog explains the observations.
55
+
56
+ | Function | What it gives |
57
+ | --- | --- |
58
+ | `diagnose(catalog, evidence)` | The probability of each hypothesis, from the most probable. The model is naive Bayes with logarithms. |
59
+ | `explain(catalog, evidence, id)` | The weight of each observation for a cause: the `log10` of its likelihood ratio against `none`. |
60
+ | `predictive(catalog, diagnosis, feature, primary)` | The probability of each value of a feature before a probe observes it. The choice of the next probe (ND-5) uses it. |
61
+
62
+ Evidence with `primary: false` comes from a reference host, for example a second public server. A fault with the reach `target` does not change the reference host. A fault with the reach `shared` changes both hosts. Thus a slow TLS handshake on both hosts points to a TLS proxy. A slow handshake on the target only points to a slow server.
63
+
64
+ The standard catalog `STANDARD_CATALOG` has 15 features and 20 faults. The rules of practice give its first likelihoods, and each hypothesis has the same prior. The lab runs (ND-7) then fit these numbers.
65
+
66
+ ```ts
67
+ import { STANDARD_CATALOG, diagnose } from "@mark1russell7/netdiag/diagnosis";
68
+
69
+ const result = diagnose(STANDARD_CATALOG, [
70
+ { feature: "icmp.reply", value: "none", primary: true },
71
+ { feature: "tcp.connect", value: "connected", primary: true },
72
+ ]);
73
+ // result.causes[0].id is "filter.icmp": a filter blocks ICMP, and the host is up.
74
+ ```
75
+
76
+ ## Example
77
+
78
+ ```ts
79
+ import { fakeClock } from "@mark1russell7/algebra/clock";
80
+ import { createRunner, namedGrant, parseTarget, scopeOf } from "@mark1russell7/netdiag";
81
+
82
+ const named = parseTarget("https://example.com");
83
+ if (named.tag === "err") throw new Error(named.error);
84
+ const scope = scopeOf([namedGrant(named.value)]);
85
+ if (scope.tag === "err") throw new Error(scope.error.join(" "));
86
+
87
+ const runner = createRunner({ scope: scope.value, clock: fakeClock() });
88
+ // runner.run(probe, input) gives the outcome: done, refused, unavailable, failed or aborted.
89
+ ```
90
+
91
+ ## License
92
+
93
+ [MIT](../../LICENSE)