Found by the independent review of #167 while verifying the premise that route's memory bound rests on. Not a defect — the cap is real and effective — so deliberately NOT milestoned 1.0.0.
/webhook/{uuid} carries only ReceiverRateLimit(). Unlike the four page route groups, it has no MaxBodySize middleware. Its 1 MB cap is enforced inside the handler instead, at internal/handlers/webhook.go:145-166, via io.LimitReader plus a length check.
That works today and the reviewer confirmed it. The problem is the asymmetry is undocumented and invisible at the routing layer: someone reading internal/server/routes.go sees four groups with an explicit body-size middleware and one without, and would reasonably conclude the receiver is uncapped — or, worse, edit the handler assuming middleware coverage that is not there and silently remove the only bound on the one unauthenticated, internet-facing endpoint.
Several units this cycle have depended on that 1 MB cap being real: #135 sized the event-log render bound from it, and #157's whole memory argument rests on it.
Definition of done
Either:
Move the receiver onto the same MaxBodySize middleware the page groups use, so the cap is visible where every other route's cap is — checking first that this does not change the 413 semantics or the error the handler currently returns, since the receiver's response shape is part of its contract with senders; or
Keep it in the handler and add a comment at the route registration in internal/server/routes.go saying where the cap actually lives and why, so the omission reads as deliberate rather than forgotten.
State which you chose and why. If you move it, add a test asserting an oversized body is rejected at the middleware rather than the handler; if you document it, add a test that pins the handler-level cap so it cannot be removed silently.
Implementation requirements
Branch from next, PR based on next, single commit, title ending (closes #N).
Run make bootstrap in a fresh clone before gating — browser assets are fetched at build time and not committed.
Gate on make check plus the Docker lint path with the cache defeated. All linting runs in Docker, never on the host.
Found by the independent review of https://git.eeqj.de/sneak/webhooker/pulls/167 while verifying the premise that route's memory bound rests on. Not a defect — the cap is real and effective — so deliberately NOT milestoned 1.0.0.
`/webhook/{uuid}` carries only `ReceiverRateLimit()`. Unlike the four page route groups, it has **no `MaxBodySize` middleware**. Its 1 MB cap is enforced inside the handler instead, at `internal/handlers/webhook.go:145-166`, via `io.LimitReader` plus a length check.
That works today and the reviewer confirmed it. The problem is the asymmetry is undocumented and invisible at the routing layer: someone reading `internal/server/routes.go` sees four groups with an explicit body-size middleware and one without, and would reasonably conclude the receiver is uncapped — or, worse, edit the handler assuming middleware coverage that is not there and silently remove the only bound on the one unauthenticated, internet-facing endpoint.
Several units this cycle have depended on that 1 MB cap being real: https://git.eeqj.de/sneak/webhooker/issues/135 sized the event-log render bound from it, and https://git.eeqj.de/sneak/webhooker/issues/157's whole memory argument rests on it.
## Definition of done
Either:
- Move the receiver onto the same `MaxBodySize` middleware the page groups use, so the cap is visible where every other route's cap is — checking first that this does not change the 413 semantics or the error the handler currently returns, since the receiver's response shape is part of its contract with senders; or
- Keep it in the handler and add a comment at the route registration in `internal/server/routes.go` saying where the cap actually lives and why, so the omission reads as deliberate rather than forgotten.
State which you chose and why. If you move it, add a test asserting an oversized body is rejected at the middleware rather than the handler; if you document it, add a test that pins the handler-level cap so it cannot be removed silently.
## Implementation requirements
- Branch from `next`, PR based on `next`, single commit, title ending ` (closes #N)`.
- Do not modify `TODO.md` (see https://git.eeqj.de/sneak/webhooker/issues/112).
- Run `make bootstrap` in a fresh clone before gating — browser assets are fetched at build time and not committed.
- Gate on `make check` plus the Docker lint path with the cache defeated. All linting runs in Docker, never on the host.
clawbot
self-assigned this 2026-08-18 00:41:49 +02:00
Plan. The receiver is now /h/{uuid} (#367). Take the issue's second option: keep the 1 MB cap in the handler, which owns the response senders see, and add a comment at the route registration in internal/server/routes.go saying where the cap lives and why the receiver has no MaxBodySize. A test sends a body one byte over the cap through the production router and pins the refusal (status and body), so the cap cannot be removed silently; if such a test already exists, the comment names it. The first option would route a refusal through middleware that, since #382, renders error pages for admin routes, and would risk changing the response senders get.
Model: opus-5-5
Plan. The receiver is now `/h/{uuid}` (https://git.eeqj.de/sneak/webhooker/issues/367). Take the issue's second option: keep the 1 MB cap in the handler, which owns the response senders see, and add a comment at the route registration in `internal/server/routes.go` saying where the cap lives and why the receiver has no `MaxBodySize`. A test sends a body one byte over the cap through the production router and pins the refusal (status and body), so the cap cannot be removed silently; if such a test already exists, the comment names it. The first option would route a refusal through middleware that, since https://git.eeqj.de/sneak/webhooker/issues/382, renders error pages for admin routes, and would risk changing the response senders get.
Model: opus-5-5
#429 keeps the receiver's 1 MB body cap in the handler, as planned. The /h/{uuid} registration in internal/server/routes.go now has a comment saying where the cap lives and why the route has no MaxBodySize. The handler's body-reading function says it is the receiver's only cap. No existing test pinned the cap, so a new routing test, TestReceiver_OversizeBodyRefused, sends a body of exactly 1 MB and one a byte over through the production router and checks that the second gets the handler's 413 and message.
Judgement call: the test writes the cap as a literal 1 MB rather than reading the handler's unexported constant, so changing the cap means changing the test too.
Model: opus-5-5
https://git.eeqj.de/sneak/webhooker/pulls/429 keeps the receiver's 1 MB body cap in the handler, as planned. The `/h/{uuid}` registration in `internal/server/routes.go` now has a comment saying where the cap lives and why the route has no `MaxBodySize`. The handler's body-reading function says it is the receiver's only cap. No existing test pinned the cap, so a new routing test, `TestReceiver_OversizeBodyRefused`, sends a body of exactly 1 MB and one a byte over through the production router and checks that the second gets the handler's 413 and message.
Judgement call: the test writes the cap as a literal 1 MB rather than reading the handler's unexported constant, so changing the cap means changing the test too.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Found by the independent review of #167 while verifying the premise that route's memory bound rests on. Not a defect — the cap is real and effective — so deliberately NOT milestoned 1.0.0.
/webhook/{uuid}carries onlyReceiverRateLimit(). Unlike the four page route groups, it has noMaxBodySizemiddleware. Its 1 MB cap is enforced inside the handler instead, atinternal/handlers/webhook.go:145-166, viaio.LimitReaderplus a length check.That works today and the reviewer confirmed it. The problem is the asymmetry is undocumented and invisible at the routing layer: someone reading
internal/server/routes.gosees four groups with an explicit body-size middleware and one without, and would reasonably conclude the receiver is uncapped — or, worse, edit the handler assuming middleware coverage that is not there and silently remove the only bound on the one unauthenticated, internet-facing endpoint.Several units this cycle have depended on that 1 MB cap being real: #135 sized the event-log render bound from it, and #157's whole memory argument rests on it.
Definition of done
Either:
MaxBodySizemiddleware the page groups use, so the cap is visible where every other route's cap is — checking first that this does not change the 413 semantics or the error the handler currently returns, since the receiver's response shape is part of its contract with senders; orinternal/server/routes.gosaying where the cap actually lives and why, so the omission reads as deliberate rather than forgotten.State which you chose and why. If you move it, add a test asserting an oversized body is rejected at the middleware rather than the handler; if you document it, add a test that pins the handler-level cap so it cannot be removed silently.
Implementation requirements
next, PR based onnext, single commit, title ending(closes #N).TODO.md(see #112).make bootstrapin a fresh clone before gating — browser assets are fetched at build time and not committed.make checkplus the Docker lint path with the cache defeated. All linting runs in Docker, never on the host.Plan. The receiver is now
/h/{uuid}(#367). Take the issue's second option: keep the 1 MB cap in the handler, which owns the response senders see, and add a comment at the route registration ininternal/server/routes.gosaying where the cap lives and why the receiver has noMaxBodySize. A test sends a body one byte over the cap through the production router and pins the refusal (status and body), so the cap cannot be removed silently; if such a test already exists, the comment names it. The first option would route a refusal through middleware that, since #382, renders error pages for admin routes, and would risk changing the response senders get.Model: opus-5-5
#429 keeps the receiver's 1 MB body cap in the handler, as planned. The
/h/{uuid}registration ininternal/server/routes.gonow has a comment saying where the cap lives and why the route has noMaxBodySize. The handler's body-reading function says it is the receiver's only cap. No existing test pinned the cap, so a new routing test,TestReceiver_OversizeBodyRefused, sends a body of exactly 1 MB and one a byte over through the production router and checks that the second gets the handler's 413 and message.Judgement call: the test writes the cap as a literal 1 MB rather than reading the handler's unexported constant, so changing the cap means changing the test too.
Model: opus-5-5