@mmmbuto/nexuscrew 0.9.16 → 0.9.18

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.
Files changed (68) hide show
  1. package/CHANGELOG.md +157 -3
  2. package/docs/ALIBABA_TOKEN_PLAN.md +70 -0
  3. package/docs/AUDIO_SHARE.md +119 -0
  4. package/docs/CELL_PANEL.md +156 -0
  5. package/docs/CONFIGURATION.md +122 -0
  6. package/docs/FLEET.md +222 -0
  7. package/docs/INSTALLATION.md +128 -0
  8. package/docs/LIVE_PROMPT.md +110 -0
  9. package/docs/MCP.md +151 -0
  10. package/docs/NODES.md +214 -0
  11. package/docs/NOTIFICATIONS.md +101 -0
  12. package/docs/OPERATIONS.md +114 -0
  13. package/docs/README.md +55 -0
  14. package/docs/SECURITY.md +185 -0
  15. package/docs/VL_MICRO_NODES.md +107 -0
  16. package/docs/img/fleet-deck-desktop.png +0 -0
  17. package/docs/img/session-mobile.png +0 -0
  18. package/docs/live-prompt-templates/LIVE_PROMPT.en.md +67 -0
  19. package/docs/live-prompt-templates/LIVE_PROMPT.es.md +70 -0
  20. package/docs/live-prompt-templates/LIVE_PROMPT.it.md +67 -0
  21. package/frontend/dist/assets/{index-CwsGpQwK.js → index-DmpQoCsc.js} +1 -1
  22. package/frontend/dist/index.html +1 -1
  23. package/frontend/dist/version.json +1 -1
  24. package/lib/auth/middleware.js +1 -1
  25. package/lib/cells/scope-guard.js +2 -2
  26. package/lib/cli/commands.js +7 -7
  27. package/lib/cli/fleet-service.js +2 -2
  28. package/lib/cli/pidfile.js +15 -15
  29. package/lib/cli/service.js +2 -2
  30. package/lib/cli/stable-alias.js +1 -1
  31. package/lib/files/routes.js +2 -2
  32. package/lib/fleet/builtin.js +6 -6
  33. package/lib/fleet/catalogs/zai-responses.json +82 -0
  34. package/lib/fleet/cell-exec.js +4 -4
  35. package/lib/fleet/cell-lease-server.js +17 -17
  36. package/lib/fleet/definitions.js +28 -17
  37. package/lib/fleet/launch-broker.js +3 -3
  38. package/lib/fleet/lease-client.js +1 -1
  39. package/lib/fleet/managed.js +86 -26
  40. package/lib/fleet/prompt-delivery.js +1 -1
  41. package/lib/fleet/runtime.js +3 -3
  42. package/lib/live-host/bridge.js +3 -3
  43. package/lib/live-host/routes.js +1 -1
  44. package/lib/mcp/server.js +7 -8
  45. package/lib/mcp/tools.js +1 -1
  46. package/lib/nodes/commands.js +2 -2
  47. package/lib/nodes/health.js +1 -1
  48. package/lib/nodes/identity.js +10 -13
  49. package/lib/nodes/store.js +2 -2
  50. package/lib/nodes/tunnel-supervisor.js +7 -7
  51. package/lib/nodes/tunnel.js +10 -11
  52. package/lib/notify/asks.js +4 -4
  53. package/lib/notify/persist.js +1 -1
  54. package/lib/notify/push.js +4 -4
  55. package/lib/notify/routes.js +12 -12
  56. package/lib/proxy/node-proxy.js +4 -4
  57. package/lib/proxy/panel-proxy.js +2 -2
  58. package/lib/pty/attach.js +1 -1
  59. package/lib/server.js +3 -3
  60. package/lib/settings/pairing-coordinator.js +1 -1
  61. package/lib/settings/routes.js +4 -4
  62. package/lib/tmux/lifecycle.js +4 -4
  63. package/lib/tmux/preview.js +1 -1
  64. package/lib/update/runner.js +9 -9
  65. package/lib/vl-nodes/broker.js +1 -1
  66. package/lib/ws/bridge.js +1 -1
  67. package/package.json +20 -2
  68. package/frontend/index.html +0 -20
package/CHANGELOG.md CHANGED
@@ -2,6 +2,133 @@
2
2
 
3
3
  All notable changes to NexusCrew are tracked here.
4
4
 
