@logicseek/hello-cc 0.1.9 → 1.0.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.
Files changed (108) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +18 -3
  3. package/README.zh-CN.md +17 -3
  4. package/bin/hcc.mjs +3 -5656
  5. package/docs/README.md +8 -0
  6. package/docs/README.zh-CN.md +6 -0
  7. package/docs/architecture.md +29 -18
  8. package/docs/architecture.zh-CN.md +20 -10
  9. package/docs/commands.md +13 -4
  10. package/docs/commands.zh-CN.md +13 -4
  11. package/docs/current-state-review-2026-08.md +341 -0
  12. package/docs/defect-review-2026-07.md +382 -0
  13. package/docs/guide.md +39 -3
  14. package/docs/guide.zh-CN.md +34 -2
  15. package/docs/superpowers/plans/2026-08-03-runtime-safety-plan.md +294 -0
  16. package/docs/superpowers/plans/2026-08-03-v1-data-identity-plan.md +400 -0
  17. package/docs/superpowers/plans/2026-08-03-v1-release-verification-plan.md +284 -0
  18. package/docs/superpowers/plans/2026-08-03-web-api-hardening-plan.md +303 -0
  19. package/docs/superpowers/plans/2026-08-04-runtime-buffer-gc-two-phase.md +71 -0
  20. package/docs/superpowers/specs/2026-08-03-runtime-safety-design.md +117 -0
  21. package/docs/superpowers/specs/2026-08-03-v1-data-identity-design.md +120 -0
  22. package/docs/superpowers/specs/2026-08-03-v1-release-verification-design.md +117 -0
  23. package/docs/superpowers/specs/2026-08-03-web-api-hardening-design.md +104 -0
  24. package/lib/cli/app.mjs +642 -0
  25. package/lib/cli/commands/coordination.mjs +247 -0
  26. package/lib/cli/commands/doctor.mjs +81 -0
  27. package/lib/cli/commands/gc.mjs +852 -0
  28. package/lib/cli/commands/hook.mjs +205 -0
  29. package/lib/cli/commands/install.mjs +294 -0
  30. package/lib/cli/commands/lifecycle.mjs +101 -0
  31. package/lib/cli/commands/lock.mjs +197 -0
  32. package/lib/cli/commands/misc.mjs +61 -0
  33. package/lib/cli/commands/msg.mjs +152 -0
  34. package/lib/cli/commands/peer.mjs +233 -0
  35. package/lib/cli/commands/query.mjs +96 -0
  36. package/lib/cli/commands/run.mjs +463 -0
  37. package/lib/cli/commands/task.mjs +405 -0
  38. package/lib/cli/commands/team.mjs +131 -0
  39. package/lib/cli/commands/tmux.mjs +838 -0
  40. package/lib/cli/commands/up.mjs +135 -0
  41. package/lib/cli-args.mjs +16 -2
  42. package/lib/coordination-state.mjs +112 -51
  43. package/lib/core/coordination/automation.mjs +5 -8
  44. package/lib/core/coordination/clock-safety.mjs +323 -0
  45. package/lib/core/coordination/event-retention.mjs +17 -0
  46. package/lib/core/coordination/gc-plan.mjs +614 -0
  47. package/lib/core/coordination/lease-renewal.mjs +102 -0
  48. package/lib/core/coordination/lock-evidence.mjs +77 -0
  49. package/lib/core/coordination/optimistic-evidence.mjs +26 -0
  50. package/lib/core/coordination/tasks.mjs +156 -36
  51. package/lib/core/peers/evidence-runtime.mjs +312 -0
  52. package/lib/core/peers/evidence.mjs +103 -0
  53. package/lib/core/peers/hook-owner.mjs +32 -0
  54. package/lib/core/peers/liveness.mjs +44 -4
  55. package/lib/core/peers/peer-helpers.mjs +121 -0
  56. package/lib/core/peers/session.mjs +8 -2
  57. package/lib/core/peers/tmux-safety.mjs +290 -0
  58. package/lib/core/sessions/launch.mjs +13 -1
  59. package/lib/db/connection.mjs +104 -0
  60. package/lib/db/events.mjs +34 -0
  61. package/lib/db/migration-backup.mjs +225 -0
  62. package/lib/db/schema.mjs +102 -5
  63. package/lib/db/stores/peers.mjs +34 -0
  64. package/lib/integrations/peers/identity.mjs +62 -16
  65. package/lib/integrations/shims/script.mjs +111 -9
  66. package/lib/process/identity.mjs +192 -0
  67. package/lib/process/pty-lifecycle.mjs +149 -0
  68. package/lib/runtime/buffer-directory-lease.mjs +47 -0
  69. package/lib/runtime/buffer-evidence.mjs +336 -0
  70. package/lib/runtime/buffer-gc-protocol.mjs +167 -0
  71. package/lib/runtime/buffer-gc.mjs +270 -0
  72. package/lib/runtime/client.mjs +60 -7
  73. package/lib/runtime/fatal-shutdown.mjs +54 -0
  74. package/lib/runtime/project-path.mjs +139 -0
  75. package/lib/runtime/projects.mjs +320 -40
  76. package/lib/runtime/state.mjs +227 -27
  77. package/lib/shared/cleanup-error.mjs +54 -0
  78. package/lib/shared/clock-grace.mjs +106 -0
  79. package/lib/shared/errors.mjs +9 -0
  80. package/lib/shared/file-lock.mjs +370 -0
  81. package/lib/shared/json-file.mjs +6 -2
  82. package/lib/shared/redact.mjs +60 -0
  83. package/lib/shared/socket-lock-worker.mjs +201 -0
  84. package/lib/terminal/tmux.mjs +78 -4
  85. package/lib/tmux.mjs +4 -0
  86. package/lib/ui/help.mjs +53 -15
  87. package/lib/web/api-version.mjs +42 -0
  88. package/lib/web/auto-attach.mjs +185 -0
  89. package/lib/web/buffer-gc-runtime.mjs +175 -0
  90. package/lib/web/cookie-auth.mjs +119 -0
  91. package/lib/web/external-sessions.mjs +254 -0
  92. package/lib/web/http-routes.mjs +542 -0
  93. package/lib/web/http.mjs +135 -6
  94. package/lib/web/liveness-reaper.mjs +145 -0
  95. package/lib/web/peer-actions.mjs +132 -23
  96. package/lib/web/project-contexts.mjs +178 -0
  97. package/lib/web/pty-sessions.mjs +203 -0
  98. package/lib/web/runtime-main.mjs +714 -0
  99. package/lib/web/runtime.mjs +91 -36
  100. package/lib/web/session-serialize.mjs +0 -0
  101. package/lib/web/startup.mjs +333 -0
  102. package/lib/web/tls.mjs +365 -0
  103. package/lib/web/tmux-sessions.mjs +1361 -0
  104. package/lib/web/tmux-stream.mjs +166 -0
  105. package/lib/web/ui-template.mjs +152 -20
  106. package/package.json +5 -2
  107. package/scripts/check-free-identifiers.mjs +290 -0
  108. package/scripts/regression.mjs +5397 -263
