Commit 0603841
Remote config and join flow (LLP 0025) (#98)
* Add LLP 0022: remote config and join flow spec
Specifies the client half of centrally-managed gateway configuration:
join sequence, config pull loop, seed-config mode, staged-restart apply
semantics, hash-pinned install-on-config, and last-known-good rollback
with post-apply probation.
Supporting amendments:
- LLP 0003: config apply engine added to the core-owns list
- LLP 0011: join added as a non-interactive entry point
- LLP 0017: staged restart for config replacement + installer relaunch
requirement
- central proto.md: policy tokens, running-config If-None-Match
convergence semantics, hash-pinned plugins, 404 demoted to legacy
- LLP 0000: subsystem map updated
- notes-archive: round-1 review record for LLP 0022
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Renumber remote-config spec to LLP 0023
Origin master took 0022 for iceberg-export-partitioning (#91) while the
join-flow spec was drafted on a local branch; renumber to the next free
slot and update all cross-references.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Implement remote config and join flow (LLP 0023)
Kernel side:
- Config apply engine (src/core/config/apply.js): validate -> install
pinned plugins -> persist to A/B slots -> atomic pointer flip ->
staged restart, with last-known-good rollback, bad-etag re-apply
backoff, and structured rollback reasons. The served etag lives in a
per-slot sidecar written before the flip, so the document and its
etag commit on the same rename in both directions.
- Narrow ctx.configControl facade (stage / confirmPoll / runningEtag)
exposed only in daemon mode; CLI boots leave it undefined so
transport plugins keep their pull loops off.
- Kernel-owned probation watchdog: window max(3 x poll_interval, 120s)
sized from the staged document, cleared only by a confirmed poll,
expiry also evaluated at boot before plugin activation (crashloop
case), orphaned markers from a crash before the flip are discarded.
- Staged restart: DAEMON_RESTART_EXIT_CODE (75) for foreground
invokers; service managers already relaunch via KeepAlive /
Restart=always (now pinned by tests). Sinks are closed on shutdown
so plugin timers stop.
- Hash-pinned install-on-config through the LLP 0007 path; pin
verified against the staged artifact before the install commits.
Bundled first-party plugins: version strict, hash skipped.
- Config shape: plugin entries accept version / artifact_hash / source
pins.
- hypaware status: remote-config section (probation, last rollback +
reason, remembered bad etag, running etag) in text and JSON.
- hypaware join <url> [token] [--token-file|stdin] [--no-daemon]:
writes the mode-0600 seed config and runs the daemon install — a
wrapper over the two existing steps, not a second code path.
Central plugin (transport only):
- Config pull loop (central/src/config_client.js): immediate pull on
bootstrap success, steady timer, If-None-Match always the running
config's etag, 401 refresh-retry, 404 legacy backoff, 429/503
Retry-After + linear backoff, 1 MiB body cap.
- Dropped the never-wired config_etag_path option (the etag is
kernel-managed and read through the facade).
Settled LLP 0023 open questions: poll default 300s, probation floor
120s, max document size 1 MiB. proto.md sidecar wording updated; the
restart exit code recorded in LLP 0017.
Tests: apply-engine state machine (18), pull loop (10), join command
(5), installer relaunch defaults; join_flow_remote_config hermetic
smoke drives seed -> bootstrap -> pull -> apply -> restart ->
probation clear against a stub server with convergence assertions.
Note: central_forward_outbox was already failing on origin/master
(empty ingest rows) before this change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Flip LLP 0023 to Active
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Address dual-review findings on remote config (PR #98)
Apply engine (LLP 0023):
- Reorder stage() to shape-check -> install pinned plugins -> full
validation, so a served config can name a not-yet-installed plugin
(catalog-backed validation only knows a plugin once it is installed).
LLP 0023 install-on-config section updated with the ordering rationale.
Config pull transport:
- Enforce the 1 MiB cap before buffering: oversized Content-Length is
rejected without reading, chunked bodies stream through a byte counter
that cancels at the cap.
- Per-poll AbortController with a 30s request deadline covering request
and body read; stop() aborts an in-flight poll after a 1s drain grace
so a stalled config GET cannot wedge daemon shutdown.
Tests:
- New test/core/config-apply-deps.test.js covering the real pin
enforcement: bundled version mismatch, bundled hash exemption,
satisfied-lock skip, artifact hash mismatch via a local git fixture,
and install-then-validate over a fresh catalog.
- Pull-loop tests for Content-Length pre-reject, chunked cap cancel,
stop() abort, request deadline, 429/503 Retry-After, and
parseRetryAfter (now exported).
- Engine ordering test (install before validate) and shape-gate test.
Typecheck and style:
- Fix broken inline type-import path and literal exit-code comparisons
that broke npm run typecheck; replace remaining inline import() types
with top-of-file @import blocks.
- Reword closeAllSinks JSDoc: identity refresh is lazy and has no timer.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Don't unref the poll deadline and stop-grace timers
An unref'd timer can't fire once a wedged fetch is the only live
handle: the event loop drains, so the deadline/grace abort never
happens — in CI this surfaced as node:test cancelling the pull-loop
tests with 'Promise resolution is still pending but the event loop has
already resolved'. Both timers are cleared as soon as the poll
settles, and the loop's policy is no-unref anyway.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Renumber remote-config spec to LLP 0025 (0024 is claimed by open PR #99)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>1 parent 322aaed commit 0603841
31 files changed
Lines changed: 3989 additions & 35 deletions
File tree
- hypaware-core
- plugins-workspace/central
- src
- smoke/flows
- llp
- notes-archive/llp-reviews
- src/core
- cli
- config
- daemon
- runtime
- test
- core
- plugins
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
323 | 323 | | |
324 | 324 | | |
325 | 325 | | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
326 | 336 | | |
327 | 337 | | |
328 | 338 | | |
329 | 339 | | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
330 | 380 | | |
331 | 381 | | |
332 | 382 | | |
| |||
405 | 455 | | |
406 | 456 | | |
407 | 457 | | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
408 | 478 | | |
409 | 479 | | |
410 | 480 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
| |||
26 | 27 | | |
27 | 28 | | |
28 | 29 | | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
29 | 34 | | |
30 | 35 | | |
31 | 36 | | |
| |||
55 | 60 | | |
56 | 61 | | |
57 | 62 | | |
58 | | - | |
| 63 | + | |
59 | 64 | | |
60 | 65 | | |
61 | 66 | | |
62 | 67 | | |
63 | 68 | | |
64 | 69 | | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
65 | 92 | | |
66 | 93 | | |
67 | 94 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
33 | | - | |
34 | | - | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
35 | 36 | | |
36 | 37 | | |
37 | 38 | | |
| |||
78 | 79 | | |
79 | 80 | | |
80 | 81 | | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
81 | 88 | | |
82 | 89 | | |
83 | 90 | | |
| |||
89 | 96 | | |
90 | 97 | | |
91 | 98 | | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
92 | 104 | | |
93 | | - | |
94 | | - | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
95 | 109 | | |
96 | 110 | | |
97 | 111 | | |
98 | | - | |
99 | | - | |
100 | | - | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
101 | 116 | | |
102 | 117 | | |
103 | 118 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
58 | 58 | | |
59 | 59 | | |
60 | 60 | | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | 61 | | |
66 | 62 | | |
67 | 63 | | |
| |||
0 commit comments