5
+ ## 0.9.18 — 2026-09-04
6
+
7
+ ### Upstream Rust 0.153.2
8
+
9
+ - **New upstream capabilities.** The GPT-6-Astra model catalogue is included,
10
+ hook activity is visible in the TUI, and app-server supports asynchronous user
11
+ input with thread metadata.
12
+ - **Smoother daily use.** Vim undo/redo and app-server reconnect improve the
13
+ interactive experience, while `[tui].disable_paste_burst` is documented in
14
+ its canonical configuration location.
15
+ - **More context control.** Experimental context management is available for
16
+ evaluation alongside the upstream release changes.
17
+
18
+ ### Codex VL fixes
19
+
20
+ - **Loops stay responsive.** Failed `/loop` owner and delegate preconditions
21
+ report an error without terminating the TUI; `/loop` usage also lists the
22
+ delegate, strategy, delegation and owner commands.
23
+ - **Vivling and fleet metadata recover cleanly.** `manage_loops` sees its jobs
24
+ after resume, and Vivling accepts fenced or prefixed JSON replies from any
25
+ provider while model metadata is resolved by engine identity.
26
+ - **Quieter, clearer operation.** The expression planner no longer floods the
27
+ log database, terminal state reconciles after every terminal event, and Esc
28
+ interrupts a running task even during a long tool call.
29
+
30
+ ## 0.9.17 — 2026-09-01 — "The Package Reads Like The Product"
31
+
32
+ - **The installed files no longer carry the workshop's notes.** In the payload
33
+ of 0.9.16 and earlier, a reader of the installed package could find traces of
34
+ the environment that builds NexusCrew: device and node names, working paths
35
+ and hostnames, and the markers of the internal review process, sitting in
36
+ comments and metadata next to the product's own code. Nothing secret and
37
+ nothing functional — but writing addressed to the builders has no business
38
+ shipping to the users, and a version is never republished, so it stayed.
39
+ The payload has been swept: the installed tree now reads as the product
40
+ only. The sweep covers the classes above, and says so rather than claiming
41
+ every sentence of every comment was rewritten.
42
+
43
+ - **The promise is checked by tests that read the artifact, not by a
44
+ checklist.** The npm payload and the published git surface are both
45
+ inspected by the suite: every forbidden pattern is listed with its reason,
46
+ a file that cannot be read fails the check instead of being skipped, and a
47
+ pattern list emptied to make the suite pass fails with it. What the package
48
+ ships is now a tested property, not a step someone remembers to do.
49
+
50
+ - **The guides it points to travel with the package.** The README and three
51
+ built-in skills cite thirteen documentation files that an npm install never
52
+ contained: all twenty-six relative links were dead out of the box, sending
53
+ the reader to files that were not there. The thirteen guides now ship with
54
+ the package — those and no others.
55
+
56
+ ## 0.9.16 — 2026-08-28 — "The Cap Becomes One Hundred"
57
+
58
+ - **The fleet engine cap is 100, and the backup reads its own exports.** The
59
+ server accepted up to 100 engines and refused the 101st with an explicit
60
+ reason, but the fleet-backup parser on the frontend still enforced the old
61
+ cap of 24: `createFleetBackup` happily produced documents with 25–100
62
+ engines that `parseFleetBackup` then rejected as `invalid-format`. The
63
+ frontend copy is now aligned with the backend cap, and the parser names the
64
+ cap in its refusal instead of reporting a generic format error — a fleet of
65
+ any valid size round-trips.
66
+
67
+ - **WebSocket closures are classified, counted, and visible.** Inter-cell
68
+ sends and drop counters land in the observability surface, so a connection
69
+ that keeps falling can be told apart from one that was never stable.
70
+
71
+ - **The first health sample that says down means down.** Hysteresis only
72
+ protects a previously authoritative `transport: up`; without one, the first
73
+ failed probe is a real failure and is reported as such, instead of leaving
74
+ a dead node labelled unknown.
75
+
76
+ - **Catalog and skills surface sanitized**: internal cell names are gone from
77
+ skills, UI placeholders and comments.
78
+
79
+ ## 0.9.15 — 2026-08-27 — "The Terminals Survive A Flaky Connection"
80
+
81
+ - **Terminal panes no longer rebuild because of one late health probe.** A
82
+ single slow peer-health check declared a live node dead; the roster
83
+ emptied, and when the node came back every terminal window was rebuilt from
84
+ scratch — new socket, new attach, full repaint — whose traffic then caused
85
+ the next drop on an already queued line. Transient peer-health failures are
86
+ tolerated instead of tearing the node down.
87
+
88
+ - **PTYs survive transient terminal disconnects**, and resumes are authorized
89
+ before scope checks, so a reconnecting pane picks its session back up
90
+ instead of starting over.
91
+
92
+ - **The Live star distinguishes a started thread from an active one**, the
93
+ Live bridge declares its Fleet identity on initialize, and thread status
94
+ labels are guarded against Live claims.
95
+
96
+ - **MCP identity failures are no longer cached for life**: a blocked tool
97
+ names the cause and gets a fresh chance instead of being failed forever.
98
+
99
+ ## 0.9.14 — 2026-08-27 — "The Bundle Builds After The Bump"
100
+
101
+ - **The package installs.** 0.9.13 shipped with `frontend/dist/version.json`
102
+ still saying `0.9.12` while the server reported `0.9.13`, so nodes refused
103
+ to start with «incomplete installation: frontend and server do not match».
104
+ The bundle had been rebuilt before the version was raised and carried the
105
+ old number. No functional change against 0.9.13 — the same two fixes ship
106
+ again, with both sides agreeing on the version.
107
+
108
+ ## 0.9.13 — 2026-08-27 — "The Cells Stay When The Connection Drops"
109
+
110
+ - **One deadline per lease.** The supervisor's lease read the clock three
111
+ times for a single transaction: the persisted bound and the proof expiry
112
+ were taken at different instants, and a one-millisecond gap invalidated the
113
+ proof at the next check, dropping the lease and letting cells detach and
114
+ reattach on their own. The deadline is now computed once and the proof
115
+ expiry derives from it.
116
+
117
+ - **The roster distinguishes "could not ask" from "nothing is there".** A
118
+ skipped poll or a fallen node no longer empties the cell list; the last
119
+ known roster is kept until a real state replaces it.
120
+
121
+ - **A cell's identity is refused, not borrowed.** Identity resolution fails
122
+ closed instead of attributing a cell to another one, and a remote `vl`
123
+ variant that is missing its Ollama pair refuses naming the offending field
124
+ rather than silently falling back to local Ollama. A `vl` cell can live
125
+ outside the local Ollama too, and `vl.native` no longer overrides the
126
+ operator's choices.
127
+
128
+ - **New skill `cellforge`** (create, modify and audit a cell) and
129
+ **opencode-go**: `deepseek-v4-flash-vision-exp` added with real
130
+ measurements; the git publish surface now has the same gate the npm one had.
131
+
5
132
  ## 0.9.12 — 2026-08-22 — "Build After The Bump"