@@ -0,0 +1,284 @@
1
+ # hello-cc v1 Release and Verification Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Convert the completed hardening into a coherent 1.0.0 release with controlled fatal shutdown, complete packaging, accurate documentation, stable tests, and fresh cross-platform evidence.
6
+
7
+ **Architecture:** A focused fatal-shutdown helper owns process-level failure behavior. Release metadata and documentation declare breaking changes and accepted risks. Verification audits the entire local delta against `origin/master`, not only the last patch.
8
+
9
+ **Tech Stack:** Node.js 24 ESM, npm packaging, Docker/BuildKit, tmux, SQLite, HTTP/HTTPS/WebSocket regression suite.
10
+
11
+ ---
12
+
13
+ ## File Map
14
+
15
+ - Create `lib/runtime/fatal-shutdown.mjs` and `test/fatal-shutdown.test.mjs`.
16
+ - Modify `bin/hcc.mjs` fatal handlers and shutdown orchestration.
17
+ - Modify `package.json`, `package-lock.json`, `CHANGELOG.md`.
18
+ - Modify root and `docs/` English/Chinese README, commands, and guide files.
19
+ - Modify `docs/defect-review-2026-07.md` status evidence.
20
+ - Track/modify `.dockerignore`, `Dockerfile`, `lib/shared/clock-grace.mjs`, `lib/web/tls.mjs`.
21
+ - Modify `scripts/regression.mjs` for deterministic TTL evidence and package audits.
22
+
23
+ ### Task 1: Replace unknown-error survival with idempotent fatal shutdown
24
+
25
+ **Files:**
26
+ - Create: `lib/runtime/fatal-shutdown.mjs`
27
+ - Create: `test/fatal-shutdown.test.mjs`
28
+ - Modify: `bin/hcc.mjs:5190-5335`
29
+
30
+ - [ ] **Step 1: Write failing fatal-state tests**
31
+
32
+ Use a real `EventEmitter` as the process signal source and real promises for asynchronous cleanup. Do not mock cleanup behavior; provide complete callbacks that append observable events.
33
+
34
+ ```js
35
+ const events = [];
36
+ const controller = createFatalShutdownController({
37
+ cleanup: async (reason) => { events.push(`cleanup:${reason}`); },
38
+ exit: (code) => { events.push(`exit:${code}`); },
39
+ forceExit: (code) => { events.push(`force:${code}`); },
40
+ log: (value) => { events.push(`log:${value.code}`); }
41
+ });
42
+
43
+ await controller.fatal(new Error('first'));
44
+ await controller.fatal(new Error('second'));
45
+ assert.deepEqual(events, ['log:FATAL_RUNTIME_ERROR', 'cleanup:first', 'exit:1', 'force:1']);
46
+ ```
47
+
48
+ Also assert cleanup failure still exits nonzero and its log is redacted.
49
+
50
+ - [ ] **Step 2: Run to verify RED**
51
+
52
+ Run: `node --test test/fatal-shutdown.test.mjs`.
53
+
54
+ Expected: missing-module failure.
55
+
56
+ - [ ] **Step 3: Implement the controller and wire Web shutdown**
57
+
58
+ ```js
59
+ export function createFatalShutdownController({ cleanup, exit, forceExit, log }) {
60
+ let state = 'idle';
61
+ return {
62
+ async fatal(error) {
63
+ if (state !== 'idle') {
64
+ forceExit(1);
65
+ return;
66
+ }
67
+ state = 'stopping';
68
+ log({ code: 'FATAL_RUNTIME_ERROR', error });
69
+ try { await cleanup(error?.message || 'fatal runtime error'); }
70
+ catch (cleanupError) { log({ code: 'FATAL_CLEANUP_ERROR', error: cleanupError }); }
71
+ state = 'stopped';
72
+ exit(1);
73
+ }
74
+ };
75
+ }
76
+ ```
77
+
78
+ In `cmdWeb`, create one controller after all owned resources exist. `uncaughtException` and `unhandledRejection` call it. Cleanup clears timers/watchers, closes WebSockets/server, detaches or exits sessions by existing semantics, closes DB resources, and clears only runtime pointers matching the current process identity. Remove the log-and-continue handlers.
79
+
80
+ - [ ] **Step 4: Verify GREEN at unit and child-process boundaries**
81
+
82
+ Run unit tests. Add a child-process fixture that imports the helper, opens a temporary server/resource, triggers an unhandled rejection, and asserts cleanup marker, closed listener, and exit status 1.
83
+
84
+ Run: `npm run test:unit`.
85
+
86
+ Run: `npm run test:regression`.
87
+
88
+ - [ ] **Step 5: Commit**
89
+
90
+ ```bash
91
+ git add lib/runtime/fatal-shutdown.mjs test/fatal-shutdown.test.mjs bin/hcc.mjs scripts/regression.mjs
92
+ git commit -m "fix: terminate cleanly after fatal runtime errors"
93
+ ```
94
+
95
+ ### Task 2: Publish the breaking 1.0.0 contract in metadata and docs
96
+
97
+ **Files:**
98
+ - Modify: `package.json:3,24-32`
99
+ - Modify: `package-lock.json`
100
+ - Modify: `CHANGELOG.md`
101
+ - Modify: `README.md`, `README.zh-CN.md`
102
+ - Modify: `docs/README.md`, `docs/README.zh-CN.md`
103
+ - Modify: `docs/commands.md`, `docs/commands.zh-CN.md`
104
+ - Modify: `docs/guide.md`, `docs/guide.zh-CN.md`
105
+ - Modify: `docs/defect-review-2026-07.md`
106
+ - Modify: `lib/ui/help.mjs`
107
+
108
+ - [ ] **Step 1: Add failing release-document assertions**
109
+
110
+ Extend `syntaxAndHelp` and release checks to require all of these concepts in
111
+ `README.md`, `README.zh-CN.md`, `docs/commands.md`,
112
+ `docs/commands.zh-CN.md`, `docs/guide.md`, `docs/guide.zh-CN.md`, the changelog,
113
+ and CLI help:
114
+
115
+ ```text
116
+ 1.0.0
117
+ schema v7 / no downgrade
118
+ pre-migration backup
119
+ Runtime API v2
120
+ provider peer IDs changed
121
+ gc --history
122
+ process evidence / unknown-only grace
123
+ --tls and --trust-proxy
124
+ plaintext LAN accepted risk
125
+ arbitrary authenticated project root accepted risk
126
+ ```
127
+
128
+ Assert the defect report uses the actual current branch dynamically or avoids naming a branch, and that fixed/partial/deferred counts add up to the listed findings.
129
+
130
+ - [ ] **Step 2: Run to verify RED**
131
+
132
+ Run: `npm run test:regression`.
133
+
134
+ Run: `npm run release:check`.
135
+
136
+ Expected: FAIL on version and missing release/help documentation.
137
+
138
+ - [ ] **Step 3: Update release metadata and documentation**
139
+
140
+ Set `package.json` version to `1.0.0`, then run:
141
+
142
+ ```bash
143
+ npm install --package-lock-only --ignore-scripts --no-audit --no-fund
144
+ ```
145
+
146
+ Add one coherent 1.0.0 changelog section with Breaking, Security, Reliability, GC, and Upgrade headings. Document exact backup naming and recovery inspection, no downgrade, old/new peer ID examples, API v2 negotiation, safe GC flags, supported transport modes, and the two accepted risks.
147
+
148
+ Update help text without claiming plaintext confidentiality or project isolation. Correct defect statuses from current tests and code evidence.
149
+
150
+ - [ ] **Step 4: Verify GREEN**
151
+
152
+ Run: `npm run test:regression`.
153
+
154
+ Run: `npm run release:check`.
155
+
156
+ Run: `node ./bin/hcc.mjs --help`.
157
+
158
+ Expected: all release assertions PASS and version/help output is 1.0.0.
159
+
160
+ - [ ] **Step 5: Commit**
161
+
162
+ ```bash
163
+ git add package.json package-lock.json CHANGELOG.md README.md README.zh-CN.md docs/README.md docs/README.zh-CN.md docs/commands.md docs/commands.zh-CN.md docs/guide.md docs/guide.zh-CN.md docs/defect-review-2026-07.md lib/ui/help.mjs scripts/regression.mjs
164
+ git commit -m "docs: publish hello-cc 1.0.0 contract"
165
+ ```
166
+
167
+ ### Task 3: Make packaging complete and TTL regression deterministic
168
+
169
+ **Files:**
170
+ - Track/modify: `.dockerignore`
171
+ - Track/modify: `Dockerfile`
172
+ - Track: `lib/shared/clock-grace.mjs`
173
+ - Track: `lib/web/tls.mjs`
174
+ - Modify: `scripts/regression.mjs:164-185,2730-2820,7072-7115`
175
+ - Modify: `package.json:10-22`
176
+
177
+ - [ ] **Step 1: Add failing package-completeness and TTL tests**
178
+
179
+ Add a package audit that runs `npm pack --dry-run --json`, parses the file list, resolves every relative import reachable from `bin/hcc.mjs`, and asserts every production dependency is tracked and included in the package.
180
+
181
+ Change hook renewal event payload assertions to require a `renewed_at` timestamp. Seed a fixture where parent `before` is one second greater than child `renewed_at` and assert expiration equals `renewed_at + renewalSec`, not a cross-process before/after window.
182
+
183
+ - [ ] **Step 2: Run to verify RED**
184
+
185
+ Run: `npm run test:regression`.
186
+
187
+ Expected: FAIL because renewal events lack `renewed_at` and current package audit sees untracked production modules.
188
+
189
+ - [ ] **Step 3: Record operation time and complete packaging**
190
+
191
+ Include `renewed_at: hookNow` in `lock.renewed_by_hook` payload and assert exact TTL from that value. Keep created time and `ttl_sec` invariants.
192
+
193
+ Track all imported production modules. Ensure package `files` includes every `lib/` module. Change Docker dependency install to:
194
+
195
+ ```dockerfile
196
+ RUN npm ci --no-audit --no-fund
197
+ ```
198
+
199
+ At test startup print `node --version` and `tmux -V`. Keep `.git`, runtime state, tokens, DBs, WAL files, logs, and local buffers out of the build context; do not exclude production source.
200
+
201
+ - [ ] **Step 4: Verify GREEN and repeatability**
202
+
203
+ Run: `npm run test:regression` twice locally.
204
+
205
+ Run: `npm pack --dry-run --json` and inspect the parsed audit result.
206
+
207
+ Build and run the container twice at this task boundary. Both runs must reach `FULL_REGRESSION_OK`.
208
+
209
+ - [ ] **Step 5: Commit**
210
+
211
+ ```bash
212
+ git add .dockerignore Dockerfile lib/shared/clock-grace.mjs lib/web/tls.mjs scripts/regression.mjs package.json
213
+ git commit -m "test: make v1 package and regression deterministic"
214
+ ```
215
+
216
+ ### Task 4: Execute the final ordered verification gates
217
+
218
+ **Files:**
219
+ - Review: every path in `git diff --name-only origin/master`
220
+ - Modify only when a failed gate identifies a defect, using a new RED-GREEN cycle.
221
+
222
+ - [ ] **Step 1: Audit final scope and production imports**
223
+
224
+ Run:
225
+
226
+ ```bash
227
+ git status --short
228
+ git diff --check origin/master
229
+ git diff --stat origin/master
230
+ git diff --name-status origin/master
231
+ npm pack --dry-run --json
232
+ ```
233
+
234
+ Expected: no untracked production dependency; no whitespace errors; package audit lists every reachable module.
235
+
236
+ - [ ] **Step 2: Run syntax, unit, and focused security checks**
237
+
238
+ Run:
239
+
240
+ ```bash
241
+ node --check bin/hcc.mjs
242
+ node --check scripts/regression.mjs
243
+ npm run test:unit
244
+ npm run smoke
245
+ npm run release:check
246
+ ```
247
+
248
+ Expected: exit 0 for every command with zero test failures.
249
+
250
+ - [ ] **Step 3: Re-run original vulnerable paths and bypass controls**
251
+
252
+ Run focused selectors or fixtures for negative GC, live-buffer manual GC, CLI-only clock gap, concurrent registry writers, PID reuse, API v1/missing version, cross-session tokens, symlink escape, secret log scanning, and fatal shutdown.
253
+
254
+ Expected: original unsafe behavior no longer reproduces; legitimate live/dead/admin/arbitrary-root/plaintext-LAN controls remain intact.
255
+
256
+ - [ ] **Step 4: Run complete local regression**
257
+
258
+ Run: `npm test`.
259
+
260
+ Expected: unit tests PASS and regression ends with `FULL_REGRESSION_OK`.
261
+
262
+ - [ ] **Step 5: Build the exact current Node 24/Linux image**
263
+
264
+ Run:
265
+
266
+ ```bash
267
+ docker build --no-cache -t hello-cc-v1-verify:node24 .
268
+ ```
269
+
270
+ Expected: successful build using `npm ci`, with all current source copied.
271
+
272
+ - [ ] **Step 6: Run three consecutive container regressions**
273
+
274
+ Run `docker run --rm hello-cc-v1-verify:node24` three separate times. Do not combine them in a shell loop; preserve each output independently.
275
+
276
+ Expected for each run: `FULL_REGRESSION_OK`, no TTL clock-window failure, no leaked tmux session, exit 0.
277
+
278
+ - [ ] **Step 7: Review the complete delta and requirements**
279
+
280
+ Read all four design specs and map every acceptance criterion to a passing command or test. Review `git diff origin/master` for alternate unsafe branches, duplicated policy logic, accidental user-change loss, secrets, stale documentation, and unrelated churn.
281
+
282
+ - [ ] **Step 8: Commit only evidence-required final corrections**
283
+
284
+ If every gate passed without a correction, do not create an empty commit. If a gate required a fix, use a focused commit containing its failing test and minimal production correction.
@@ -0,0 +1,303 @@
1
+ # Web and Runtime API v2 Hardening Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Ship an explicit Runtime API v2 with bounded browser sessions, session-scoped terminal input, CSP, secret redaction, and symlink-safe project databases.
6
+
7
+ **Architecture:** Central helpers own API negotiation, redaction, token comparison, and canonical path validation. The HTTP and WebSocket entry points enforce these helpers before routing while preserving the accepted plaintext-LAN and arbitrary-root behaviors.
8
+
9
+ **Tech Stack:** Node.js 24 HTTP/HTTPS, `ws`, browser WebSocket/fetch, `node:test`, real filesystem integration tests.
10
+
11
+ ---
12
+
13
+ ## File Map
14
+
15
+ - Create `lib/web/api-version.mjs` and `test/web-api-version.test.mjs`.
16
+ - Create `lib/shared/redact.mjs` and `test/redact.test.mjs`.
17
+ - Create `lib/runtime/project-path.mjs` and `test/project-path.test.mjs`.
18
+ - Modify `lib/web/http.mjs`, `lib/web/runtime.mjs`, `lib/runtime/client.mjs`.
19
+ - Modify `bin/hcc.mjs` HTTP/WS routing, cookie lifecycle, logging, and TLS use.
20
+ - Modify `lib/web/ui-template.mjs` for API headers, WS version query, and CSP nonce.
21
+ - Modify `lib/web/tls.mjs` only where focused tests show a contract gap.
22
+ - Modify `scripts/regression.mjs` for real HTTP/HTTPS/WS tests.
23
+
24
+ ### Task 1: Add Runtime API v2 negotiation
25
+
26
+ **Files:**
27
+ - Create: `lib/web/api-version.mjs`
28
+ - Create: `test/web-api-version.test.mjs`
29
+ - Modify: `lib/runtime/client.mjs:7-35`
30
+ - Modify: `lib/web/runtime.mjs:11-185`
31
+ - Modify: `bin/hcc.mjs:4675-5190`
32
+ - Modify: `lib/web/ui-template.mjs:1-2100`
33
+
34
+ - [ ] **Step 1: Write failing negotiation tests**
35
+
36
+ ```js
37
+ import test from 'node:test';
38
+ import assert from 'node:assert/strict';
39
+ import { API_VERSION, readHttpApiVersion, readWebSocketApiVersion } from '../lib/web/api-version.mjs';
40
+
41
+ test('reads API v2 from the HTTP header', () => {
42
+ assert.equal(API_VERSION, 2);
43
+ assert.deepEqual(readHttpApiVersion({ headers: { 'x-hcc-api-version': '2' } }), { ok: true, version: 2 });
44
+ });
45
+
46
+ test('rejects missing and old protected API versions', () => {
47
+ assert.deepEqual(readHttpApiVersion({ headers: {} }), { ok: false, version: null });
48
+ assert.deepEqual(readHttpApiVersion({ headers: { 'x-hcc-api-version': '1' } }), { ok: false, version: 1 });
49
+ });
50
+
51
+ test('reads browser WebSocket version from query', () => {
52
+ assert.deepEqual(readWebSocketApiVersion(new URL('http://localhost/ws?api_version=2')), { ok: true, version: 2 });
53
+ });
54
+ ```
55
+
56
+ - [ ] **Step 2: Run to verify RED**
57
+
58
+ Run: `node --test test/web-api-version.test.mjs`.
59
+
60
+ Expected: missing-module failure.
61
+
62
+ - [ ] **Step 3: Implement and enforce v2**
63
+
64
+ ```js
65
+ export const API_VERSION = 2;
66
+
67
+ function result(value) {
68
+ const version = Number.parseInt(String(value ?? ''), 10);
69
+ return { ok: version === API_VERSION, version: Number.isFinite(version) ? version : null };
70
+ }
71
+
72
+ export function readHttpApiVersion(req) {
73
+ return result(req?.headers?.['x-hcc-api-version']);
74
+ }
75
+
76
+ export function readWebSocketApiVersion(url) {
77
+ return result(url?.searchParams?.get('api_version'));
78
+ }
79
+ ```
80
+
81
+ Before every protected `/api/*` route, return status 426 and:
82
+
83
+ ```json
84
+ {"ok":false,"error":{"code":"API_VERSION_UNSUPPORTED","message":"Runtime API version 2 is required","supported_version":2}}
85
+ ```
86
+
87
+ Expose `api_version: 2` from runtime metadata. Add `X-HCC-API-Version: 2` in `runtimeRequest` and every browser fetch wrapper. Add `api_version=2` to terminal WebSocket URLs and reject old/missing WS versions before authentication/session lookup.
88
+
89
+ - [ ] **Step 4: Verify GREEN and real compatibility rejection**
90
+
91
+ Run unit tests. Add regression requests with missing/1/2 headers plus WS query 1/2. Public `/`, `/login`, and static assets must remain exempt.
92
+
93
+ Run: `npm run test:regression`.
94
+
95
+ - [ ] **Step 5: Commit**
96
+
97
+ ```bash
98
+ git add lib/web/api-version.mjs test/web-api-version.test.mjs lib/runtime/client.mjs lib/web/runtime.mjs lib/web/ui-template.mjs bin/hcc.mjs scripts/regression.mjs
99
+ git commit -m "feat: require Runtime API v2"
100
+ ```
101
+
102
+ ### Task 2: Enforce administrator and session-action token semantics
103
+
104
+ **Files:**
105
+ - Modify: `lib/web/http.mjs:62-122`
106
+ - Modify: `bin/hcc.mjs:2382-2465,2475-2580,4675-4955,5100-5185`
107
+ - Modify: `lib/web/ui-template.mjs:1570-1760,2050-2110`
108
+ - Modify: `scripts/regression.mjs:1193-1355,1974-2115,2866-3300`
109
+
110
+ - [ ] **Step 1: Add failing privilege-boundary tests**
111
+
112
+ Through real HTTP and WebSocket connections assert:
113
+
114
+ ```text
115
+ admin bearer -> create/stop/inject allowed
116
+ admin cookie + same Origin -> create/stop/inject allowed
117
+ admin cookie + foreign Origin -> mutation denied
118
+ session action token absent from GET /api/sessions
119
+ wrong/missing session action token -> WS input denied
120
+ correct token for another session -> WS input denied
121
+ logout/expiry/eviction -> existing cookie WS closed and future input denied
122
+ ```
123
+
124
+ Also assert tokenless non-loopback startup fails while tokenless loopback still works.
125
+ Because constant-time behavior is not safely distinguishable with a timing test,
126
+ add a static security assertion that extracts `resolveWebActionSession` from
127
+ `bin/hcc.mjs`, requires `tokenMatches(provided, expected)`, and rejects direct
128
+ `provided !== expected` comparison. This is the repeatable validation artifact
129
+ for the timing boundary; the integration cases remain the behavior proof.
130
+
131
+ - [ ] **Step 2: Run to verify RED**
132
+
133
+ Run: `npm run test:regression`.
134
+
135
+ Expected: FAIL on the static constant-time assertion while the current code uses
136
+ direct inequality. Any additional integration failure is fixed through the same
137
+ boundary, but is not required to establish RED.
138
+
139
+ - [ ] **Step 3: Apply one shared token comparator and explicit roles**
140
+
141
+ Use `tokenMatches(provided, expected)` for administrator and session action tokens. Keep `webAuthMode` results `token` and `cookie`, document both as administrator roles, and keep same-origin enforcement on every cookie mutation. Track each cookie-authenticated WS in its server session record and validate the record before inbound and outbound frames.
142
+
143
+ Do not put action tokens in serialized session lists. Deliver them only in the corresponding terminal snapshot frame. Preserve CLI injection through administrator bearer authentication without requiring the session token.
144
+
145
+ - [ ] **Step 4: Verify GREEN and alternate bypasses**
146
+
147
+ Run the regression suite, then repeat token tests with same-length wrong tokens, URL-encoded peer IDs, revoked cookies, and a token from a sibling project session.
148
+
149
+ Expected: legitimate admin and matching-session behavior remains; all bypasses fail.
150
+
151
+ - [ ] **Step 5: Commit**
152
+
153
+ ```bash
154
+ git add lib/web/http.mjs bin/hcc.mjs lib/web/ui-template.mjs scripts/regression.mjs
155
+ git commit -m "fix: enforce Web administrator and session tokens"
156
+ ```
157
+
158
+ ### Task 3: Add structured secret redaction and CSP
159
+
160
+ **Files:**
161
+ - Create: `lib/shared/redact.mjs`
162
+ - Create: `test/redact.test.mjs`
163
+ - Modify: `lib/web/http.mjs:33-59`
164
+ - Modify: `lib/web/ui-template.mjs:1-2150`
165
+ - Modify: `bin/hcc.mjs:2180-2250,2368-2470,5290-5300`
166
+ - Modify: `scripts/regression.mjs:1841-2115,3894-4300`
167
+
168
+ - [ ] **Step 1: Write failing redaction tests**
169
+
170
+ ```js
171
+ import test from 'node:test';
172
+ import assert from 'node:assert/strict';
173
+ import { redactSecrets } from '../lib/shared/redact.mjs';
174
+
175
+ test('redacts CLI, URL, headers, cookies, and structured tokens', () => {
176
+ const secret = 's3cr3t-value';
177
+ const value = {
178
+ command: `hcc web --token ${secret}`,
179
+ url: `http://host/?token=${secret}&root=/tmp/x`,
180
+ headers: { authorization: `Bearer ${secret}`, cookie: `hcc_sid=${secret}` },
181
+ action_token: secret
182
+ };
183
+ const redacted = JSON.stringify(redactSecrets(value));
184
+ assert.equal(redacted.includes(secret), false);
185
+ assert.equal(redacted.includes('[REDACTED]'), true);
186
+ });
187
+ ```
188
+
189
+ Add an HTML test that extracts the CSP nonce, verifies the script tag uses it, and asserts a second un-nonced inline script would violate the generated `script-src` policy.
190
+
191
+ - [ ] **Step 2: Run to verify RED**
192
+
193
+ Run: `node --test test/redact.test.mjs`.
194
+
195
+ Expected: missing-module failure.
196
+
197
+ - [ ] **Step 3: Implement redaction and nonce responses**
198
+
199
+ `redactSecrets(value)` recursively clones arrays/objects, replaces secret-key values, redacts bearer/cookie fragments, replaces `--token` arguments, and deletes URL `token` parameter values. It must handle cycles with a `WeakSet` and replace them with `[Circular]`.
200
+
201
+ Pass all startup/error logging through this helper before stringification. Never log raw request headers or URLs.
202
+
203
+ Generate a random nonce per HTML response and call `webIndexHtml({ nonce })` or `webLoginPage({ nonce })`. Add the nonce to every inline script. Emit CSP:
204
+
205
+ ```text
206
+ default-src 'self'; script-src 'self' 'nonce-<value>'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' ws: wss:; object-src 'none'; base-uri 'none'; frame-ancestors 'none'
207
+ ```
208
+
209
+ - [ ] **Step 4: Verify GREEN and leak controls**
210
+
211
+ Run unit tests. Start Web with unique test secrets, exercise login/WS/errors, stop it, and scan all generated logs/runtime output. Assert no supplied secret appears. Verify browser HTML contains a matching nonce and no un-nonced script.
212
+
213
+ Run: `npm run test:regression`.
214
+
215
+ - [ ] **Step 5: Commit**
216
+
217
+ ```bash
218
+ git add lib/shared/redact.mjs test/redact.test.mjs lib/web/http.mjs lib/web/ui-template.mjs bin/hcc.mjs scripts/regression.mjs
219
+ git commit -m "fix: redact Web secrets and enforce CSP"
220
+ ```
221
+
222
+ ### Task 4: Canonicalize arbitrary project roots without symlink escape
223
+
224
+ **Files:**
225
+ - Create: `lib/runtime/project-path.mjs`
226
+ - Create: `test/project-path.test.mjs`
227
+ - Modify: `bin/hcc.mjs:2479-2525`
228
+ - Modify: `scripts/regression.mjs:2866-3300,6300-6700`
229
+
230
+ - [ ] **Step 1: Write failing real-filesystem tests**
231
+
232
+ Create temporary roots for: normal project, missing `.hello-cc`, `.hello-cc` symlink to an outside directory, nested DB symlink escape, `/proc` pseudo-file target on Linux, and a normal custom DB below `.hello-cc`.
233
+
234
+ Use the intended API:
235
+
236
+ ```js
237
+ const safe = resolveProjectDatabase({ root, db: path.join(root, '.hello-cc', 'mesh.db'), createStateDir: true });
238
+ assert.equal(safe.db.startsWith(safe.stateDir + path.sep), true);
239
+ assert.throws(() => resolveProjectDatabase({ root: escapedRoot, db: escapedDb, createStateDir: true }), /PROJECT_PATH_FORBIDDEN/);
240
+ ```
241
+
242
+ - [ ] **Step 2: Run to verify RED**
243
+
244
+ Run: `node --test test/project-path.test.mjs`.
245
+
246
+ Expected: missing-module failure.
247
+
248
+ - [ ] **Step 3: Implement canonical containment**
249
+
250
+ Resolve an existing root with `realpathSync`. Create `.hello-cc` with mode `0700` only when requested and absent. Reject it when `lstat` reports a symlink or when its real path is outside root. Resolve the DB parent, reject symlink components escaping the state directory, and require an existing DB target to be a regular file.
251
+
252
+ Replace `assertDbUnderRoot`/lexical-only checks with `resolveProjectDatabase`. Continue allowing every existing canonical root after administrator authentication.
253
+
254
+ - [ ] **Step 4: Verify GREEN and HTTP boundary**
255
+
256
+ Run unit tests, then issue real authenticated Web requests for normal arbitrary roots and each escape fixture. Require normal requests to work and every escape to return `PROJECT_PATH_FORBIDDEN` without creating/opening the outside DB.
257
+
258
+ Run: `npm run test:regression`.
259
+
260
+ - [ ] **Step 5: Commit**
261
+
262
+ ```bash
263
+ git add lib/runtime/project-path.mjs test/project-path.test.mjs bin/hcc.mjs scripts/regression.mjs
264
+ git commit -m "fix: prevent project database symlink escape"
265
+ ```
266
+
267
+ ### Task 5: Finish TLS and trusted-proxy mode coverage
268
+
269
+ **Files:**
270
+ - Track/modify: `lib/web/tls.mjs`
271
+ - Modify: `lib/web/runtime.mjs:80-190`
272
+ - Modify: `lib/runtime/state.mjs:91-123`
273
+ - Modify: `lib/web/http.mjs:76-112`
274
+ - Modify: `scripts/regression.mjs:6440-6600`
275
+
276
+ - [ ] **Step 1: Add failing mode-matrix tests**
277
+
278
+ Cover direct self-signed TLS, expired certificate rejection, SAN mismatch regeneration, key mismatch regeneration, loopback trusted proxy, forwarded headers from non-loopback, and accepted plaintext LAN. Assert `Secure` cookie only for direct TLS or trusted loopback proxy reporting HTTPS.
279
+
280
+ - [ ] **Step 2: Run to verify RED**
281
+
282
+ Run: `npm run test:regression`.
283
+
284
+ Expected: any uncovered certificate generation race or proxy-origin discrepancy fails with the specific mode fixture.
285
+
286
+ - [ ] **Step 3: Make only evidence-required production changes**
287
+
288
+ Keep generation directories, `.creating`/`.published`, atomic `current.json`, SAN/date/key validation, and CA-scoped runtime requests. Ensure proxy host/proto is consumed only when `--trust-proxy` is enabled and socket remote address is loopback. Do not make TLS mandatory or change the accepted default listener.
289
+
290
+ - [ ] **Step 4: Verify GREEN**
291
+
292
+ Run: `npm run test:unit`.
293
+
294
+ Run: `npm run test:regression`.
295
+
296
+ Expected: all three supported modes pass and every spoofed forwarded-header control fails closed.
297
+
298
+ - [ ] **Step 5: Commit**
299
+
300
+ ```bash
301
+ git add lib/web/tls.mjs lib/web/runtime.mjs lib/runtime/state.mjs lib/web/http.mjs scripts/regression.mjs
302
+ git commit -m "test: close Web transport mode coverage"
303
+ ```
@@ -0,0 +1,71 @@
1
+ # Runtime Buffer GC Two-Phase Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Make manual GC use one server-derived timing snapshot, one unified clock decision, a one-shot runtime file apply, and only then an exact database apply.
6
+
7
+ **Architecture:** The runtime prepares an immutable canonical file plan from a canonical retention period and stores it behind a short-lived root/DB-bound token. The CLI uses the runtime-derived cutoff to capture an exact database plan and merges both plans' would-delete boundaries into one clock observation. Runtime file batches each recheck clock grace under `BEGIN IMMEDIATE`; database rows are finalized only after the runtime reports that every prepared file candidate was processed.
8
+
9
+ **Tech Stack:** Node.js ESM, `node:sqlite`, synchronous filesystem APIs, built-in test runner, HTTP runtime API.
10
+
11
+ ---
12
+
13
+ ### Task 1: Read-only clock prediction
14
+
15
+ **Files:**
16
+ - Modify: `lib/core/coordination/clock-safety.mjs`
17
+ - Test: `test/clock-safety.test.mjs`
18
+
19
+ - [ ] Write a failing test that seeds a watermark, pending gap, and grace row, calls `previewClockSafety`, verifies the same grace decision expected from apply, and compares the complete `meta` table before/after.
20
+ - [ ] Run `node --test test/clock-safety.test.mjs` and verify the missing export fails.
21
+ - [ ] Extract the shared pending-gap decision calculation and implement `previewClockSafety(db, options)` without a transaction or write.
22
+ - [ ] Re-run the focused test and verify preview and observer decisions match while preview leaves `meta` byte-for-byte unchanged.
23
+
24
+ ### Task 2: One-shot runtime plan tokens and bounded apply barriers
25
+
26
+ **Files:**
27
+ - Create: `lib/runtime/buffer-gc-protocol.mjs`
28
+ - Modify: `lib/runtime/buffer-gc.mjs`
29
+ - Create: `test/buffer-gc-protocol.test.mjs`
30
+
31
+ - [ ] Write failing tests for token entropy shape, root/DB binding, replay, expiry, mismatch cleanup, and dry-run plans leaving no token.
32
+ - [ ] Write a failing real SQLite/filesystem test with more than 64 frozen candidates; extend grace from a second connection after the first committed batch and assert exactly the first batch is deleted and the remainder deferred.
33
+ - [ ] Implement `createBufferGcPlanStore` with a 15-second TTL and destructive take semantics.
34
+ - [ ] Implement `applyClockSafeBufferPlan` using batches of 64; each batch opens `BEGIN IMMEDIATE`, runs `observeClockSafetyInTransaction`, checks current grace, and performs only that batch's bounded identity rechecks/unlinks before commit.
35
+ - [ ] Re-run the focused protocol tests.
36
+
37
+ ### Task 3: Authenticated prepare/apply API
38
+
39
+ **Files:**
40
+ - Modify: `bin/hcc.mjs`
41
+ - Modify: `scripts/regression.mjs`
42
+
43
+ - [ ] Add failing authenticated endpoint regressions: legacy or forged `cutoffMs`/`observedAt` bodies return `BAD_REQUEST`; prepare accepts only canonical safe `retentionSec`; apply rejects replay, wrong root/DB, and expired tokens.
44
+ - [ ] Replace the single-call endpoint with `phase: prepare|apply`. Prepare derives `observedAt` and `cutoffMs` from server time, freezes the full actual-directory plan, and returns counts/cutoffs plus a token only for apply. Apply destructively takes the token and runs the bounded barrier.
45
+ - [ ] Add periodic opportunistic expiry cleanup and verify dry-run preparation stores no token.
46
+ - [ ] Run the endpoint regression through the multi-project runtime workflow.
47
+
48
+ ### Task 4: Manual CLI unified decision and apply ordering
49
+
50
+ **Files:**
51
+ - Modify: `bin/hcc.mjs`
52
+ - Modify: `lib/core/coordination/gc-plan.mjs`
53
+ - Modify: `scripts/regression.mjs`
54
+ - Test: `test/gc-plan.test.mjs`
55
+
56
+ - [ ] Add a failing regression with an exact DB history/lock/peer plan plus an actual-directory buffer candidate crossing one pending gap; assert dry-run predicts all would-delete categories deferred without changing `meta`, and apply deletes neither resource.
57
+ - [ ] Add a failing regression where grace is extended after prepare and before apply; assert runtime deletes zero and database finalization never starts.
58
+ - [ ] Add async snapshot cleanup support so the history temp table is always dropped across runtime awaits while preserving primary/cleanup errors.
59
+ - [ ] Split manual GC into prepare, unified preview/observe, runtime/local file apply, and exact DB finalization. Treat runtime unavailability, token failure, incomplete batched apply, subject drift, or clock failure as a barrier that prevents all database deletion.
60
+ - [ ] Ensure result counters report only committed deletes, retain verified-live buffer counts as protected, and report all unprocessed would-delete rows/files as deferred.
61
+ - [ ] Run `node --test test/gc-plan.test.mjs` and the focused manual/runtime regression.
62
+
63
+ ### Task 5: Full verification and amended commit
64
+
65
+ **Files:**
66
+ - Verify all Task 4 files only.
67
+
68
+ - [ ] Run `npm run test:unit` and require zero failures.
69
+ - [ ] Run `npm run test:regression` and require `FULL_REGRESSION_OK`.
70
+ - [ ] Rebuild the Node 24 image and run its full regression.
71
+ - [ ] Run `git diff --cached --check`, selectively stage Task 4 files, and amend `f075976` without staging unrelated worktree changes.