moshcode 0.81.0 → 0.83.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moshcode",
3
- "version": "0.81.0",
3
+ "version": "0.83.0",
4
4
  "type": "module",
5
5
  "description": "moshcode \u2014 a metal wrapper for coding engines and native UGig/CoinPay workflow CLIs, with OpenPRD and moshscript",
6
6
  "repository": {
@@ -67,6 +67,7 @@ export function serviceUnit({
67
67
  port,
68
68
  registryBase = null,
69
69
  upstreams = [],
70
+ proxy = null,
70
71
  user = process.env.USER || process.env.LOGNAME,
71
72
  } = {}) {
72
73
  if (!entry) throw new Error("serviceUnit needs the entry script to run");
@@ -80,6 +81,13 @@ export function serviceUnit({
80
81
  // registry. Recorded here, while a working resolver is still around to be
81
82
  // asked, rather than rediscovered at boot when it cannot be.
82
83
  if (upstreams.length) args.push("--upstream", upstreams.join(","));
84
+ // Without this the supervised bridge answers every name with its origin, and
85
+ // the origin serves a certificate no CA signed — so `https://` fails on a
86
+ // machine where the pinned-TLS proxy is installed, trusted and running.
87
+ // `dns enable` has always probed for the proxy and passed it through;
88
+ // `dns service` did not, which made a service-managed bridge the one way to
89
+ // run Moshpit where HTTPS could never work.
90
+ if (proxy) args.push("--proxy", proxy);
83
91
  const exec = [execPath, ...args].map((part) => (/\s/.test(part) ? JSON.stringify(part) : part)).join(" ");
84
92
 
85
93
  const lines = [
package/src/dns.mjs CHANGED
@@ -1664,6 +1664,37 @@ export function dnsmasqCatchAllConf({ host = DEFAULT_HOST, port = DEFAULT_PORT }
1664
1664
  * Repeatable and comma-separated both work. `address#port` matches resolv.conf
1665
1665
  * and dnsmasq rather than inventing a third spelling.
1666
1666
  */
1667
+ /**
1668
+ * Which name to detect the pinned-TLS proxy with.
1669
+ *
1670
+ * The probe is a TLS handshake with the name in SNI, and the proxy can only
1671
+ * present a certificate for a name that really exists — it fetches the
1672
+ * registry pin to mint one. A synthesised `a.<ending>` is never real, so the
1673
+ * handshake yields no certificate and a proxy that is installed, trusted and
1674
+ * listening reports as absent. Measured against a running proxy:
1675
+ *
1676
+ * a.moshpit -> no certificate
1677
+ * a.2600 -> no certificate
1678
+ * alt.2600 -> issuer=CN=Moshpit Local CA
1679
+ *
1680
+ * No registry endpoint lists names, so a real one cannot be discovered here.
1681
+ * `--proxy-probe <name>` supplies it. A bare flag is a typo rather than a
1682
+ * request to probe with nothing, and is reported instead of silently falling
1683
+ * back to the synthetic name that cannot work.
1684
+ */
1685
+ export function proxyProbeFromArgs(args = [], claimed = []) {
1686
+ const at = args.indexOf("--proxy-probe");
1687
+ if (at >= 0) {
1688
+ const value = args[at + 1];
1689
+ if (value === undefined || value.startsWith("--")) return { name: null, invalid: true };
1690
+ return { name: value, invalid: false };
1691
+ }
1692
+ // The historical default. Kept because it is right on a machine whose proxy
1693
+ // serves every ending, and because removing it would turn "detected nothing"
1694
+ // into "refused to look".
1695
+ return { name: claimed[0] ? `a.${claimed[0]}` : null, invalid: false };
1696
+ }
1697
+
1667
1698
  export function upstreamsFromArgs(args = []) {
1668
1699
  const servers = [];
1669
1700
  const invalid = [];
@@ -2377,6 +2408,8 @@ const USAGE = `moshcode dns — resolve Moshpit names on this machine
2377
2408
  running across reboots; --write installs and
2378
2409
  starts it, --system for a system unit rather
2379
2410
  than this user's, --remove takes it away
2411
+ --proxy-probe NAME a real Moshpit name to detect
2412
+ the pinned-TLS proxy with (it cannot serve a made-up one)
2380
2413
 
2381
2414
  moshcode dns filter block ads, trackers, malware and phishing at the
2382
2415
  resolver — \`moshcode dns filter help\` for the verbs
@@ -2788,7 +2821,59 @@ export async function dnsCommand(args = [], out = console.log, deps = {}) {
2788
2821
  const upstreams = upstreamsFromArgs(rest).servers.length
2789
2822
  ? upstreamsFromArgs(rest).servers
2790
2823
  : await discoverUpstreams();
2791
- const unit = serviceUnit({ system, entry: cliEntry(), port, registryBase, upstreams });
2824
+ // Probed the same way `enable` probes it, and for the same reason: a name
2825
+ // that resolves but cannot complete a TLS handshake reads as broken to the
2826
+ // person who typed the URL. Asked now, while this command can ask; the unit
2827
+ // it writes runs at boot and has no way to find out later.
2828
+ // The probe is a TLS handshake with the name in SNI, and the proxy can only
2829
+ // present a certificate for a name that actually exists — it fetches the
2830
+ // registry pin to make one. A synthesised `a.<ending>` is never a real name,
2831
+ // so the handshake yields no certificate and a proxy that is installed,
2832
+ // trusted and listening reports as absent. Measured on a running proxy:
2833
+ //
2834
+ // a.moshpit -> no certificate
2835
+ // a.2600 -> no certificate
2836
+ // alt.2600 -> issuer=CN=Moshpit Local CA
2837
+ //
2838
+ // There is no registry endpoint that lists names, so the tool cannot find a
2839
+ // real one by itself. `--proxy-probe <name>` supplies one. The issuer check
2840
+ // still runs against it: naming a probe says which name to ask about, never
2841
+ // that a proxy is there.
2842
+ let proxy = null;
2843
+ if (!rest.includes("--no-proxy")) {
2844
+ const explicit = proxyProbeFromArgs(rest);
2845
+ if (explicit.invalid) {
2846
+ out("! --proxy-probe needs a Moshpit name, e.g. --proxy-probe blue.eggs");
2847
+ out("");
2848
+ }
2849
+ // The registry is only consulted when no name was given: fetching 18000
2850
+ // endings to build a probe that cannot work is pure cost.
2851
+ const claimed = explicit.name ? [] : await fetchTlds({ registryBase }).catch(() => []);
2852
+ const probeName = proxyProbeFromArgs(rest, claimed).name;
2853
+ if (probeName) {
2854
+ const local = await findLocalProxyImpl(probeName).catch(() => ({ found: false }));
2855
+ if (local.found) proxy = local.address.v4 || local.address.v6;
2856
+ }
2857
+ }
2858
+
2859
+ const unit = serviceUnit({ system, entry: cliEntry(), port, registryBase, upstreams, proxy });
2860
+
2861
+ if (proxy) {
2862
+ out(`ok pinned-TLS proxy on ${proxy}:${PROXY_PORT} — names will answer there, so https:// verifies`);
2863
+ out("");
2864
+ } else if (!rest.includes("--no-proxy")) {
2865
+ out("! no pinned-TLS proxy found on this machine");
2866
+ out(" names will answer their origin, and a stock client cannot verify those —");
2867
+ out(" https:// will fail even though the name resolves.");
2868
+ out("");
2869
+ out(" If it is not installed:");
2870
+ out(" curl -fsSL https://raw.githubusercontent.com/profullstack/moshpit-proxy/main/install.sh | sh");
2871
+ out("");
2872
+ out(" If it IS installed and listening, the probe used a name that does not");
2873
+ out(" exist — the proxy can only serve a certificate for a real one. Name one:");
2874
+ out(" moshcode dns service --proxy-probe <a.real.name> --write");
2875
+ out("");
2876
+ }
2792
2877
 
2793
2878
  if (!upstreams.length) {
2794
2879
  out("! no upstream nameservers found, and none given");