@mmmbuto/nexuscrew 0.9.4 → 0.9.6
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/CHANGELOG.md +138 -0
- package/frontend/dist/assets/index-MdL-ZvnH.js +93 -0
- package/frontend/dist/assets/index-forYfgKZ.css +32 -0
- package/frontend/dist/index.html +2 -2
- package/frontend/dist/version.json +1 -1
- package/lib/cli/init.js +22 -2
- package/lib/cli/pidfile.js +389 -16
- package/lib/cli/runtime-lifecycle.js +11 -0
- package/lib/cli/service.js +11 -3
- package/lib/cli/stable-alias.js +117 -0
- package/lib/nodes/commands.js +8 -2
- package/lib/nodes/health.js +1 -0
- package/lib/nodes/peer-transitions.js +10 -4
- package/lib/nodes/pubkey-format.js +69 -0
- package/lib/nodes/tunnel-supervisor.js +271 -30
- package/lib/nodes/tunnel.js +166 -0
- package/lib/proxy/panel-auth.js +45 -5
- package/lib/settings/pairing-coordinator.js +34 -1
- package/lib/settings/routes.js +11 -0
- package/lib/update/runner.js +10 -3
- package/package.json +1 -1
- package/frontend/dist/assets/index-CbMytNmg.css +0 -32
- package/frontend/dist/assets/index-DFI1J-u5.js +0 -93
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,144 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to NexusCrew are tracked here.
|
|
4
4
|
|
|
5
|
+
## 0.9.6 — 2026-08-18 — "Causes With Names"
|
|
6
|
+
|
|
7
|
+
The previous release fixed a blank panel, a service that hung on a terminal, and
|
|
8
|
+
a message that named the wrong cause. They were three symptoms of one defect: a
|
|
9
|
+
value that collapsed different states into `null`, and a low level composing
|
|
10
|
+
sentences about facts it could not know. This release fixes the defect itself.
|
|
11
|
+
|
|
12
|
+
- **Resolving a public key now has five distinct outcomes instead of one
|
|
13
|
+
`null`**: derived, no identity configured, identity present but the actual key
|
|
14
|
+
unknown, the tool itself missing, and encrypted-or-unreadable. They are data,
|
|
15
|
+
not prose — nothing downstream has to parse a sentence to learn what happened.
|
|
16
|
+
The pairing response carries the outcome beside the text, so a consumer has
|
|
17
|
+
the fact rather than an interpretation of it.
|
|
18
|
+
|
|
19
|
+
Previously all five answered `null`, and the message that followed had to
|
|
20
|
+
guess: "I cannot derive the public key" was shown for a missing file, a
|
|
21
|
+
missing binary and an encrypted key alike, and only one of the three had
|
|
22
|
+
anything to do with a passphrase.
|
|
23
|
+
|
|
24
|
+
- **The sentences are now composed in one place.** A single formatter turns each
|
|
25
|
+
outcome into a cause and an action. Success stays silent — there is nothing to
|
|
26
|
+
say when a thing works — and an outcome the formatter does not recognise is
|
|
27
|
+
**named** rather than swallowed, because a silent default rebuilds exactly the
|
|
28
|
+
collapse this removes.
|
|
29
|
+
|
|
30
|
+
- **The boot service no longer points at a path that expires.** The generated
|
|
31
|
+
unit and launch agent recorded the interpreter path of the moment, which on
|
|
32
|
+
package managers that version their install directory disappears at the next
|
|
33
|
+
upgrade — and the entry point was versioned too, so fixing only the first
|
|
34
|
+
produced a service that looked repaired and failed identically. Both are now
|
|
35
|
+
resolved to a stable alias when one exists and points at the very same file;
|
|
36
|
+
when none exists the current path is written **and the fact is declared**,
|
|
37
|
+
because a path that will expire silently is worse than a warning.
|
|
38
|
+
|
|
39
|
+
- **Unreachable peers are no longer polled at a fixed rate forever.** Three
|
|
40
|
+
stopped peers used to fill the console until the service worker gave up. The
|
|
41
|
+
interval now backs off with a ceiling and recovers as soon as the peer answers
|
|
42
|
+
— the recovery is what the test pins, because a backoff that never resets is a
|
|
43
|
+
sentence rather than a protection.
|
|
44
|
+
|
|
45
|
+
And three different answers stopped looking alike: a peer that is absent, one
|
|
46
|
+
that is present and refuses, and one missing that route each say so, with the
|
|
47
|
+
action they imply — wait, grant the permission **on the remote node**, update
|
|
48
|
+
that node.
|
|
49
|
+
|
|
50
|
+
- **The panel window can be moved and resized**, remembers where it was, and
|
|
51
|
+
can be recentred when it gets lost. Below the mobile breakpoint it stays full
|
|
52
|
+
screen and dragging is genuinely disabled rather than merely hidden.
|
|
53
|
+
|
|
54
|
+
- **The panel no longer reports "ready" and then stops watching.** It now
|
|
55
|
+
distinguishes a frame that loaded from one that failed, using the events the
|
|
56
|
+
element actually emits — with the limit stated where it matters: a `load`
|
|
57
|
+
event fires for an error page too, so it means "a response arrived", never
|
|
58
|
+
"the panel is alive".
|
|
59
|
+
|
|
60
|
+
## 0.9.5 — 2026-08-17 — "Measured From Where It Breaks"
|
|
61
|
+
|
|
62
|
+
Almost everything in this release was already broken before it, and none of it
|
|
63
|
+
was found by the tests that were supposed to cover it. The pattern repeats often
|
|
64
|
+
enough to be worth naming: a check that runs from a convenient place reports
|
|
65
|
+
success for a mechanism it never actually crossed. The panel tunnel was declared
|
|
66
|
+
ready by measuring a local bind and never a request through it. A public key was
|
|
67
|
+
called valid by our own parser rather than by the program that would authenticate
|
|
68
|
+
with it. A test that promised "leaves no orphans" left one. Each of them was
|
|
69
|
+
green for weeks.
|
|
70
|
+
|
|
71
|
+
- **The per-cell panel could not load, and the interface said it was ready.**
|
|
72
|
+
The viewing cookie was issued with `Path=/api/panel/<cell>`, but the dedicated
|
|
73
|
+
panel port serves `/panel/<cell>` — the control-plane prefix is not there. A
|
|
74
|
+
browser stores that cookie and never sends it back: the first request enters
|
|
75
|
+
with its ticket, and every sub-resource of the panel is refused, as is the
|
|
76
|
+
WebSocket upgrade, which looks for exactly that cookie. The panel stays blank.
|
|
77
|
+
|
|
78
|
+
The cookie path now follows the mount the request actually entered through,
|
|
79
|
+
taken from the router rather than from a constant, with a strict allow-list —
|
|
80
|
+
an unrecognised mount fails closed instead of guessing a scope, and it is
|
|
81
|
+
checked *before* the single-use ticket is spent, so a misconfiguration cannot
|
|
82
|
+
burn a ticket belonging to someone who did nothing wrong.
|
|
83
|
+
|
|
84
|
+
Present since the panel port was separated. It survived because a neighbouring
|
|
85
|
+
function in the same file already handled *both* prefixes: someone had thought
|
|
86
|
+
of the case, in one place out of two.
|
|
87
|
+
|
|
88
|
+
- **Deriving a public key could hang the foreground service for five seconds.**
|
|
89
|
+
Reading a key ran `ssh-keygen -y -f`, with stdin closed and the askpass helper
|
|
90
|
+
disabled. Neither matters: OpenSSH does not read a passphrase from stdin, it
|
|
91
|
+
opens `/dev/tty`. Against an encrypted key on a terminal, the call waited out
|
|
92
|
+
its whole timeout — and the pairing path calls it synchronously, so the event
|
|
93
|
+
loop stopped with it.
|
|
94
|
+
|
|
95
|
+
Now the prompt is forced away from the terminal and onto a helper that cannot
|
|
96
|
+
run, so the attempt fails in a tenth of a second instead of five seconds.
|
|
97
|
+
Below OpenSSH 8.4 that control does not exist and the timeout remains the net:
|
|
98
|
+
slow, but never stuck. The limit is stated in the code rather than assumed.
|
|
99
|
+
|
|
100
|
+
This one was invisible by construction: automated runs have no controlling
|
|
101
|
+
terminal, so `/dev/tty` does not exist and the defect cannot appear. The test
|
|
102
|
+
now **brings its own** terminal rather than skipping, and the probe *measures*
|
|
103
|
+
whether it got one instead of trusting that it did.
|
|
104
|
+
|
|
105
|
+
- **The panel tunnel was authorised to fail.** The generated `authorized_keys`
|
|
106
|
+
line restricted forwarding to the node port and never listed the panel port,
|
|
107
|
+
which had been separated in an earlier release — so pairing negotiated the
|
|
108
|
+
port, reserved the local side, wrote the line, and the server refused the
|
|
109
|
+
channel. "Forward ready" was reported after checking the local bind only,
|
|
110
|
+
which is the half that always succeeds. The channel is now exercised, the
|
|
111
|
+
probe gives up on a budget instead of retrying forever, and when a key cannot
|
|
112
|
+
be determined the product no longer promises a line it cannot produce.
|
|
113
|
+
|
|
114
|
+
- **A public key is now derived from the private one, not read from beside it.**
|
|
115
|
+
Three rounds of hardening had gone into validating the `.pub` file, which
|
|
116
|
+
proves that a file contains *a* valid key — never that it is *the* key for
|
|
117
|
+
that identity. Deriving it removes the question. Relatedly, whether a key is
|
|
118
|
+
acceptable is now decided by the program that will use it, not by our parser.
|
|
119
|
+
|
|
120
|
+
- **Replacing a degraded forwarding channel no longer leaves stray processes.**
|
|
121
|
+
Stopping and waiting now happen at a single point before any replacement.
|
|
122
|
+
The test that certified this used to leave an orphan of its own, and its
|
|
123
|
+
guard identified processes by number rather than by what they were — a
|
|
124
|
+
recycled id could satisfy it.
|
|
125
|
+
|
|
126
|
+
- **A pidfile declares its identity at birth**, its schema marker is one-way,
|
|
127
|
+
and an obstacle to writing that marker now closes the window instead of
|
|
128
|
+
holding it open forever. A refusal that was uniform across two different
|
|
129
|
+
causes used to break updates; the two are separated.
|
|
130
|
+
|
|
131
|
+
- **A remote cell's row resolves against its own node's sessions.** Both the
|
|
132
|
+
sidebar row and the popup could show data from a same-named local session.
|
|
133
|
+
|
|
134
|
+
### Notes for anyone reading the tests
|
|
135
|
+
|
|
136
|
+
Several fixes here are in tests that were passing while proving nothing: a guard
|
|
137
|
+
that matched too loosely to ever fail, a timing assertion that measured a
|
|
138
|
+
stopwatch instead of the signal it cared about, a cleanup check that read a file
|
|
139
|
+
it had already deleted, and one that could not fail because the shell it relied
|
|
140
|
+
on rejected the syntax it used. If a test has never been seen red for the right
|
|
141
|
+
reason, it has not been seen at all.
|
|
142
|
+
|
|
5
143
|
## 0.9.4 — 2026-08-17 — "The Interface Was Last Week's"
|
|
6
144
|
|
|
7
145
|
- **The published interface is the one this version was built from.** 0.9.3
|