Summary
Closing #1166 took three passes over the route table, and each pass found routes the previous one missed:
- The issue named
/api/v1/servers and /api/v1/config.
- An adversarial review found
/api/v1/tools, /api/v1/index/search, /api/v1/profiles, /api/v1/annotations/coverage, /api/v1/security/scans and /api/v1/status.
- A cross-model review then found the entire
/api/v1/servers/{id}/* read subtree (tools, logs, tool-calls, scan/*, integrity, tools/export), the whole /api/v1/activity and /api/v1/tool-calls family, /api/v1/sessions, and /api/v1/stats/tokens.
Hand-enumerating a route table does not converge. The fix that ships gates the subtree with a chi middleware so new sub-resources inherit it, but nothing stops a new top-level route being added unscoped.
Proposal
A test that walks the production router (chi.Walk over s.router), collects every GET under /api/v1, and asserts each is either:
- demonstrably scope-filtered (exercised by a scoped-caller test), or
- on an explicit allowlist with a written reason (e.g. "admin-only, denied by requireAdminRead", "carries no server identity").
A new route then fails the build until someone classifies it, and the allowlist becomes the reviewable record of what is deliberately unscoped.
Guard against vacuity: the test must assert it found a non-trivial number of routes, or a chi.Walk that silently matches nothing passes forever.
Why it is worth the effort
This is the durable fix for the enumerate-and-patch problem. Two of the routes in pass 3 (/servers/{id}/logs, /activity) leaked strictly more than the enumeration the original issue was about — server stdout/stderr, and tool-call arguments and responses.
Summary
Closing #1166 took three passes over the route table, and each pass found routes the previous one missed:
/api/v1/serversand/api/v1/config./api/v1/tools,/api/v1/index/search,/api/v1/profiles,/api/v1/annotations/coverage,/api/v1/security/scansand/api/v1/status./api/v1/servers/{id}/*read subtree (tools, logs, tool-calls, scan/*, integrity, tools/export), the whole/api/v1/activityand/api/v1/tool-callsfamily,/api/v1/sessions, and/api/v1/stats/tokens.Hand-enumerating a route table does not converge. The fix that ships gates the subtree with a chi middleware so new sub-resources inherit it, but nothing stops a new top-level route being added unscoped.
Proposal
A test that walks the production router (
chi.Walkovers.router), collects everyGETunder/api/v1, and asserts each is either:A new route then fails the build until someone classifies it, and the allowlist becomes the reviewable record of what is deliberately unscoped.
Guard against vacuity: the test must assert it found a non-trivial number of routes, or a
chi.Walkthat silently matches nothing passes forever.Why it is worth the effort
This is the durable fix for the enumerate-and-patch problem. Two of the routes in pass 3 (
/servers/{id}/logs,/activity) leaked strictly more than the enumeration the original issue was about — server stdout/stderr, and tool-call arguments and responses.