Skip to content

docs(spec): server.trustProxy describe says "two consumers" but the per-endpoint rate limiter is a third #25058

Open

Description

@objectstack-fleet

Filing gate ① a measured defect (a documentation inaccuracy), found by the retroactive contract review of PR #24859 (record 6308841043, merge 55d7aaba4c, closes #24818). Filed by the domain:spec seat 3 (session session_01D6F9axfoPzR7t8G1J7Ytzz). ⛔ Not a claim.

The finding

The server.trustProxy .describe() text in packages/spec/src/system/stack-server.zod.ts (and so the generated row in content/docs/references/system/stack-server.mdx) now reads "One client-address rule, two consumers: the inbound rate limiter when server.security.rateLimit.enabled is set, and the authentication plugin …".

On origin/main there is a third caller of the same rule:

  • packages/runtime/src/endpoint-policy.ts:366-372 calls resolveRateLimitKey({… trustProxy: input.trustProxy === true }) for any endpoint that declares rateLimit (packages/spec/src/api/endpoint.zod.ts:189).
  • packages/runtime/src/dispatcher-plugin.ts:2278-2282 passes config.rateLimit?.trustProxy into that chain. The registry is built unconditionally (dispatcher-plugin.ts:2115), so it does not depend on server.security.rateLimit.enabled.

The same file's own header (stack-server.zod.ts:56) already names "the endpoint policy chain". No key, default or behaviour is misstated. Only the count is wrong, and the "when … enabled is set" qualifier predates #24859.

Candidate shape (for triage)

Name the endpoint-policy chain as a consumer, or drop the count, and regenerate the reference with gen:schema && gen:docs. @objectstack/spec patch, Clause-②: no.

Dedupe

Method: every objectstack-ai/objectstack issue updated since 2026-09-01 (any state), from the repo-scoped issues?state=all endpoint, matched on trustProxy in the title or body.

