nixamp 0.7.3 → 0.7.4

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/dist/admin.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * or against a nixamp on a different machine entirely.
7
7
  */
8
8
  import { createApp, themes } from "@profullstack/hqtui";
9
- import { daemonUrl, readState } from "./daemon.js";
9
+ import { daemonUrl, isLoopbackTls, readState } from "./daemon.js";
10
10
  import { KEY_HEADER } from "./share.js";
11
11
  /** Where to point, from the flags or from the daemon that is running. */
12
12
  export function resolveTarget(argv) {
@@ -23,8 +23,13 @@ export function resolveTarget(argv) {
23
23
  if (state === null) {
24
24
  throw new Error("nixamp: no daemon is running. Start one with `nixamp daemon start`, or pass --url.");
25
25
  }
26
+ const url_ = daemonUrl(state);
27
+ // Talking to our own daemon, whose certificate names somewhere else. Nothing
28
+ // is in the way on loopback, so there is nothing for a certificate to prove.
29
+ if (isLoopbackTls(url_))
30
+ process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0";
26
31
  return {
27
- url: daemonUrl(state),
32
+ url: url_,
28
33
  key: key ?? state.key,
29
34
  // A state file written before 0.5.3 has no list; loopback stands in.
30
35
  links: state.urls ?? [{ label: "here", url: daemonUrl(state) }],
package/dist/daemon.d.ts CHANGED
@@ -48,6 +48,16 @@ export declare function status(): {
48
48
  };
49
49
  /** The URL an admin client should talk to. */
50
50
  export declare function daemonUrl(state: DaemonState): string;
51
+ /**
52
+ * Whether this address is our own machine over TLS with a certificate that
53
+ * cannot possibly name it.
54
+ *
55
+ * A certificate proves you reached the host you asked for. Asking for loopback
56
+ * proves that already: nothing is in the way to impersonate. So a daemon with a
57
+ * certificate for some public name is still reachable at 127.0.0.1, and a
58
+ * client refusing to talk to it is protecting nobody from anything.
59
+ */
60
+ export declare function isLoopbackTls(url: string): boolean;
51
61
  /**
52
62
  * What `nixamp daemon start` and `nixamp daemon status` print, as lines, so it
53
63
  * can be tested without starting a daemon.
package/dist/daemon.js CHANGED
@@ -61,8 +61,37 @@ export function status() {
61
61
  }
62
62
  /** The URL an admin client should talk to. */
63
63
  export function daemonUrl(state) {
64
+ // A daemon serving https has a certificate for a name, and loopback is not
65
+ // that name. Asking it for https://localhost fails verification even though
66
+ // it is the same process on the same machine, which is how turning TLS on
67
+ // silently broke `nixamp admin` and `nixamp attach`. Prefer the address it
68
+ // was told to publish, which is the one the certificate is actually for.
69
+ const told = state.urls?.find((entry) => entry.label === "on the internet" && entry.url.startsWith("https://"));
70
+ if (told)
71
+ return told.url;
72
+ const scheme = state.urls?.[0]?.url.startsWith("https://") ? "https" : "http";
64
73
  const host = state.host === "0.0.0.0" || state.host === "::" ? "127.0.0.1" : state.host;
65
- return `http://${host.includes(":") ? `[${host}]` : host}:${state.port}`;
74
+ return `${scheme}://${host.includes(":") ? `[${host}]` : host}:${state.port}`;
75
+ }
76
+ /**
77
+ * Whether this address is our own machine over TLS with a certificate that
78
+ * cannot possibly name it.
79
+ *
80
+ * A certificate proves you reached the host you asked for. Asking for loopback
81
+ * proves that already: nothing is in the way to impersonate. So a daemon with a
82
+ * certificate for some public name is still reachable at 127.0.0.1, and a
83
+ * client refusing to talk to it is protecting nobody from anything.
84
+ */
85
+ export function isLoopbackTls(url) {
86
+ try {
87
+ const parsed = new URL(url);
88
+ if (parsed.protocol !== "https:")
89
+ return false;
90
+ return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "::1";
91
+ }
92
+ catch {
93
+ return false;
94
+ }
66
95
  }
67
96
  /**
68
97
  * How long it has been up, as a person would say it. Written here rather than
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nixamp",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "description": "It really whips the terminal's ass. A Winamp-shaped audio player for your terminal.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/admin.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  import { createApp, themes, type Container, type KeyEvent, type Theme } from "@profullstack/hqtui";
9
9
  import type { Color } from "@profullstack/hqtui";
10
10
  import type { Connection } from "./connections.ts";
11
- import { daemonUrl, readState } from "./daemon.ts";
11
+ import { daemonUrl, isLoopbackTls, readState } from "./daemon.ts";
12
12
  import { KEY_HEADER } from "./share.ts";
13
13
 
14
14
  interface Report {
@@ -59,8 +59,12 @@ export function resolveTarget(argv: string[]): AdminOptions {
59
59
  if (state === null) {
60
60
  throw new Error("nixamp: no daemon is running. Start one with `nixamp daemon start`, or pass --url.");
61
61
  }
62
+ const url_ = daemonUrl(state);
63
+ // Talking to our own daemon, whose certificate names somewhere else. Nothing
64
+ // is in the way on loopback, so there is nothing for a certificate to prove.
65
+ if (isLoopbackTls(url_)) process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0";
62
66
  return {
63
- url: daemonUrl(state),
67
+ url: url_,
64
68
  key: key ?? state.key,
65
69
  // A state file written before 0.5.3 has no list; loopback stands in.
66
70
  links: state.urls ?? [{ label: "here", url: daemonUrl(state) }],
package/src/daemon.ts CHANGED
@@ -95,8 +95,38 @@ export function status(): { running: boolean; state: DaemonState | null } {
95
95
 
96
96
  /** The URL an admin client should talk to. */
97
97
  export function daemonUrl(state: DaemonState): string {
98
+ // A daemon serving https has a certificate for a name, and loopback is not
99
+ // that name. Asking it for https://localhost fails verification even though
100
+ // it is the same process on the same machine, which is how turning TLS on
101
+ // silently broke `nixamp admin` and `nixamp attach`. Prefer the address it
102
+ // was told to publish, which is the one the certificate is actually for.
103
+ const told = state.urls?.find(
104
+ (entry) => entry.label === "on the internet" && entry.url.startsWith("https://"),
105
+ );
106
+ if (told) return told.url;
107
+
108
+ const scheme = state.urls?.[0]?.url.startsWith("https://") ? "https" : "http";
98
109
  const host = state.host === "0.0.0.0" || state.host === "::" ? "127.0.0.1" : state.host;
99
- return `http://${host.includes(":") ? `[${host}]` : host}:${state.port}`;
110
+ return `${scheme}://${host.includes(":") ? `[${host}]` : host}:${state.port}`;
111
+ }
112
+
113
+ /**
114
+ * Whether this address is our own machine over TLS with a certificate that
115
+ * cannot possibly name it.
116
+ *
117
+ * A certificate proves you reached the host you asked for. Asking for loopback
118
+ * proves that already: nothing is in the way to impersonate. So a daemon with a
119
+ * certificate for some public name is still reachable at 127.0.0.1, and a
120
+ * client refusing to talk to it is protecting nobody from anything.
121
+ */
122
+ export function isLoopbackTls(url: string): boolean {
123
+ try {
124
+ const parsed = new URL(url);
125
+ if (parsed.protocol !== "https:") return false;
126
+ return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "::1";
127
+ } catch {
128
+ return false;
129
+ }
100
130
  }
101
131
 
102
132
  /**
package/web/dist/sw.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /* nixamp service worker — generated, do not edit */
2
- const CACHE = "nixamp-1788949147610";
2
+ const CACHE = "nixamp-1788949719455";
3
3
  const PRECACHE = [
4
4
  "/",
5
5
  "/apple-touch-icon.png",