Repository navigation
docs(spec): server.trustProxy describe names all three client-address consumers - #25116
objectstack-fleet[bot] wants to merge 3 commits into
Conversation
…ird client-address consumer The describe and TSDoc said "two consumers". The per-endpoint declarative rate limiter keys anonymous callers by the same rule under the same trustProxy whenever an endpoint's own rateLimit is enabled, independent of server.security.rateLimit.enabled. Prose only. Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz Co-authored-by: Claude <noreply@anthropic.com>
…ustproxy-consumers-count
…describe Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz Co-Authored-By: Claude <noreply@anthropic.com>
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 144 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 629a2e411a40052c1dbaa9329c0547afb27b6f1b && git checkout 629a2e411a40052c1dbaa9329c0547afb27b6f1b
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin ae58b26f9c099ca44ac479d61df2727dea711d04 9aa6fdc4c8b6ca86101a1c8ec24d1f8f3da59c03 && git checkout -B drift-repro ae58b26f9c099ca44ac479d61df2727dea711d04 && git merge --no-ff 9aa6fdc4c8b6ca86101a1c8ec24d1f8f3da59c03
node scripts/docs-audit/affected-docs.mjs --json ae58b26f9c099ca44ac479d61df2727dea711d04 |
Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Seat re-read of the check-runs at landing: all 42 runs on |
Closes #25058
Clause-②: no (prose only: no shape, key, default or accepted value moves)
What
The
server.trustProxy.describe()and TSDoc inpackages/spec/src/system/stack-server.zod.tssaid the client-address rule had two consumers. There are three. The text now names the endpoint policy chain's per-endpoint rate limiter as well, and the generated row incontent/docs/references/system/stack-server.mdxis regenerated from it (gen:docs, ascheck:generatednamed it stale). Prose only:z.boolean().default(false), the strict object around it and every sibling key are unchanged. Changeset:@objectstack/specpatch.Every consumer, checked on
origin/main(ba244f1b48, merged into this branch)All three reach the one rule,
resolveClientAddress(packages/plugins/plugin-auth/src/client-address.ts), under the one authoredserver.trustProxythatobjectstack servereads atpackages/cli/src/commands/serve.ts:4733(dispatcher) and:4263(auth).packages/runtime/src/security/inbound-rate-limit.ts:156-159(viaresolveRateLimitKey,:306-310), installed atpackages/runtime/src/dispatcher-plugin.ts:906-908. It isnull, so nothing is registered, unless a budget is declared and enabled. That matches the qualifier "whenserver.security.rateLimit.enabledis set".packages/runtime/src/endpoint-policy.ts:366-372callsresolveRateLimitKeywithtrustProxy: input.trustProxy === truewheneverlimiters.limiterFor(endpoint)returns a limiter, and that happens only when the endpoint's ownrateLimitis enabled (deriveBucketConfig,:162-174). The value is threaded atdispatcher-plugin.ts:2278-2282. The registry is built unconditionally atdispatcher-plugin.ts:2115, so this limiter does not depend on the server-level budget. That matches the qualifier "whether or not the server-level one is".AuthPlugin:AuthManager.resolveClientAddress(packages/plugins/plugin-auth/src/auth-manager.ts:10059-10066), used by the D5 allow-list (auth-plugin.ts:2777-2784) and the D7last_login_ipstamp.No fourth.
git grepover the whole tree, excluding tests, forresolveClientAddress,resolveRateLimitKeyandtrustProxyfinds no other caller. The other uses (client-address-forwarding.ts,client-address-vendor.ts,request-client-address.ts) are internal to@objectstack/plugin-auth, which means they fall under consumer 3.Verification (head
9aa6fdc4c8)pnpm --filter @objectstack/spec build: exit 0.pnpm --filter @objectstack/spec check:generated: exit 0 with "All 14 generated artifacts are up to date". Beforegen:docsit saidcontent/docs/references/**was stale.pnpm --filter @objectstack/spec test: 706 files, 21459 passed and 1 todo.pnpm --filter @objectstack/spec test:repo: 72 files, 1237 passed.pnpm --filter @objectstack/spec typecheck: exit 0.node scripts/pm/dispatch-gates.mjs --ranreported "105 derived famil(ies) accounted for — 104 run, 1 NOT-MEASURED". The changeset gates (check-empty-changeset,check-changeset-no-major,check-adr-0087-registration,check:changeset-gate-self-tests) andcheck:nul-bytesall exited 0.check:dual-build-cjs-loads. Reason: it needs every workspace package'sdist/, and a full build was not run locally. CI builds them.Acceptance notes
docs/adr/0069-enterprise-authentication-hardening.md(D5 amendment, around line 145) still implies two consumers: "imported back by the runtime's inbound rate limiter, so both apply it identically". It is not edited here becausedocs/adr/**is a governed surface outside this card. carrier: none.Generated by Claude Code