@mmmbuto/nexuscrew 0.9.66 → 0.9.68
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 +21 -1
- package/docs/NODES.md +14 -3
- package/frontend/dist/assets/index-Bq57nMaf.js +169 -0
- package/frontend/dist/index.html +1 -1
- package/frontend/dist/version.json +1 -1
- package/lib/audio/dispatch.js +14 -1
- package/lib/audio/routes.js +2 -2
- package/lib/live-host/federation.js +10 -0
- package/lib/live-host/routes.js +12 -2
- package/lib/nodes/reverse-slot-listeners.js +4 -1
- package/lib/nodes/store.js +10 -2
- package/lib/proxy/federation.js +266 -14
- package/lib/proxy/node-proxy.js +86 -3
- package/lib/server.js +98 -7
- package/lib/ws/bridge.js +17 -1
- package/package.json +1 -1
- package/frontend/dist/assets/index-DhZOap1-.js +0 -169
package/CHANGELOG.md
CHANGED
|
@@ -2,9 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to NexusCrew are tracked here.
|
|
4
4
|
|
|
5
|
+
## 0.9.68 — 2026-10-14
|
|
6
|
+
|
|
7
|
+
- **The web terminal no longer goes black on the first mode query from the session.** @xterm/xterm 6.0.0's `requestMode` (the DECRQM handler, `CSI ? Ps $ p` and `CSI Ps $ p`) ships a compiled const-enum emulation that this repo's build pipeline rewrites into an assignment to an identifier nothing declares: in the shipped ES module the first DECRQM written to the terminal throws `ReferenceError: n is not defined`, the write buffer never drains again, and the terminal stops rendering while the WebSocket stays healthy — the black terminal of the phone-hosted cells, seen from any client. The web terminal now registers its own CSI handlers for both DECRQM forms ahead of the defective built-in, answers DECRPM (`Pm=0`, not recognized) over the input channel, and swallows the sequence; a view-only terminal swallows it without answering, like any other input. No published release of @xterm/xterm changes the pattern (latest is 6.0.0; the 6.1.0-beta line still carries it), so the shield lives in the app and the test that pins the rewritten bundle fails loudly the day an upgrade makes it unnecessary.
|
|
8
|
+
- **A parser exception can no longer take the output stream down silently.** The writes that carry the channel's output to the terminal go through a guarded helper: an exception is logged to the console, together with what was discarded, instead of escaping into the message handler and leaving a live channel with a terminal that never draws again.
|
|
9
|
+
- **A reconnect through a hub-held federated attach no longer leaves the terminal black.** When a client drops without a close frame (a mobile network cut, a killed tab) and reconnects while the hub still holds the upstream websocket, the returning browser's attach frame reached a bridge that was already attached on that websocket and was rejected with close 1002 "already attached", putting the client in a reconnect loop. A duplicate attach for the same session on an already-attached websocket is now an idempotent re-ack (the payload is ignored: no readonly downgrade, no size-policy change, no second pty); a duplicate attach for a different session is still a protocol violation closed with 1002.
|
|
10
|
+
|
|
11
|
+
## 0.9.67 — 2026-10-10
|
|
12
|
+
|
|
13
|
+
- **A host-only peer is closed to every legacy door.** The audience gate of 0.9.66 covered the peer-facing protocol only; the older paths that re-enter the local API with the node's own authority did not look at it. They now do, and the owner's view and the trusted peers keep their behaviour.
|
|
14
|
+
- **No unauthenticated federated panel route towards, or through, a host-only peer.** The panel route that lets a browser frame load a remote panel without a header is no longer opened for a route that names a host-only peer at any hop, for a request that carries any Authorization header, or for a request that arrived on a reverse (peer-bound) listener; those go through the normal token check. A host-only peer has no panel and no Live host: the panel, its ticket and every Live-host resource towards it are refused, over HTTP and WebSocket.
|
|
15
|
+
- **Audience is checked on every hop of the request, not only on the last.** A peer outside the audience can no longer reach a host-only peer through an audience member, and the topology served to an audience member on behalf of an outsider does not list it; the topology answer to a peer is decided again after the fan-out wait, so a grant, an audience or a publication that ended meanwhile removes the leaf, and a reader that was not entitled to a host-only peer when it asked is not handed it because the publication ended during the wait (the same holds for the local topology, the Live host and the audio dispatcher).
|
|
16
|
+
- **What a host-only peer answers cannot act on the origin of the app.** HTTP answers (the routes and the legacy `/node/<name>` proxy) lose their cookies, `Clear-Site-Data`, CSP and the headers that redirect (`Location`, `Content-Location`, `Refresh`), and are served with `Content-Security-Policy: sandbox; default-src 'none'` and `nosniff`; any 3xx is refused with a 502, so a browser can no longer repeat the owner's call somewhere else with the owner's Authorization, and the app itself no longer follows redirects on a call that goes through a peer (remote API calls from the web app, `/api/route/*` and `/node/*`, to any peer, trusted or not): no API of this server redirects, so nothing changes in normal operation, while a peer that answers with a redirect gets an error instead of having the owner's Authorization replayed. WebSocket upgrade answers (the federated route and `/node/<name>/ws`) are passed along only when the first status is a `101`; the head is read the way the most lenient parser would read it (bare line feeds, folded lines, odd header names), cookies, `Clear-Site-Data` and CSP are dropped from it, anything malformed, interim or not a `101` is refused, and the frames that follow are untouched. No CSP or `nosniff` is added to a WebSocket head (a `101` has no body). A frame whose route passes through a host-only peer is also sandboxed in an opaque origin (second lock: its panel is refused anyway); the frames of trusted remote cells are unchanged.
|
|
17
|
+
- **Open streams towards a host-only peer end on revocation.** Server-sent events, WebSockets and held (detached) terminal attaches are re-checked about once a second against the publication they were opened under (the mode still on, the same audience and generation), the operator grant, the credentials and identity of both peers and the store; a stream whose authorization changed in any of these ways is closed, and a held attach cannot be resumed. Streams already open through a peer when its publication is turned on are closed too (the event streams and terminals that were opened under plain sharing).
|
|
18
|
+
- **A federated `/audio/speak` or `/audio/stop` that terminates here is not re-sent to a host-only peer.** The target actually resolved (instance id and every hop of the route) is checked just before the request leaves. Audience members keep their direct route.
|
|
19
|
+
- **Live host never designates, queries or bridges to a host-only peer.** A designation recorded before the mode was turned on is unusable and is not shown to a reader outside the audience.
|
|
20
|
+
- **Topology, configuration, diagnostics and decks do not name a host-only peer to a federated reader outside its audience.** The reader is identified only by the verified hop proof (HMAC over method, path and chain); a header that does not verify hides the peer. The decision is taken on the final result, after any wait, and covers cached and stale entries, whole diagnostic records and the tiles and windows of a deck that point at the peer; a reader that cannot see those references cannot change or delete the deck either, so it cannot save back the filtered copy it was served.
|
|
21
|
+
- **Two opt-ins in the same second no longer collide.** The protected backup taken before each opt-in gets a distinct name when another backup of the same bytes exists, instead of failing the command.
|
|
22
|
+
- **Behaviours worth knowing.** An upgrade answer from a host-only peer that is refused shows up as a WebSocket closed with code 4402 on the federated route and as a 502 on `/node/<name>/ws`; a 304 from a host-only peer is answered with a 502 like any 3xx; a refused change to a deck that holds references the reader cannot see says only that the deck cannot be changed from this node; the `Link` header of a host-only peer is not stripped.
|
|
23
|
+
- **The 0.9.66 note about older owners is corrected.** An ASK that arrived with an origin cell attested on this node stays dismissible even with an older owner; any other card cannot be dismissed locally and answers `cell-unresolved`.
|
|
24
|
+
|
|
5
25
|
## 0.9.66 — 2026-10-10
|
|
6
26
|
|
|
7
|
-
- **An imported ASK from a cell with a custom tmux session can be dismissed on this node.** The local dismissal looked the cell up by decoding the session name, which only works for `cloud-*` names, so an ASK from a session such as `lab-Dev` answered 404 "unknown or invisible ask" although the node holds the grants and the owner publishes the ASK. The owner now publishes, with each ASK of its snapshot, the cell it resolved through its own definitions; this node keeps it (from the snapshot, from the cell-scoped live envelope, or from the attested origin of a pushed ASK), validates it at the authenticated ingress, binds it to the owner and to the generation of the ASK, and uses it before the name. A cell is never guessed from a name, never read from this node's own definitions and never accepted from the request. A card persisted by an older release with no authenticated cell is refused with `cell-unresolved` instead of a generic 404, and an answer in flight or of uncertain delivery still blocks the dismissal. Non-canonical names resolve
|
|
27
|
+
- **An imported ASK from a cell with a custom tmux session can be dismissed on this node.** The local dismissal looked the cell up by decoding the session name, which only works for `cloud-*` names, so an ASK from a session such as `lab-Dev` answered 404 "unknown or invisible ask" although the node holds the grants and the owner publishes the ASK. The owner now publishes, with each ASK of its snapshot, the cell it resolved through its own definitions; this node keeps it (from the snapshot, from the cell-scoped live envelope, or from the attested origin of a pushed ASK), validates it at the authenticated ingress, binds it to the owner and to the generation of the ASK, and uses it before the name. A cell is never guessed from a name, never read from this node's own definitions and never accepted from the request. A card persisted by an older release with no authenticated cell is refused with `cell-unresolved` instead of a generic 404, and an answer in flight or of uncertain delivery still blocks the dismissal. Non-canonical names resolve when the owner runs this release. With an older owner, canonical names decode as before, and an ASK that arrived with an origin cell attested on this node (a legacy card, even with the owner offline) stays dismissible too; any other card cannot be dismissed locally (the node answers `cell-unresolved`).
|
|
8
28
|
- **A remote peer can be published one way to your own admin nodes.** `nexuscrew nodes publish-remote <peer> on --audience <instanceId,...>` (and `off`, `status`) makes a peer you reach over your private `-L` tunnel visible and operable to an explicit audience of your operator nodes, while the peer reaches nothing of yours: no peer, no route, no topology, no protocol endpoint but the health probe. It is separate from Share and never restarts SSH, opens a reverse channel or touches the reverse pool.
|
|
9
29
|
- **Turning it on zeroes every grant towards the peer in the same save, and widening them is refused while it is on.** Cell scope none; events, node events, files, ASK replies, operator, panel and live host off; `eventsReceive` off. `off` ends the publication and leaves the grants at zero.
|
|
10
30
|
- **The topology announces the peer as a leaf, only to the audience.** What the peer reports about its own children is never requested, relayed or cached, and entries cached before the mode was turned on are purged.
|
package/docs/NODES.md
CHANGED
|
@@ -121,9 +121,20 @@ nexuscrew nodes publish-remote <name|nodeId> status [--json]
|
|
|
121
121
|
(`nodes.json.pre-publish-remote-<time>-<sha>.bak`, mode 0600). Older stores
|
|
122
122
|
keep loading unchanged. A release older than 0.9.66 cannot read a schema 4
|
|
123
123
|
store (the schema is closed): going back means restoring that backup.
|
|
124
|
-
-
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
- Streams opened towards the peer by a reader (event streams, terminals, held
|
|
125
|
+
terminal attaches) are re-checked about once a second against the audience, the
|
|
126
|
+
operator grant, the publication state, the identity of both nodes and the store:
|
|
127
|
+
after `off`, an audience change or a revoked grant they are closed within a couple
|
|
128
|
+
of seconds, and a held attach cannot be resumed. Read it as a short, bounded
|
|
129
|
+
delay, not as an instant cut. (Before 0.9.67 they stayed open.)
|
|
130
|
+
- The peer has **no panel and no Live host**: the panel, its ticket and every
|
|
131
|
+
Live-host resource towards it are refused, and a Live designation recorded
|
|
132
|
+
before the mode was turned on stops being used. What the peer answers is served
|
|
133
|
+
without its cookies, `Clear-Site-Data` and CSP, sandboxed and with `nosniff`.
|
|
134
|
+
A federated audio command that terminates here is not re-sent to it, and
|
|
135
|
+
topology, configuration and diagnostics served to a federated reader outside
|
|
136
|
+
the audience do not name it. The audience is checked on every hop of a request,
|
|
137
|
+
not only on the last one.
|
|
127
138
|
- A reader's topology cache is emptied by the **next authoritative answer** from
|
|
128
139
|
this installation. A reader that cannot reach it keeps showing the peer, marked
|
|
129
140
|
stale, until then; nothing is pushed to it.
|