Skip to content

docs: generate the route reference from every place routes are registered - #191

Merged
vishr merged 1 commit into
mainfrom
docs/route-reference-completeness
Aug 24, 2026
Merged

docs: generate the route reference from every place routes are registered#191
vishr merged 1 commit into
mainfrom
docs/route-reference-completeness

Conversation

@vishr

@vishr vishr commented Aug 24, 2026

Copy link
Copy Markdown
Member

The route reference was generated by scanning internal/api alone. Its
read_when invites an operator to audit what an instance exposes, and it
omitted sixteen routes — so someone using it for exactly that concluded
/debug/pprof/* and /-/metrics were not served.

What was missing

Eleven operational, protocol and SPA routes registered in cmd/fanout:
/-/metrics, six /debug/pprof/*, /mcp, /api/mcp, and the SPA catch-all.

And five /api/agent routes in the agent runtime. The page's caveat did not
mention those at all — it named only the first group, so the table was missing
routes the page did not admit to missing.

POST /api/agent was missing for a second reason worth separating out. A
group's own root is registered as group.POST("", ...), and the collector
required a leading slash — so the one route that actually runs the investigator
was dropped silently rather than refused. Same shape as the group-prefix bug
fixed in #187, in the same function: a path the collector could not read became
an omission instead of an error.

Routes went 45 → 61.

What this changes

  • The scan list is a flag naming each directory
    (internal/api,internal/agent,cmd/fanout). A directory in it that registers
    nothing fails the run, so the list going stale is loud rather than a quietly
    shorter table. It counts what a directory registers rather than how much it
    adds to the total — a route registered in two places would otherwise dedupe to
    nothing new and read as a directory registering none.
  • An empty relative path is a real route when the receiver is a known group.
  • Any collapses to one row only when the middleware answers identically for
    every method it distinguishes. Where it would not, the build fails rather than
    publish a requirement that is wrong for some verb. Both current Any routes
    qualify.
  • The page states what it covers instead of carrying a caveat, and names which
    groups are conditional: pprof off by default, MCP on by default, the agent
    only with a provider key. That is the distinction an exposure audit needs, and
    the previous text gave none of it.

The property that matters is unchanged: paths are parsed from the source, the
authorization requirement is asked of the middleware. A generator that re-read
classifyRoute would be a second authorization model free to drift from the one
that runs.

Verification

Each guard was verified by breaking it deliberately:

Guard Broken by Result
Unregistered group prefix removing Runtime.Register from groupPrefixes fails, names the fix
Directory registers nothing adding . to the scan list fails, names the directory
Per-directory counting reverting to counting newness new test fails

just check green. Six collector tests, including one that pins
POST /api/agent and one that pins the routes registered outside
internal/api, so neither omission can return quietly.

Two things I corrected mid-work

  • My first version of the conditional-routes note claimed only pprof was
    conditional. Three groups are. That is the same class of error as the bug
    being fixed.
  • I had written that an unconfigured /api/agent returns 404 rather than 403.
    With e.Use middleware and a GET /* catch-all registered that is likely
    wrong, and I could not verify it, so it is gone rather than guessed.

Also fixes a stale error message pointing at routes.go for groupPrefixes,
which lives in groups.go.

Closes #188.

…ered

The route reference was built by scanning `internal/api` alone, so it omitted
sixteen routes on a page whose own `read_when` invites an operator to audit
what an instance exposes. Someone using it to enumerate exposure concluded
`/debug/pprof/*` and `/-/metrics` were not served.

Missing were the eleven operational, protocol and SPA routes wired up in
`cmd/fanout` — `/-/metrics`, six `/debug/pprof/*`, `/mcp`, `/api/mcp`, and the
SPA catch-all — and the five `/api/agent` routes in the agent runtime, which
the caveat did not even mention because it named only the first group.

`POST /api/agent` was missing for a second reason worth stating separately: a
group's own root is registered as `group.POST("", ...)`, and the collector
required a leading slash, so the one route that actually runs the investigator
was dropped silently rather than refused. An empty relative path is now a real
route when the receiver is a known group.

`Any` registrations are collapsed to one row only when the middleware gives
every method the same answer; where it would not, the build fails rather than
publish a requirement that is wrong for some verb. Both current `Any` routes
qualify.

The scan list is a flag with each directory named. A directory in it that
registers nothing is an error, so the list going stale is loud rather than a
quietly shorter table — the same property the existing per-run check had, moved
to per-directory. It counts what a directory registers rather than how much it
adds to the total, because a route registered in two places would dedupe to
nothing new and read as a directory registering none.

The page now says what it covers instead of carrying a caveat, and states which
groups are conditional: pprof off by default, MCP on by default, the agent only
with a provider key. That distinction is what an exposure audit needs, and the
previous text gave none of it.

Each guard was verified by breaking it: an unregistered group prefix, a scan
directory that registers nothing, and the per-directory count — the last fails
the build over a duplicated directory if it counts newness instead. Routes went
45 to 61.

Closes #188.
@vishr
vishr merged commit 43c5d99 into main Aug 24, 2026
8 checks passed
@vishr
vishr deleted the docs/route-reference-completeness branch August 24, 2026 14:00
vishr added a commit that referenced this pull request Aug 24, 2026
…ered (#191)

The route reference was built by scanning `internal/api` alone, so it omitted
sixteen routes on a page whose own `read_when` invites an operator to audit
what an instance exposes. Someone using it to enumerate exposure concluded
`/debug/pprof/*` and `/-/metrics` were not served.

Missing were the eleven operational, protocol and SPA routes wired up in
`cmd/fanout` — `/-/metrics`, six `/debug/pprof/*`, `/mcp`, `/api/mcp`, and the
SPA catch-all — and the five `/api/agent` routes in the agent runtime, which
the caveat did not even mention because it named only the first group.

`POST /api/agent` was missing for a second reason worth stating separately: a
group's own root is registered as `group.POST("", ...)`, and the collector
required a leading slash, so the one route that actually runs the investigator
was dropped silently rather than refused. An empty relative path is now a real
route when the receiver is a known group.

`Any` registrations are collapsed to one row only when the middleware gives
every method the same answer; where it would not, the build fails rather than
publish a requirement that is wrong for some verb. Both current `Any` routes
qualify.

The scan list is a flag with each directory named. A directory in it that
registers nothing is an error, so the list going stale is loud rather than a
quietly shorter table — the same property the existing per-run check had, moved
to per-directory. It counts what a directory registers rather than how much it
adds to the total, because a route registered in two places would dedupe to
nothing new and read as a directory registering none.

The page now says what it covers instead of carrying a caveat, and states which
groups are conditional: pprof off by default, MCP on by default, the agent only
with a provider key. That distinction is what an exposure audit needs, and the
previous text gave none of it.

Each guard was verified by breaking it: an unregistered group prefix, a scan
directory that registers nothing, and the per-directory count — the last fails
the build over a duplicated directory if it counts newness instead. Routes went
45 to 61.

Closes #188.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Route reference claims completeness but omits every route registered outside internal/api

1 participant