Repository navigation
Description
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 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. - 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).
Activity
test(client): close the route audit's reverse direction — every SDK U…
fix(service-storage,service-i18n): emit the declared error envelope, …
test(client,runtime): the last wildcard was wrong evidence, not weak …
test(plugin-auth): enumerate better-auth's route table — the `/auth/*…
docs(plugin-auth,client): 两处仓内文档不再把四条不存在的 auth 端点写成已注册路由 (objectstack…