6
133
 
7
134
  - **Fixes 0.9.11, which would not start.** `frontend/dist/version.json` said
@@ -2199,7 +2326,7 @@ nothing anywhere said so.
2199
2326
  injection. Persistent offline queues, attachments and delegated capability workers remain
2200
2327
  explicitly deferred rather than being represented as implemented.
2201
2328
  - Tests: **770 total** (769 pass / 1 platform-dependent skip) in the isolated Node harness plus
2202
- 5 passing frontend component tests; production build PASS. Real Mac–hub–Pixel end-to-end
2329
+ 5 passing frontend component tests; production build PASS. Real nodo-A–hub–nodo-B end-to-end
2203
2330
  pairing and delivery remain an operator field test and are not represented as automated.
2204
2331
 
2205
2332
  ## 0.8.12 — 2026-07-13 — "Mobile Roster"
@@ -2223,7 +2350,7 @@ nothing anywhere said so.
2223
2350
  is disabled and explains that a Termux boot script still requires the Termux:Boot app to be
2224
2351
  installed and launched once.
2225
2352
  - Tests: **749 total** (748 pass / 1 platform-dependent skip), production build PASS, root and
2226
- frontend dependency audits clean. MacPixel–hub end-to-end pairing was not executed in this
2353
+ frontend dependency audits clean. nodo-Anodo-B–hub end-to-end pairing was not executed in this
2227
2354
  release gate.
2228
2355
 
2229
2356
  ## 0.8.11 — 2026-07-13 — "Tmux Survival"
@@ -2281,7 +2408,7 @@ nothing anywhere said so.
2281
2408
  HTTP-port move when paired peers depend on the configured endpoint.
2282
2409
  - Tests: **726 total** (725 pass / 1 platform-dependent skip), frontend production build PASS,
2283
2410
  dependency audit clean, isolated HOME clean, and package/public-tree verification required
2284
- before publication. Real MacPixel–hub interoperability remains an external follow-up and is
2411
+ before publication. Real nodo-Anodo-B–hub interoperability remains an external follow-up and is
2285
2412
  not represented as an automated test.
2286
2413
 
2287
2414
  ## 0.8.9 — 2026-07-12 — "Hydra Workspaces"
@@ -2309,6 +2436,11 @@ nothing anywhere said so.
2309
2436
 
2310
2437
  ## 0.8.8 — 2026-07-12 — "Reliable Composer"
2311
2438
 
2439
+ > **Never published.** Only a preparation commit exists for this version and
2440
+ > npm has no 0.8.8 — the next published release is 0.8.9. The entry stays
2441
+ > because the changes it describes are real; a reader looking for the version
2442
+ > on the registry should know why it is not there.
2443
+
2312
2444
  - The PWA composer now sends long and multiline drafts through xterm's explicit paste path,
2313
2445
  preserving the terminal application's bracketed-paste mode. Enter travels as a separate
2314
2446
  input only after the complete paste was accepted, so agent TUIs no longer absorb submission
@@ -2625,6 +2757,19 @@ Core rewrite from screenshot-and-poll to a faithful tmux client.
2625
2757
  backpressure cutoff, JSON errors with codes
2626
2758
  - token delivered via URL fragment (never logged), 0600 file, constant-time compare
2627
2759
 