Activity

  1. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 35
    Session: session_01D6F9axfoPzR7t8G1J7Ytzz
    Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-25058-trustproxy-consumers-count
    Worktree: objectstack-issue-25058
    Domain: domain:spec
    Seat: domain:spec#3 (seat post #18883)
    File surface (at origin/main 4f97aeb5c4 or later; stop on a breach and explain it in the report):

    • packages/spec/src/system/stack-server.zod.ts: the server.trustProxy .describe() / TSDoc text only;
    • the regenerated content/docs/references/system/stack-server.mdx;
    • .changeset/25058-*.md.
      Container & model: S, mode:subagent, model: default (no path-derived mandate)
      Clause-②: no (prose only: no shape, key, default or accepted value moves)
      Responsibility: the follow-up the retroactive contract review of PR #24859 (6308841043) filed
      Thread-read: none
      Serial constraints cleared: no in-flight PR of this seat touches stack-server.zod.ts. The dev re-checks open PRs before opening its own.

    Read on origin/main 4f97aeb5c4 at 2026-10-21T10:10Z: the trustProxy describe says "two consumers", and the per-endpoint declarative rate limiter (runtime/src/endpoint-policy.ts) is a third.

  2. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    Paused — claim released (handover)

    The seat stopped this dispatch at the maintainer's request (wind-down). The claim 6313443713 ends here, and the card returns to pm:queue.

    • Work on the branch: claude/issue-25058-trustproxy-consumers-count, 1 commit, pushed at 177ffab832 ("server.trustProxy names the endpoint policy chain as a third …"), clean. No PR is open.
    • Not done: the dev had not yet built spec or regenerated stack-server.mdx. The next dev picks up from the branch, regenerates, verifies and opens the draft PR. The PR owes a contract review, because it changes text in packages/spec/src/**.
  3. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    Resumed — claim restored

    The handover note above is withdrawn. The maintainer has asked the seat to carry every half-finished card through to merge, so this seat's claim on this card stands again, and pm:dispatched is restored. The seat now runs one dev at a time. This card resumes from its pushed branch head, in the seat's serial order.

  4. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 25058,
      "status": "done",
      "branch": "claude/issue-25058-trustproxy-consumers-count",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/25116",
      "session": "session_01D6F9axfoPzR7t8G1J7Ytzz",
      "premise_still_valid": true,
      "summary": "Resumed from 177ffab832. The server.trustProxy describe and TSDoc now name three consumers of the client-address rule. I checked each on origin/main ba244f1b48. (1) The inbound rate limiter: runtime/src/security/inbound-rate-limit.ts:156-159, called via resolveRateLimitKey :306-310 and installed at dispatcher-plugin.ts:906-908; it only exists when a server-level budget is enabled. (2) The endpoint policy chain: endpoint-policy.ts:366-372, gated on the endpoint's own enabled rateLimit (deriveBucketConfig :162-174), threaded at dispatcher-plugin.ts:2278-2282 from a registry built unconditionally at :2115. (3) AuthPlugin: auth-manager.ts:10059-10066 and auth-plugin.ts:2777-2784, given the value at cli serve.ts:4263. A whole-tree non-test git grep for resolveClientAddress, resolveRateLimitKey and trustProxy finds no fourth caller. Both qualifiers are correct. I merged origin/main (9424cb264b), regenerated content/docs/references/system/stack-server.mdx with gen:docs (9aa6fdc4c8), and opened draft PR #25116 through the relay, assigned to baozhoutao. Its body starts with 'Closes #25058' and carries the claim's Clause-② line. The changeset (@objectstack/spec patch, the same Clause-② line) was already in 177ffab832. No open PR touches either stack-server file: I paged the file lists of all 9 open PRs. Deviation: the harness's commit trailer named a model, the pre-push check-commit-card-trailers refused it, and I reworded the unpublished commit to the model-free pair before pushing, so no history was rewritten. I opened the PR with the dispatch's 'Closes' keyword; os-dev.md's text says 'Fixes', and both close the card.",
      "tests": "At head 9aa6fdc4c8, all through os-verify-lock where heavy: `pnpm --filter @objectstack/spec build` gave VERDICT command-exit 0. `pnpm --filter @objectstack/spec test` passed 706 files (21459 tests, 1 todo), command-exit 0. `pnpm --filter @objectstack/spec test:repo` passed 72 files (1237 tests), command-exit 0. `pnpm --filter @objectstack/spec typecheck` gave command-exit 0, including check:test-typecheck OK. `check:generated` first reported '1 of 14 artifact(s) stale: content/docs/references/**'; after gen:docs it exited 0 with 'All 14 generated artifacts are up to date'. `dispatch-gates --commands` derived 105 families and I ran all 105, capturing each exit before any pipe. Six first exited 3 (PREREQUISITE NOT MET). After a locked `turbo build --concurrency=2` of the lint, formula, client-react and objectql closures, five of them exited 0 on re-run: check:doc-formula-expressions, check:doc-security-posture, check:skill-examples, check:docs-transcript-drift and check:lean-entry-closure. That leaves one NOT MEASURED: check:dual-build-cjs-loads, reason: it needs every workspace package's dist, a full build was not run locally, and CI owns it. `dispatch-gates --ran` with exit codes: '105 derived famil(ies) accounted for — 104 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)'. The changeset gates check-empty-changeset, check-changeset-no-major, check-adr-0087-registration and check:changeset-gate-self-tests, and check:nul-bytes, all exited 0. No ablation (prose-only change). No lint run (CI-owned).",
      "mcp_calls": "0",
      "api_writes": "2 — (1) relay pr_create via POST /repos/objectstack-ai/objectstack/dispatches (run 39603600654: POST /pulls plus POST /issues/25116/assignees, read back byte-identical); (2) this os-dev-report comment via POST /issues/25058/comments. Plus git push of the branch (not a REST write). Zero label writes: the dispatch named no label, and skip-changeset does not apply because the spec describe ships.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none · noted in PR #25116 Acceptance notes, not filed · docs/adr/0069-enterprise-authentication-hardening.md D5 amendment (~line 145) still implies two consumers ('imported back by the runtime's inbound rate limiter, so both apply it identically'). It is not edited because docs/adr/** is governed (Tier H). Dedupe words: trustProxy, ADR-0069 D5, client-address consumers, endpoint policy chain"
      ]
    }

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions