You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Completes the review backlog.
Boot ordering (10). Under CLI transport, unpause and catch-up ran before
the listeners attached — and an event delivered with no session attached
is discarded rather than queued, so recovery replayed the outage window
into nothing. Recovery is now driven per route by the listener's
`onConnected`, which also means a tunnel that drops and returns recovers
its own window instead of waiting for the next boot. Clearing the
disconnect cursor on "replay accepted" is sound in that order: with a
session attached, a later failure is CLI_UNAVAILABLE and stays in the
retry pipeline. Modes with no attach event recover immediately, as
before. `start()` is now idempotent — a second call would have orphaned
children with no handle left to kill them.
Recovery (14) settled the ledger row before asking Hookdeck to redeliver;
a crash between the two left a terminal row, no orphan, no Issue and no
dead-letter record. The order is inverted, and the over-budget path
records its breadcrumb before settling.
The in-dispatch capacity guard (12) returned `deferred`, which promises
that a background run owns the row and will settle it. No run started, so
the row stayed `running` forever, invisible to in-process recovery.
Replay protection (15). The HMAC covers the body only: the event id and
attempt count are unsigned, the secret is project-level, and no timestamp
is signed. The comment claiming otherwise now says exactly what the
scheme proves, the README lists it under Limitations, and an implausible
attempt count is discarded rather than recorded — otherwise anyone able
to replay a captured body could retire every legitimate redelivery of an
event as a duplicate.
A configured-but-unresolvable API key (16) reported "no API key is
configured", sending someone to fix a config that was already correct. A
secretRef needs the host secret runtime, which only the Gateway's service
start receives, so tools now distinguish "unavailable here" from
"absent".
Rate limits (17) get their own code and carry Retry-After; a replay
batch stops on the first 429 and says how many of how many ran, rather
than reporting a generic error per remaining event.
Also: the raw-body timeout destroys the stream instead of buffering on;
the readiness-timeout kill escalates to SIGKILL like stop() does; backoff
jitter is clamped so maxDelayMs is really the maximum; prerelease
versions compare per semver, not lexically, so beta.10 is above beta.9;
the config-error route honours a custom basePath and still registers
hookdeck_status, since a config error is exactly when someone asks
whether webhooks are working; and `deliver`, `lane` and `sync` warn at
startup that the TaskFlow transport does not carry them.
raw-body.ts and node-spawn.ts had no tests; both now have suites, the
latter against a fake child_process covering the error-without-exit case.
559 tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: README.md
+4-2Lines changed: 4 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -341,7 +341,9 @@ Signature headers and resolved secrets are redacted from logs.
341
341
342
342
Not yet implemented:
343
343
344
-
-**No completion tracking for agent turns.** See [Agent turns](#agent-turns) — `sync` and `maxAgentRetries` need a completion hook the TaskFlow transport does not provide.
344
+
-**No completion tracking for agent turns.** See [Agent turns](#agent-turns). Agent turns run through TaskFlow `run_task`, which exposes flow state rather than a completion signal, so `ackMode: "sync"` behaves as `async_retry`, and `deliver` and `lane` are recorded but not passed to the turn. Each is warned about at startup rather than failing quietly.
345
+
-**A signature authenticates the body, not the headers.** Hookdeck's HMAC covers the raw body only, with a project-level secret and no signed timestamp. So the event id and attempt count arrive unauthenticated, and a captured `(body, signature)` pair stays valid. Deduplication is what provides replay protection, an implausible attempt count is discarded rather than recorded, and provider verification at the Source is the layer that keeps unsigned traffic out in the first place.
346
+
-**List endpoints read the first page only.**`hookdeck_issues` and `hookdeck_recent_deliveries` report a real total from the count endpoint, but return one page of results.
345
347
346
348
## Development
347
349
@@ -351,7 +353,7 @@ npm test
351
353
npm run typecheck
352
354
```
353
355
354
-
526 tests, no Gateway or Hookdeck account required. Signature vectors are computed independently with `openssl`, `test/http-integration.test.ts` exercises the pipeline over a real socket including multi-byte UTF-8 and multi-chunk bodies, the store suites inject write failures at an exact call to prove the degradation rule, and `test/store-io.test.ts` runs against a real filesystem because that is the only place durability actually lives.
356
+
559 tests, no Gateway or Hookdeck account required. Signature vectors are computed independently with `openssl`, `test/http-integration.test.ts` exercises the pipeline over a real socket including multi-byte UTF-8 and multi-chunk bodies, the store suites inject write failures at an exact call to prove the degradation rule, and `test/store-io.test.ts` runs against a real filesystem because that is the only place durability actually lives.
0 commit comments