Quantified by the capstone URL-conformance guard (#3642), which reports and ratchets exactly this weakness rather than hiding it.
The number
The guard matches each SDK-built URL against the union of the four ledgers, and separately counts how many matched only via a ** wildcard row. That count is 60 of ~196 matched calls — roughly a third of the SDK:
| Wildcard row |
Methods it covers |
Namespaces |
* /auth/** |
54 |
auth (26), organizations (~19), oauth (6), a few others |
* /ai/** |
3 |
ai.nlq, ai.suggest, ai.insights |
* /mcp/** |
0 reached by the SDK sweep |
— |
/auth/** alone is 54. Those rows are honest — the routes really are built at runtime (better-auth's own table via plugin-auth; service-ai buildAIRoutes() at plugin start) — but a ** row asserts only that a prefix family is claimed, not that any specific URL in it resolves.
Why this matters concretely
auth.me and auth.refreshToken both build GET /api/v1/auth/get-session. The guard confirms the prefix is claimed; it cannot confirm better-auth actually exposes get-session under that name. Rename it upstream — better-auth is a third-party dependency on its own release cadence — and 26 auth.* methods start 404-ing with every guard in the repo still green. That is precisely the #3584 / #3611 / #3636 failure mode, just with an external trigger instead of an internal one, and it is concentrated on the most security-relevant namespace in the SDK.
The exposure is real rather than theoretical: this repo already had to chase better-auth 1.7 column drift in #3624 / #3647.
What would close it
plugin-auth mounts better-auth's handler; the route table is obtainable at runtime rather than by hand:
- Enumerate at start and ledger it — the same seam tranche 3 used for the autonomous service mounts: drive the plugin's registration against a capturing mock
IHttpServer, diff the captured list against an auth-route-ledger.ts in plugin-auth. Whether better-auth exposes its table cleanly is the thing to check first; if it mounts one catch-all, this collapses to option 2.
- Pin the subset the SDK actually calls — ~30 concrete paths — and assert each resolves against a booted auth plugin. Narrower than a full ledger, but it covers exactly what would break, and an integration-style assertion catches an upstream rename that a static list cannot.
Option 2 is likely the better value here, because the risk is upstream drift and only a live check detects that.
Doing either lowers the ratchet in client-url-conformance.test.ts (currently toBeLessThanOrEqual(60)); the bound is written so it can only shrink.
Refs: #3642 (the guard and the ratchet), #3563 / #3587 / #3636 (the three ledger tranches), #3624 / #3647 (prior better-auth drift).
Quantified by the capstone URL-conformance guard (#3642), which reports and ratchets exactly this weakness rather than hiding it.
The number
The guard matches each SDK-built URL against the union of the four ledgers, and separately counts how many matched only via a
**wildcard row. That count is 60 of ~196 matched calls — roughly a third of the SDK:* /auth/**auth(26),organizations(~19),oauth(6), a few others* /ai/**ai.nlq,ai.suggest,ai.insights* /mcp/**/auth/**alone is 54. Those rows are honest — the routes really are built at runtime (better-auth's own table viaplugin-auth;service-ai buildAIRoutes()at plugin start) — but a**row asserts only that a prefix family is claimed, not that any specific URL in it resolves.Why this matters concretely
auth.meandauth.refreshTokenboth buildGET /api/v1/auth/get-session. The guard confirms the prefix is claimed; it cannot confirm better-auth actually exposesget-sessionunder that name. Rename it upstream — better-auth is a third-party dependency on its own release cadence — and 26auth.*methods start 404-ing with every guard in the repo still green. That is precisely the #3584 / #3611 / #3636 failure mode, just with an external trigger instead of an internal one, and it is concentrated on the most security-relevant namespace in the SDK.The exposure is real rather than theoretical: this repo already had to chase better-auth 1.7 column drift in #3624 / #3647.
What would close it
plugin-authmounts better-auth's handler; the route table is obtainable at runtime rather than by hand:IHttpServer, diff the captured list against anauth-route-ledger.tsinplugin-auth. Whether better-auth exposes its table cleanly is the thing to check first; if it mounts one catch-all, this collapses to option 2.Option 2 is likely the better value here, because the risk is upstream drift and only a live check detects that.
Doing either lowers the ratchet in
client-url-conformance.test.ts(currentlytoBeLessThanOrEqual(60)); the bound is written so it can only shrink.Refs: #3642 (the guard and the ratchet), #3563 / #3587 / #3636 (the three ledger tranches), #3624 / #3647 (prior better-auth drift).