feat(plugin-hono-server)!: 删除 CRUD/discovery 便利面 —— 插件回归纯传输适配器(#4073 终点) - #4296
Merged
os-zhuang merged 4 commits intoJul 31, 2026
Merged
Conversation
…ugin is a transport adapter (#4073) Completes the retirement. HonoServerPlugin now owns the socket, the middleware and the three current-user endpoints, and nothing else. The data and discovery APIs have one owner each — @objectstack/rest and the runtime dispatcher (ADR-0076 D11). Deleted: raw C+R `/api/v1/data/:object` (+ `/:id`), this plugin's third `/api/v1/discovery` + `/.well-known/objectstack` payload, and the `registerStandardEndpoints` flag itself — gone, not defaulted off. With it go `DISCOVERY_ROUTE_SEGMENTS` and six imports (`IDataEngine`, `shouldDenyAnonymous`, both `ANONYMOUS_DENY_*`, `ApiRoutes`, `makeExecutionContextResolver`) that existed only to serve it. Unaffected: `/auth/me/*` + `/me/apps` (sole supply, unconditional since #4144), and every composed host — `os serve`, `objectstack dev`, cloud's objectos — which mount an owner that already answered these routes byte-identically either way (#4260). A host with NO owner keeps the boot warn, reworded to name both remedies now that opting in is not one of them. Tests, each decided on what it was actually pinning rather than swept: - `hono-discovery` / `hono-anonymous-deny` — deleted. Both existed wholly to test the retired routes. #2567's invariant is not lost with the latter: the gate lives in core and REST enforces it on the surface that survives. - `server-timing-e2e` — KEPT, repointed at `/auth/me/permissions`. #3361's regression value is "perf middleware + a handler that resolves the principal itself", not the route; the permission-set fixtures already fit. Its anonymous case asserts 200-without-timings instead of 401, because that endpoint answers anonymous callers rather than denying them — the invariant is the closed gate. - `client.hono` — KEPT, repointed at a REST-composed host. It is the ONLY coverage of `client.connect()` and the discovery handshake, so deleting it would have cost real signal; its #4018 assertions now read against REST's registry-driven discovery, where `metadata` IS advertised and answers. - `hono-standard-endpoints-default` → `hono-transport-only`. Its subject was the flag's default; it now pins the end state, including that passing the removed option resurrects nothing. - The two cases pinning `/me/*` ordering relative to the CRUD block are gone, with a note: #4088 made plugin-auth's catch-all fall through, so the ordering fragility they guarded no longer exists and is pinned directly in `auth-catchall-fallthrough.test.ts`. - Seven cross-package call sites simply drop the option. AGENTS.md gains the four rules this thread paid for — one route one owner, explicit composition over default magic, absence must be loud, machine-readable surfaces must not lie — plus the verification note: "who serves this path" is a question about the composed, PROVISIONED runtime. #4073 was answered wrongly three times, once per shortcut around that. Verified: plugin-hono-server 135/135, client 3 suites 9/9, runtime 4 integration suites 19/19, http-conformance 46/46, full build 71/71, package tsc clean, changed-file lint 0/0. Net −678 lines. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016gEeLZ4oTeSXG6fKG1r3vd
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
…-permissions-provider-iwtkqm # Conflicts: # packages/plugins/plugin-hono-server/src/hono-plugin.ts
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 9 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…-deny ledger
The Dogfood Regression Gate caught the deletion, which is the ledger doing its
job:
STALE covers — surface no longer in source: data:hono-plugin.ts:POST /data/:object
data:hono-plugin.ts:GET /data/:object/:id
data:hono-plugin.ts:GET /data/:object
`anonymous-deny-hono-data` classified an entry point that no longer exists, so
the row goes with its surface. Three places referenced it and each needed a
different answer:
- the matrix row itself — deleted, with a note on the section above it that the
#2567 invariant is UNCHANGED: it has one fewer implementation to hold it in,
not one fewer guarantee;
- `HIGH_RISK` — the id dropped from the list;
- the ratchet's own negative test `(a) a row that DROPS its covers` — it used
that row as its FIXTURE, so it would have passed vacuously or not at all.
Repointed at `anonymous-deny-meta`, which covers a live surface, so the test
still has a real classification to lose. This one mattered: silently letting
it fall through would have disarmed the ratchet that just caught this.
`showcase-anonymous-deny-surfaces.dogfood.test.ts` keeps every assertion — the
surface-level 401 is enforced by @objectstack/rest, the route's sole owner now.
Only its comments, which described the deleted raw-hono routes, are updated.
Verified: authz-conformance 8/8, full dogfood suite 430 passed / 3 skipped.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016gEeLZ4oTeSXG6fKG1r3vd
…2567 matrix The drift check flagged nine docs; eight carry none (the quickstart and authentication ones were settled in #4280, `http-protocol.mdx` entered only via the CORS change this branch merged from main, and `delegated-administration.mdx` came along as a same-package sibling with nothing to say about this). `authorization.mdx` was real, and it is the kind that matters most: it documents a SECURITY invariant, and it named the deleted surface twice — once in the list of HTTP surfaces the anonymous-deny decision is uniform across, and once in the implementation list, as `plugin-hono-server` `denyAnonymous`. Left alone, the permissions doc would promise a gate on an entry point that no longer exists. Both now read as history, matching how the same sentence already handles the retired GraphQL endpoint: the surface LEFT the matrix (v17, #4073) because the duplicate was deleted, not because it went ungated. The invariant is unchanged — `shouldDenyAnonymous` still backs REST `/data` and the dispatcher's metadata/AI paths, and the source-enumerating ratchet still fails CI on a new ungated surface. It just has one fewer door to hold shut. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016gEeLZ4oTeSXG6fKG1r3vd
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#4073 的终点。前置拆分(#4144)、证据(#4260)、默认值翻转(#4280)都已合并;按决定把删除也放进 17.0.0,不跨 major 拆成两次迁移。
净 −678 行。
HonoServerPlugin现在只拥有 socket、中间件和三条 current-user 端点 —— 数据面与 discovery 各自单一 owner(ADR-0076 D11)。删除
/api/v1/data/:object(含/:id)—— 直连 ObjectQL 的那份/api/v1/discovery+/.well-known/objectstackpayload —— 它早于DiscoverySchema,且缺services(D12 的 single source of truth),无法合规registerStandardEndpoints本身 —— 是删掉,不是默认关掉。传它现在是类型错误;用as never硬传也挂不出任何东西(有测试钉住)DISCOVERY_ROUTE_SEGMENTS与六个只为它存在的 import不受影响
/auth/me/*+/me/apps(唯一供给,#4144 起无条件注册);以及所有 composed 宿主 ——os serve、objectstack dev、cloud 的 objectos —— 它们挂的 owner 本来就在应答这些路由,且 #4260 已证两种 flag 位置下响应逐字节相同。无 owner 的宿主保留 boot warn,措辞改为点名两个 owner(因为"opt in"已不再是出路之一)。
测试:逐个按「它到底在钉什么」决定,而不是一刀切
hono-discovery、hono-anonymous-denyserver-timing-e2e/auth/me/permissionsclient.honoclient.connect()与 discovery 握手的测试,删掉会丢真实信号hono-standard-endpoints-defaulthono-transport-only/me/*相对 CRUD 块的两条顺序用例auth-catchall-fallthrough.test.ts直接钉住server-timing-e2e的匿名用例从断言 401 改为 200 且无 Server-Timing —— 该端点对匿名调用者是"如实回答"而非"拒绝",要钉的不变量是闸门没开,不是状态码。AGENTS.md:把这条线付过学费的四条规则写下来
一条路由一个 owner · 显式组合优于默认魔法 · 缺席要响亮 · 机器可读面不许撒谎。
外加一条验证方法:「谁在服务这条路径」是关于 composed 且 provisioned 运行时的问题 —— 不是关于谁声明了它、不是关于注册顺序、也不是关于一个能启动的最小 harness。#4073 被答错三次,每次正好对应绕过其中一条。
验证
迁移(changeset 内有完整 FROM → TO)
只有「挂
HonoServerPlugin但两个 owner 都不挂」的宿主受影响:挂createRestApiPlugin(完整 CRUD + 全闸门)或createDispatcherPlugin。没有 flag 可以退回。关联:#4073(终点)、#4280、#4260、#4144、#4088、#4018、#3298、#2567。
🤖 Generated with Claude Code
https://claude.ai/code/session_016gEeLZ4oTeSXG6fKG1r3vd
Generated by Claude Code