2760
+ ## 0.2.5 — 2026-04-09 — "Discovery, Routes, Selector"
2761
+
2762
+ Reconstructed from the release commit and its tree: this entry was missing
2763
+ from the file at the time, and the history before 0.7.1 survives only in the
2764
+ `v0.2.5` tag and on npm.
2765
+
2766
+ - Engine discovery and explicit send routes in the runtime — the release
2767
+ commit's own summary — with the chat flow and the status bar wired to them.
2768
+ - A dedicated model selector interface: new `ModelSelector` component and
2769
+ styles wired into the chat.
2770
+ - A prebuilt SQLite library travels with the package instead of requiring a
2771
+ local build.
2772
+
2628
2773
  ## 0.2.4
2629
2774
 
2630
2775
  - added host-scoped tmux/session discovery so active session truth comes from the selected host
@@ -2649,3 +2794,12 @@ Core rewrite from screenshot-and-poll to a faithful tmux client.
2649
2794
  ## 0.2.1
2650
2795
 
2651
2796
  - older release line, now deprecated in favor of the current stable line
2797
+
2798
+ ## 0.2.0 — 2026-04-01 — "First Published"
2799
+
2800
+ - The first version of the 0.2 line to reach npm: a tmux-based cockpit with
2801
+ persistent sessions for Claude Code, Codex CLI, Gemini CLI and Qwen Code,
2802
+ with remote SSH host support.
2803
+ - Reconstructed from the published package's own description — no release
2804
+ notes from that date survive in this history. The line was later superseded
2805
+ by the 0.4 rewrite and deprecated.
@@ -0,0 +1,70 @@
1
+ # Alibaba Token Plan Personal engines
2
+
3
+ NexusCrew exposes three first-class managed profiles backed by one fixed local
4
+ credential name, `ALIBABA_CODE_API_KEY`:
5
+
6
+ - `claude.alibaba-token-plan`
7
+ - `codex-vl.alibaba-token-plan`
8
+ - `pi.alibaba-token-plan`
9
+
10
+ The credential value is resolved by the NexusCrew credential layer and is
11
+ passed only in the selected child process environment. Engine definitions,
12
+ generated extensions, argv, status responses and logs contain only the
13
+ environment-variable name, never its value. The default model is
14
+ `qwen3.8-max` for all three profiles.
15
+
16
+ ## Claude Code
17
+
18
+ - Anthropic-compatible base URL:
19
+ `https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic`
20
+ (Claude appends `/v1/messages`; the configured base must not include it).
21
+ - Authentication: `ANTHROPIC_AUTH_TOKEN` receives the locally resolved value;
22
+ `ANTHROPIC_API_KEY` is deliberately empty.
23
+ - Aliases: MODEL/SONNET/OPUS/FABLE = `qwen3.8-max`, HAIKU =
24
+ `qwen3.6-flash`, SUBAGENT = `qwen3.7-max`.
25
+ - Context: `983616`; effort: `xhigh`. Qwen Cloud documents qwen3.8 thinking
26
+ as always enabled and not user-disableable.
27
+ - State is isolated under
28
+ `~/.nexuscrew/claude-profiles/alibaba-token-plan/` with private modes; the
29
+ native Claude configuration is not modified and no credential is written
30
+ there.
31
+
32
+ ## Codex-VL
33
+
34
+ - Compatible base URL:
35
+ `https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1`.
36
+ - Provider wire API: `responses`; provider `env_key`:
37
+ `ALIBABA_CODE_API_KEY`. `OPENAI_API_KEY` is neither read nor forwarded by
38
+ this profile, and there is no OpenAI/PAYG fallback.
39
+ - Latest-Codex allowlist: `qwen3.8-max`, `qwen3.7-max`,
40
+ `qwen3.7-plus`, `qwen3.6-flash`. GLM and DeepSeek are intentionally absent.
41
+ - The bundled qwen3.8 catalog pins context `983616`, effective context `95%`,
42
+ reasoning levels `low/high/xhigh` with `xhigh` default, parallel tools off,
43
+ text+image input and original image detail.
44
+ - Offline compatibility target: `codex-vl 0.144.7`.
45
+
46
+ ## Pi
47
+
48
+ - Dedicated provider ID `alibaba-token-plan`, standard permissions only.
49
+ - A generated private extension refers to `$ALIBABA_CODE_API_KEY`; it contains
50
+ no credential value.
51
+ - Qwen response-capable models use Pi's `openai-responses` adapter. `glm-5.2`
52
+ and `deepseek-v4-pro` use Pi's `openai-completions` adapter, whose compatibility
53
+ path preserves `reasoning_content` in replayed assistant/tool turns.
54
+ - `qwen3.8-max` is reasoning-enabled, accepts text+image, uses context
55
+ `983616`, and launches with `--thinking xhigh`.
56
+ - Offline compatibility target: `pi 0.80.10`.
57
+
58
+ ## Verification boundary
59
+
60
+ Catalog, normalization, credential resolution, generated config/extension,
61
+ argv/env separation and local runtime loading are tested without network
62
+ access. No provider request is part of this candidate. Endpoint acceptance,
63
+ stream completion, vision input, tool round-trip and Token Plan quota behavior
64
+ remain post-reboot interactive smoke gates.
65
+
66
+ Primary references:
67
+
68
+ - https://docs.qwencloud.com/developer-guides/clients-and-developer-tools/claude-code
69
+ - https://docs.qwencloud.com/developer-guides/clients-and-developer-tools/codex
70
+ - https://docs.qwencloud.com/developer-guides/clients-and-developer-tools/openclaw
@@ -0,0 +1,119 @@
1
+ # Audio Share and native TTS
2
+
3
+ [← Documentation index](README.md)
4
+
5
+ Audio Share lets an authorized Fleet cell ask a **specific node with a real
6
+ speaker** to synthesize a short utterance. It is separate from browser spoken
7
+ notifications: browser speech remains a visible-and-focused PWA convenience,
8
+ while Audio Share is a node-local backend capability.
9
+
10
+ There is no cloud speech provider, central voice service, browser relay, or
11
+ `play arbitrary audio file` command in this feature.
12
+
13
+ ## Consent and routing are separate
14
+
15
+ Three independent controls must all permit delivery:
16
+
17
+ 1. **Share** publishes a node to the authorized transport topology.
18
+ 2. **Visibility** controls which peers may route through it.
19
+ 3. **Audio consent** is local to the node that would make sound, defaults to
20
+ off, and is never mutable through federation.
21
+
22
+ Turning on Share never grants permission to make a device speak. A target node
23
+ also applies its own ACL, `READONLY` state, rate limit, native-adapter
24
+ availability and local consent immediately before synthesis.
25
+
26
+ Configure consent under **Settings → Audio** on the device that owns the
27
+ speaker. The panel exposes only redacted capability metadata, an explicit
28
+ fixed-text local test, and a local Stop button. Stop remains available in
29
+ `READONLY`; speaking does not.
30
+
31
+ ## Exact targets and groups
32
+
33
+ `nc_speak` accepts one exact 32-character node instance ID. There is no
34
+ wildcard, `all`, label or route-name target.
35
+
36
+ Settings can also save local named groups of up to eight exact node IDs:
37
+
38
+ - **Primary + failover** is the default. It tries the ordered primary, waits up
39
+ to five seconds for a start acknowledgement, then tries the next endpoint on
40
+ `refused`, `unreachable`, or `unknown`.
41
+ - **Fan-out** is explicit. It tries all configured endpoints in parallel.
42
+
43
+ A group is only an origin-side delivery preference. It cannot override consent,
44
+ ACL, liveness, `READONLY`, or a local Stop on any member. A temporarily absent
45
+ member remains visible in group editing but is not treated as ready.
46
+
47
+ ## MCP commands
48
+
49
+ The caller must be an active local Fleet cell. NexusCrew signs the bridge
50
+ request with a node-local HMAC and resolves the real tmux/Fleet identity; a UI
51
+ bearer token, a request header, or a body field cannot declare a cell identity.
52
+
53
+ | Tool | Purpose |
54
+ |---|---|
55
+ | `nc_speak` | Speak to one exact node ID. |
56
+ | `nc_speak_status` | Read the caller-scoped receipt for one exact-target utterance. |
57
+ | `nc_speak_stop` | Stop an exact-target utterance, or all caller-owned utterances on that target. |
58
+ | `nc_speak_group` | Start a local named primary/failover or explicit fan-out group. |
59
+ | `nc_speak_group_status` | Read the caller-scoped per-endpoint group receipt. |
60
+ | `nc_speak_group_stop` | Prevent later failover candidates and send Stop to endpoints already admitted. |
61
+
62
+ Text is limited to 320 characters. Receipts intentionally never retain text,
63
+ language, voice, binary path, route, secret, or an aggregate success boolean.
64
+ Each endpoint is reported independently as:
65
+
66
+ ```text
67
+ refused | unreachable | accepted | spoken | unknown
68
+ ```
69
+
70
+ `spoken` means only that the native adapter confirmed it started synthesis. It
71
+ does **not** prove that a person heard anything: an output sink can be muted,
72
+ null, disconnected, or unavailable. `accepted` is transitional; the caller
73
+ queries status by immutable utterance ID for later state changes.
74
+
75
+ ## Native platform adapters
76
+
77
+ | Platform | Preferred native command | Important limitation |
78
+ |---|---|---|
79
+ | Android / Termux | `termux-tts-speak` | Requires Termux:API and its permissions; Android Doze can suspend the process. |
80
+ | macOS | `say` | Requires an active GUI/CoreAudio output path; command success is not an audibility test. |
81
+ | Linux | `espeak-ng`, then `spd-say` | Requires a real output sink; a null or headless sink can return exit 0 without sound. |
82
+
83
+ The implementation probes executable availability without playing sound. It
84
+ sends text on stdin whenever the command supports it; `spd-say` is a declared
85
+ fallback whose text must be placed in argv. Automatic tests use fakes and do
86
+ not claim physical audibility on any platform.
87
+
88
+ ## Safety behavior
89
+
90
+ - One node serializes its own utterances; normal pending work is bounded.
91
+ - A high-urgency utterance may preempt queue order but never bypasses rate
92
+ limits or consent.
93
+ - An adapter-start watchdog reports `unknown`, not a fabricated success.
94
+ - Remote Stop is scoped by origin and utterance ID. Local Stop is sovereign and
95
+ works without network, hub, credentials, or a running caller.
96
+ - Audio receipt and group receipt storage are bounded to 512 records with a
97
+ 24-hour TTL.
98
+ - An utterance ID cannot be reused between group and single-target commands by
99
+ the same caller while its receipt remains live.
100
+
101
+ ## Local state
102
+
103
+ All Audio Share state remains local to the node:
104
+
105
+ | File | Contents |
106
+ |---|---|
107
+ | `~/.nexuscrew/audio.json` | Closed-schema local audio consent. |
108
+ | `~/.nexuscrew/audio-groups.json` | Local named target groups, user-only and atomic. |
109
+ | `~/.nexuscrew/audio-bridge.key` | User-only HMAC secret for MCP-to-server origin proof. |
110
+
111
+ These files are not Fleet credentials, are not federated, and must not be
112
+ copied into logs, tickets, or a shared repository.
113
+
114
+ ## Related guides
115
+
116
+ - [MCP bridge](MCP.md)
117
+ - [Notifications](NOTIFICATIONS.md)
118
+ - [Connect nodes](NODES.md)
119
+ - [Security](SECURITY.md)
@@ -0,0 +1,156 @@
1
+ # The cell panel
2
+
3
+ A cell can carry a **panel**: a web interface of its own that NexusCrew serves
4
+ inside the app, next to the terminal. A remote desktop, a notebook, a dashboard
5
+ — anything that speaks HTTP.
6
+
7
+ The panel is opt-in and off by default. A cell without `panelUrl` simply has no
8
+ panel button.
9
+
10
+ ## Configuring it
11
+
12
+ `panelUrl` is a **property of the cell**, a sibling of `engine` and `cwd`. It is
13
+ not a command, not an argument, and not something the cell runs:
14
+
15
+ ```json
16
+ {
17
+ "cells": [
18
+ {
19
+ "id": "Design",
20
+ "engine": "shell.local",
21
+ "commands": { "shell.local": "zsh" },
22
+ "panelUrl": "http://127.0.0.1:6080/vnc.html"
23
+ }
24
+ ]
25
+ }
26
+ ```
27
+
28
+ Put it on an **engine** instead, and it becomes the default for every cell using
29
+ that engine.
30
+
31
+ The value must be an `http:` or `https:` URL pointing at **loopback** —
32
+ `127.0.0.1`, `[::1]` or `localhost`. Anything else is rejected when the
33
+ definition is read, and the cell keeps working without a panel. A container's
34
+ own address does not count as loopback: publish the port to `127.0.0.1` on the
35
+ host and point the panel there.
36
+
37
+ ### The mistake to avoid
38
+
39
+ Do not put the URL in the cell's **command**. The command is what the cell
40
+ executes, so this:
41
+
42
+ ```json
43
+ "commands": { "shell.local": "panelUrl: http://127.0.0.1:6900/" }
44
+ ```
45
+
46
+ makes the cell try to run `panelUrl:` as a program. It fails to start, and
47
+ because no `panelUrl` property was ever set, no panel button appears either —
48
+ two symptoms, one cause, and neither of them says "wrong field".
49
+
50
+ ### Setting it from the app
51
+
52
+ The cell and engine editors carry a `panelUrl` field. It anticipates the same
53
+ loopback rule the backend enforces, so a non-loopback value is flagged before
54
+ you save — but that check is not authoritative: the definition is validated
55
+ again, the same way, the moment it is actually read.
56
+
57
+ You can still set it by editing the fleet definition directly, or by exporting
58
+ a fleet backup, adding the property, and importing it back.
59
+
60
+ ## A worked example
61
+
62
+ A container publishing a remote desktop on the host's loopback:
63
+
64
+ ```
65
+ 127.0.0.1:6900 -> the desktop's web interface
66
+ ```
67
+
68
+ Then, on the cell you want to watch it from:
69
+
70
+ ```json
71
+ "panelUrl": "http://127.0.0.1:6900/"
72
+ ```
73
+
74
+ Start the cell and open it: the panel button sits next to the terminal.
75
+
76
+ If the desktop asks for a password, you will be asked for it inside the panel —
77
+ the forwarder carries no credentials of its own, in either direction.
78
+
79
+ ## Checking it worked
80
+
81
+ | What you see | What it means |
82
+ |---|---|
83
+ | No panel button at all | The cell has no valid `panelUrl`. Either it was never set, or it was rejected — a non-loopback host, or a value that is not a URL. |
84
+ | Button present, frame says the panel is unavailable | The cell was found but the destination did not answer. Check the service is listening **on loopback of the machine running the node**. |
85
+ | Button present, frame shows the page | Working. Sub-resources and WebSocket travel on the viewing cookie described below. |
86
+ | "This node does not grant the panel to the requester" | The panel lives on a paired node that has not granted access. It is granted there, not here — see the last section. |
87
+
88
+ Note the difference between the first two rows: a missing button is a
89
+ **configuration** problem, an unavailable frame is a **reachability** one. They
90
+ look similar and have nothing to do with each other.
91
+
92
+ ## Why loopback only
93
+
94
+ This is the constraint that shapes everything else, so it is worth stating
95
+ plainly: **the panel forwarder is not a port-forward.**
96
+
97
+ The caller picks *which cell* to open, never *where to connect*. The
98
+ destination is resolved from that cell's own `panelUrl`, on the machine that
99
+ runs the node. Without this rule, a route that opens "a panel" would be a way
100
+ to reach any address the node can reach — including services that trust the
101
+ network they sit on.
102
+
103
+ The panel is also a service that already holds sessions: behind a remote
104
+ desktop there is usually a browser with accounts logged in. That kind of access
105
+ is not revoked by rotating a key, which is why access is granted per peer and
106
+ denied by default.
107
+
108
+ ## Where it runs
109
+
110
+ | Platform | State |
111
+ |---|---|
112
+ | Linux server / VPS | **In use.** Any loopback service works; the common case is a container exposing noVNC on `127.0.0.1`. |
113
+ | Termux / Android | **Expected to work, not verified.** The forwarder needs nothing but a loopback HTTP service — no container runtime, no specific desktop. It has not been tried, and until it is, treat it as untested rather than supported. |
114
+
115
+ Nothing in the forwarder is platform-specific: it proxies HTTP and WebSocket to
116
+ a URL. If a platform can run a web service on loopback, it can host a panel.
117
+
118
+ ## How the browser gets in
119
+
120
+ Worth knowing, because it explains a design that would otherwise look odd.
121
+
122
+ An `<iframe src>` is a browser navigation: it carries no application headers.
123
+ So the panel cannot sit behind the usual bearer-token gate — the one consumer
124
+ it exists for would be locked out. Putting the token in the query string does
125
+ not help either: the panel's own pages request their sub-resources with
126
+ relative URLs and no query, so everything after the first request would fail
127
+ and the frame would stay blank.
128
+
129
+ Instead:
130
+
131
+ 1. the authenticated app asks for a **ticket** — opaque, one-use, valid for
132
+ seconds, bound to one cell;
133
+ 2. the frame's first request spends it, and the answer sets a **viewing
134
+ cookie**: `HttpOnly`, `SameSite=Strict`, scoped by `Path` to that cell's
135
+ panel subtree;
136
+ 3. the relative sub-resources travel on that cookie, and nothing else does.
137
+
138
+ The node's token never reaches the browser, a log, or a `Referer` header, and
139
+ no credential of ours is forwarded to the panel itself.
140
+
141
+ ## Panels on another machine
142
+
143
+ A panel can be opened toward a node you have paired, but **only if that node
144
+ granted it**:
145
+
146
+ ```
147
+ nexuscrew nodes panel <node> on
148
+ ```
149
+
150
+ The default is off, and it is off deliberately: in the pairing model a peer is
151
+ otherwise treated much like the operator, and that is a coherent choice for
152
+ everything except a panel, for the reason given above.
153
+
154
+ The decision is always made by the node that **owns** the panel, never by the
155
+ one asking. When it says no, the interface says so by name — you get "panel not
156
+ granted", not a blank rectangle you have to guess about.
@@ -0,0 +1,122 @@
1
+ # Configuration
2
+
3
+ [← Documentation index](README.md)
4
+
5
+ Most setup lives in the PWA. Runtime state is local to the current user and is
6
+ never synchronized to a hosted control service.
7
+
8
+ ## Local paths
9
+
10
+ | Path | Contents |
11
+ |---|---|
12
+ | `~/.nexuscrew/config.json` | Port, Fleet mode and runtime options |
13
+ | `~/.nexuscrew/token` | Local PWA bearer token |
14
+ | `~/.nexuscrew/credentials.json` | Optional node-local write-only provider store |
15
+ | `~/.nexuscrew/audio.json` | Local Audio Share consent (default off) |
16
+ | `~/.nexuscrew/audio-groups.json` | Local named Audio Share target groups |
17
+ | `~/.nexuscrew/audio-bridge.key` | Local HMAC proof for MCP Audio Share calls |
18
+ | `~/.nexuscrew/tunnels/` | Managed SSH supervisor state and owner-only logs |
19
+ | `~/NexusFiles/<session>/` | Per-session inbox and outbox |
20
+
21
+ Sensitive files are created with user-only permissions. The credential store is
22
+ optional; NexusCrew can also resolve compatible provider variables from its
23
+ service environment.
24
+
25
+ ## Precedence
26
+
27
+ Runtime values follow:
28
+
29
+ ```text
30
+ defaults < config file < environment
31
+ ```
32
+
33
+ Common overrides:
34
+
35
+ | Variable | Purpose |
36
+ |---|---|
37
+ | `NEXUSCREW_PORT` | Loopback HTTP port |
38
+ | `NEXUSCREW_CONFIG_FILE` | Alternate config file, useful for isolated tests |
39
+ | `NEXUSCREW_TOKEN_FILE` | Alternate bearer-token file |
40
+ | `NEXUSCREW_FILES_ROOT` | Alternate per-session file root |
41
+ | `NEXUSCREW_TMUX` | tmux executable |
42
+ | `NEXUSCREW_FLEET=0` | Disable Fleet management |
43
+ | `NEXUSCREW_ALTERNATE_SCREEN=1` | Keep tmux's standard alternate-screen behavior for newly created NexusCrew sessions |
44
+ | `NEXUSCREW_READONLY=1` | Disable server-side mutations |
45
+ | `NEXUSCREW_AUTO_UPDATE=0` | Disable the stable update scheduler |
46
+ | `NEXUSCREW_DEBUG=1` | Enable bounded verbose diagnostics |
47
+ | `NEXUSCREW_VOICE_URL` | Optional server-side speech-to-text endpoint |
48
+ | `NEXUSCREW_VOICE_TOKEN_FILE` | Token file for the optional STT endpoint |
49
+
50
+ Use `NEXUSCREW_CONFIG_FILE`, `NEXUSCREW_TOKEN_FILE`, `NEXUSCREW_FILES_ROOT`
51
+ and a separate `HOME`/XDG root when creating an isolated test runtime.
52
+
53
+ ## Session and Fleet settings
54
+
55
+ | `config.json` key | Default | Effect |
56
+ |---|---:|---|
57
+ | `alternateScreen` | `false` | New sessions created through Fleet or the PWA keep full-screen TUI output on the normal screen, where it enters tmux history and remains scrollable. Set `true` to restore the standard tmux alternate screen. |
58
+
59
+ The setting applies only when NexusCrew creates a session through Fleet or the
60
+ PWA; active and unmanaged sessions are not changed. `NEXUSCREW_ALTERNATE_SCREEN=1` has the usual
61
+ environment precedence. For the normal-screen mode, keep the user-owned tmux
62
+ `history-limit` at least 10000 (100000 is a practical value). `nexuscrew doctor`
63
+ warns when it observes a lower value but never changes `~/.tmux.conf`.
64
+ In the PWA, the same local setting is available at **Settings → System →
65
+ Diagnostics**.
66
+
67
+ Fleet cells and engines are managed in **Settings → Fleet**. A cell records:
68
+
69
+ - logical cell name and tmux session
70
+ - working directory
71
+ - engine, provider and model
72
+ - permission policy
73
+ - optional system prompt or trusted Shell command
74
+ - boot state
75
+
76
+ Provider key values are not stored in cell or engine definitions. The PWA
77
+ reports whether a required variable is configured, but never returns its value.
78
+
79
+ ## Browser-local settings
80
+
81
+ The following are local to the browser origin:
82
+
83
+ - deck layout and node collapse state
84
+ - session ordering and pins
85
+ - mobile key-bar and keyboard preferences
86
+ - per-cell draft, composer size and bounded prompt history
87
+ - spoken-notification opt-in and successful per-page voice priming
88
+
89
+ Browser-local state is not included in Fleet backups and can be cleared from
90
+ **Settings → System**.
91
+
92
+ ## Speech input
93
+
94
+ Speech-to-text has two independent paths:
95
+
96
+ - Browser Web Speech where the browser exposes it.
97
+ - Optional server STT through `NEXUSCREW_VOICE_URL`.
98
+
99
+ If neither path is available, the microphone control is hidden. Server STT is
100
+ separate from optional spoken notifications, which use the device's browser
101
+ speech engine and send no text to a speech service.
102
+
103
+ ## Token rotation
104
+
105
+ The browser token travels in the URL fragment (`#token=...`), not in the
106
+ initial HTTP request. To rotate it:
107
+
108
+ ```bash
109
+ nexuscrew stop
110
+ rm ~/.nexuscrew/token
111
+ nexuscrew
112
+ ```
113
+
114
+ Review the exact target before removing any alternate token file.
115
+
116
+ ## Related guides
117
+
118
+ - [Fleet and terminals](FLEET.md)
119
+ - [Notifications](NOTIFICATIONS.md)
120
+ - [Audio Share and native TTS](AUDIO_SHARE.md)
121
+ - [Security](SECURITY.md)
122
+ - [Operations](OPERATIONS.md)