Repository navigation
refactor(plugin-hono-server): the /auth/me door calls the one request-to-ExecutionContext composition, resolveRequestExecutionContext - #24437
Conversation
…e one composition, before the door moves The door itself is unchanged in this commit; the next one converts it. Claude-Session: https://claude.ai/code/session_01SYEJqDL3NXxrJRBVqaMu9T Co-authored-by: Claude <noreply@anthropic.com>
…-to-ExecutionContext composition, resolveRequestExecutionContext Claude-Session: https://claude.ai/code/session_01SYEJqDL3NXxrJRBVqaMu9T Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check2 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 — 9 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 7786652b9443656cf3f4073c7f76c09d06fb04fe && git checkout 7786652b9443656cf3f4073c7f76c09d06fb04fe
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin db1f1fd20fba543408b209746e566b48373a4622 2f7f09a8efc21a980cf375cfa36519d7e852440c && git checkout -B drift-repro db1f1fd20fba543408b209746e566b48373a4622 && git merge --no-ff 2f7f09a8efc21a980cf375cfa36519d7e852440c
node scripts/docs-audit/affected-docs.mjs --json db1f1fd20fba543408b209746e566b48373a4622 |
席位核对ACCEPT (lane review) ·
Generated by Claude Code |
Fixes #23217
Clause-②: no
Ruling-ref: 6257816853
Basis for the
Clause-②line: ruling record 6227928430's "later slicesClause-②: no", and ruling record 6257816853 item 1. One answer of the/auth/medoor changes: a session read that carriessession.userIdand nouser.idwas answered as signed out and is now signed in as that user, the answer the four converted doors already give. The ruling records that no producer yields that read, in this repository or in cloud's control plane. No export or published surface of@objectstack/plugin-hono-servermoves.Slice 5 of #23217, the last door, under ruling record 6257816853 (batch #367 item 2, slice 5 letter A): the
/auth/medoor,makeExecutionContextResolverinpackages/plugins/plugin-hono-server/src/current-user-endpoints.ts, calls the oneresolveRequestExecutionContext(@objectstack/core) with its existing parameters and adopts the composition's reading of who a session names.@objectstack/core's function is not touched. There is no new named parameter (B), no door-local copy (C) and no strict reading for everyone (D). With this slice all five doors call the one composition. Storage's ADR-0069 gate reader stays a hand-written door fact under its parity pin, as ruling record 6257816853 item 2 (letter (i)) closes it.What changed
The door
makeExecutionContextResolverkeeps its door facts and calls the function once. Its parameters are slice 1's row for this door, which report 6255071471 re-measured on the hand-written door over 18 wirings:/auth/medoor passesanonymous'fail-closed'userIdgot no context, and each route answered its own signed-out bodyauthGate'withhold'credentials'session-only'headers: undefinedto the resolver, so no key was extracted and no admission ransessionApi'required'apiorgetApi(), or nogetSession: no contextsession'request'inProcessSessionReadInput)tenancyundefinedlocalization,requestLocale'withhold',undefinedaccessToken'withhold'oauth,onAdmissionundefinedThese door facts stay at the door:
ctx.getService('auth')read and the guardedgetObjectQL();resolveRequestContext, which picks the locator the resolver is built over);catchwithrethrowAuthzStoreUnavailable, which also answers the function's propagatedgetApi()rejection as no context, as the door always did;isPerfDisclosurePrincipalreadsisSystem,principalKindandposture, which carryfalse,'human'and the same posture, so its value is unchanged.resolveAuthzContext,assembleExecutionContext,userAuthGateSourceOf,apiKeyLifecyclePolicySourceOf,inProcessSessionReadInputandIAuthServiceleft the door's imports.resolveRequestExecutionContextand theRequestAuthServicetype came in. The door's comments now give each parameter's reason where the hand-written composition gave each omission's.The one changed answer (ruling record 6257816853 item 1)
The hand-written door's
readSessiondropped a session read with nouser.id(return session?.user?.id ? session : null;). The composition readsuser.id ?? session.userId(packages/core/src/security/resolve-authz-context.ts). The three routes now answer signed in as that user, instead of signed out, when the auth service's session read carriessession.userIdwith no user, with a user that has no id, or withuser.id: null. REST, the runtime dispatcher, the settings routes and the storage download already gave that answer. An empty-stringuser.idstill names nobody, at every door. No producer yields the shape. better-auth's session read returns no session when the session row joins no user. plugin-auth'scustomSessionpasses the read through. Cloud's control plane uses the same plugin-auth read (ruling record 6257816853, read at cloud9099bd1ea646).Readers moved in the same PR
packages/core/src/security/authz-store-unavailable.test.ts:TRANSPORT_LEDGERleft, because the file no longer holdsresolveAuthzContext({. 7 rows in 6 packages remain.scripts/check-single-authz-resolver.mjs:DELEGATORSgained the hono file (resolveRequestExecutionContext). The gate readsall 6 entry points delegate.packages/runtime/src/security/request-execution-context.conformance.test.ts:HONO_ROW. Its drive registers the current-user endpoints on aHonoHttpServerover the world's services plus a metadata service and a security service. It sendsGET /api/v1/auth/me/permissionsand reads the context the route hands tosecurity.resolvePermissionSetsForContext.expectedPrincipallearnedcredentials: a request carrying no session resolves nobody at a session-only door, so an API key alone admits nobody there.session.userIdalone. It holds all five doors to one reading of who a session names, and all five resolveu_ses. It sits before the last request, which the CONTROLs read by position.anonymous,authGate,accessToken,localization,requestLocaleandcredentials. Fortenancyit adds a session claiming an organization its user is not a member of, under the walled posture: the claim stands at this door, and the walled posture drops the claim and keeps the caller. It also states thatsessionApiis not observable at a session-only door: with no session read the door reads no credential at all, so'optional'admits nobody either.KNOWN_UNALIASED_TEST_IMPORTSentry already lists@objectstack/plugin-hono-server. The row reads the door fromdist/, andcheck:test-source-aliasstays green.request-execution-context.ts), read row by row against the converted door: no row turns false. Every row already names the/auth/meendpoints for the value this door passes. Not edited.scripts/slot-lookup-baseline.jsondoes not name the hono file. Not touched../current-user-endpoints: none reads its source text. The 11 that fake a session read answer withuser.idor with nothing, so none moved.The pins
packages/plugins/plugin-hono-server/src/current-user-endpoints.request-context-door.pin.test.ts(ruling record 6227928430 item 2) boots the realHonoServerPluginthroughinit(),start()and itskernel:readyhooks. It drives the three real routes and reads each body and what the security service received. 9 rows:authGate;503 SERVICE_UNAVAILABLE;session.userId-only read is served as that user;packages/plugins/plugin-hono-server/src/current-user-endpoints.resolver-wirings.pin.test.tsis the earlier dev's 18-wiring parity probe, committed. Each wiring pins who the door names: no context, the caller with its organization, or the outage. Each also requires the composition, under the parameters this door declares, to give the same answer. The one declared difference is the door's owncatch, and the file's CONTROL shows the composition rejecting withgetApi downthere. The threesession.userId-only wirings assert the composition's answer, signed in as that user. The other 15 assert their unchanged answers.Evidence
All on a shared container; lock seconds are shared-box readings.
c0e104d2c,main8baeda520, pins at348b11ba7): the two pin files readTests 4 failed | 25 passed (29). The four failures are exactly the ruled-reading rows (three wirings and the route-level row), where the door answered no context and{ authenticated: false }.e7cc167d5): the whole hono suite readsTest Files 34 passed (34),Tests 422 passed (422), both pins included.86cd71673(door blobe7cc167d5): two legs in one verify-lock call, throughscripts/ablation-replace.mjs. Each leg rebuilt@objectstack/plugin-hono-serverand proved its marker indist/withscripts/ablation-dist-preflight.mjs, because the conformance row reads the door fromdist/. Each restore was rebuilt and preflighted with--absent.makeExecutionContextResolverreplaced bymain's text. Markersession?.user?.id ? session : null, present in 2 built files. Wirings pinTests 3 failed | 17 passed (20), route pinTests 1 failed | 8 passed (9), conformanceTests 1 failed | 12 passed (13). Each failure is a ruled-reading row or thesession.userId-only request.credentials: 'session-only'to'api-key-and-session'. Markercredentials: "api-key-and-session"indist/. Wirings pin2 failed | 18 passed (20): the API key alone, and the key beside a session. Route pin1 failed | 8 passed (9): the API key alone. Conformance3 failed | 10 passed (13): the API key, the ex-member's key, and the hono CONTROL.e7cc167d5),git diff HEADis empty, and the dist marker is absent after the rebuild.git status --porcelainis empty at the end. Observed direction equals declared.Tests (HEAD
2f7f09a8e, after mergingorigin/maine22defb13)@objectstack/plugin-hono-server:Test Files 34 passed (34),Tests 422 passed (422).typecheckexit 0 (test layer 0 / 0 / 0), andtsc -p tsconfig.test.json --listFilesincludes both new pins.@objectstack/runtime: the conformance test readsTest Files 1 passed (1),Tests 13 passed (13)(7 requests, the identity test, 5 CONTROLs).typecheckexit 0 (test layer 27 files / 190 errors / 68 signatures held, none in the conformance file). The rest of the runtime suite was not run locally: the conformance file is the only runtime file touched, and CI's Test Core runs the suite.@objectstack/core:src/security/readsTest Files 43 passed (43),Tests 961 passed (961); the ledger file reads 36, which is 37 minus the hono row.typecheckexit 0 (test layer 4 / 4 / 4 held). Core's source is untouched.turbo run build --filter=!@objectstack/docs --concurrency=2,76 successful, 76 total.Gates (HEAD
2f7f09a8e)node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths) derived 90 commands. Each ran with its exit code captured before any pipe, and all exited 0.pnpm check:type-check-debtran under the verify lock: exit 0,none above its recorded number.--ranreads90 derived, 90 run, 0 NOT-MEASURED, 0 UNRUN, exit 0.check:authz-resolver:all 6 entry points delegate;check:test-source-alias;check:slot-lookup:100 unswept site(s) in 25 file(s), none new;check:cross-package-test-inputs,check:route-envelope,check:dual-build-cjs-loads(on the built tree),check:nul-bytes,check:adr-0087-registration,check:empty-changesetandcheck:pm-dispatch-gates.check:init-service-contract,check:live-db-isolation,check:meta-type-normalized,check:optional-error-sink,check:resume-authority-declared,check:runner-env-posture,check:settings-bind-window,check:startup-registry-verdict,check:verify-stand-inandcheck:wildcard-fallthrough.node scripts/check-single-authz-resolver.mjsexit 0, and--self-testexit 0.pnpm exec eslint --no-inline-configover the five changed.ts/.mjsfiles: exit 0, no output.The card's acceptance, measured (HEAD
2f7f09a8e)git grep -c -Fover the five door files:resolveAuthzContext(assembleExecutionContext(/assembleExecutionContextOrGuest(classifyAdmissionTenancyPosture(inProcessSessionReadInput(resolveApiKeyAdmission(resolveRequestExecutionContext(resolveApiKeyAdmission(, about:1283), the gate reader's posture read (resolveAdmissionTenancyPosture, about:1432), andbuildGetSession(about:973), the session read the gate reader shares with the upload session resolver.resolveAdmissionTenancyPosture, about:469) feeds the settings SERVICE's owntenancyPostureoption, not the HTTP door. The door calls the composition (slice 3), and that method's docblock records why the resolution stayed there.Acceptance notes
buildGetSessionas outside the acceptance line's one exception, the first line should readPart of #23217instead. The seat writes that change from the report, since this body is written once.packages/core/src/security/authz-store-unavailable.test.ts(declared on [PM seat] domain:engine — 🟢 os-project-manager #6367, comment6254881518) andscripts/check-single-authz-resolver.mjs(declared on [PM seat] domain:spec — ⏳ vacant #6017, comment6254886923). Every other path is indomain:clior is the changeset.Generated by Claude Code