feat(hono): one fetch adapter, and Hono on top of it - #31
Merged
Conversation
Express, Fastify and Next.js had each grown their own copy of the same decision tree: skip-path matching, monitor versus enforce, honeytoken arming, the 429 with a Retry-After, fail-open error handling. Three copies is three places for the branch that matters to be subtly different, and it already had been -- the leftmost-X-Forwarded-For bug survived in two adapters after the WordPress plugin fixed it. createFetchGuard() is that tree written once, over WHATWG Request and Response. It is also the answer to "which framework do you support": Bun, Deno, Astro, Nitro, SvelteKit and Remix all hand you a Request and want a Response, so they need a documented recipe rather than a package. Hono gets a package because it has a middleware contract worth fitting, and because it is the default on Workers, Bun and Deno -- the runtimes the rest of our stack already fronts. The Cloudflare edge sensor has been tagging every request it forwards and readEdgeVerdict() has existed so the origin can act on that tag; there was no origin middleware there to do it. Honeytoken injection works through the fetch shape too, which the Express implementation needed response-stream interception to achieve. Reading and rewriting a Response is enough, so Hono got it for free. check:edge now covers three entry points. Closes WebDecoy/app#727 Closes WebDecoy/app#736
This was referenced Aug 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #727 and #736.
The duplication (#736)
Express, Fastify and Next.js had each grown their own copy of the same decision tree: skip-path matching, monitor-versus-enforce, honeytoken arming, the 429 with a
Retry-After, fail-open error handling.Three copies is three places for the branch that matters to be subtly different — and it already had been. The leftmost-
X-Forwarded-Forbug (#725) survived in two adapters after the WordPress plugin had fixed the same class of bug, precisely because there was no one place to fix it.createFetchGuard()is that tree written once, over WHATWGRequest/Response:It's also the answer to "which framework do you support?". The issue's suggestion was to start with a generic
Requestrecipe rather than adding packages speculatively, and that's what this is: Bun, Deno, Astro, Nitro, SvelteKit and Remix all work through it with no package at all, documented in the README.I did not refactor Express/Fastify/Next onto it in this PR. Their honeytoken injection hooks into framework-specific response streaming — Express intercepts
res.write/res.end, Fastify uses anonSendhook — and that machinery carries hard-won detail (the Angular SSRheadersSentcase, Content-Length correction). Rewriting it on top of a shared core is a behaviour-preserving refactor that deserves its own PR with those tests as the contract, not a rider on a feature. Filed as a follow-up rather than left implied.Hono (#727)
Hono gets a real package because it has a middleware contract worth fitting, and because it's the default on Workers, Bun and Deno — the runtimes the rest of our stack already fronts. The Cloudflare edge sensor has been tagging every request it forwards and
readEdgeVerdict()exists so the origin can act on that tag; there was no origin middleware there to do it.c.get('webdecoy')carries the decision — in monitor mode, which is the default, that's the only place the verdict surfaces, so there's a test for it.Honeytoken injection works through the fetch shape. The Express implementation needed response-stream interception to get there; reading and rewriting a
Responseis enough, so Hono got it for free — including Content-Length correction, which has its own test because a stale one truncates the body at the client.Verification
12 new tests through a real Hono app via
app.request()— the same fetch-shaped entry point Workers and Bun call, so this exercises the actual runtime contract rather than a mock. 406 tests total, 20/20 turbo tasks.check:edgenow covers three entry points (core, Next.js, Hono); Hono is externalised in the gate alongside next/express/fastify.