Repository navigation
Commit 8ed3786
refactor(plugin-hono-server): the /auth/me door calls the one request-to-ExecutionContext composition, resolveRequestExecutionContext (#24437)
Fixes #23217
Clause-②: no
Ruling-ref: 6257816853
Basis for the `Clause-②` line: ruling record 6227928430's "later slices
`Clause-②: no`", and ruling record 6257816853 item 1. One answer of the
`/auth/me` door changes: a session read that carries `session.userId`
and no `user.id` was 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-server` moves.
Slice 5 of #23217, the last door, under ruling record 6257816853 (batch
#367 item 2, slice 5 letter A): the `/auth/me` door,
`makeExecutionContextResolver` in
`packages/plugins/plugin-hono-server/src/current-user-endpoints.ts`,
calls the one `resolveRequestExecutionContext` (`@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
`makeExecutionContextResolver` keeps 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:
| parameter | the `/auth/me` door passes | what the hand-written door
did |
|:--|:--|:--|
| `anonymous` | `'fail-closed'` | a request with no `userId` got no
context, and each route answered its own signed-out body |
| `authGate` | `'withhold'` | never carried; these routes are
allow-listed for a gated user |
| `credentials` | `'session-only'` | `headers: undefined` to the
resolver, so no key was extracted and no admission ran |
| `sessionApi` | `'required'` | no auth service, no `api` or `getApi()`,
or no `getSession`: no context |
| `session` | `'request'` | its own read under the in-process rule
(`inProcessSessionReadInput`) |
| `tenancy` | `undefined` | no posture, so the session's claimed
organization stood |
| `localization`, `requestLocale` | `'withhold'`, `undefined` | none;
the endpoints read the cascade themselves |
| `accessToken` | `'withhold'` | never carried |
| `oauth`, `onAdmission` | `undefined` | none |
These door facts stay at the door:
- the per-request `ctx.getService('auth')` read and the guarded
`getObjectQL()`;
- the kernel-resolver seam (`resolveRequestContext`, which picks the
locator the resolver is built over);
- the fail-closed `catch` with `rethrowAuthzStoreUnavailable`, which
also answers the function's propagated `getApi()` rejection as no
context, as the door always did;
- the perf-disclosure gate. It now reads the assembled context.
`isPerfDisclosurePrincipal` reads `isSystem`, `principalKind` and
`posture`, which carry `false`, `'human'` and the same posture, so its
value is unchanged.
`resolveAuthzContext`, `assembleExecutionContext`,
`userAuthGateSourceOf`, `apiKeyLifecyclePolicySourceOf`,
`inProcessSessionReadInput` and `IAuthService` left the door's imports.
`resolveRequestExecutionContext` and the `RequestAuthService` type 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 `readSession` dropped a session read with no
`user.id` (`return session?.user?.id ? session : null;`). The
composition reads `user.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 carries `session.userId` with no user,
with a user that has no id, or with `user.id: null`. REST, the runtime
dispatcher, the settings routes and the storage download already gave
that answer. An empty-string `user.id` still 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's
`customSession` passes the read through. Cloud's control plane uses the
same plugin-auth read (ruling record 6257816853, read at cloud
`9099bd1ea646`).
### Readers moved in the same PR
- `packages/core/src/security/authz-store-unavailable.test.ts`:
- The hono row of `TRANSPORT_LEDGER` left, because the file no longer
holds `resolveAuthzContext({`. 7 rows in 6 packages remain.
- The composition's comment names the fifth door.
- The scanner CONTROL's floor went from 8 to 7, with the reason in its
comment. The floor is the ledger's size. Set equality already fails a
walk that loses any listed file. The floor and the known-member
assertion refuse the one case set equality cannot see: a broken walk and
a gutted ledger together. Every door now calls the composition, so no
later conversion moves it.
- `scripts/check-single-authz-resolver.mjs`: `DELEGATORS` gained the
hono file (`resolveRequestExecutionContext`). The gate reads `all 6
entry points delegate`.
-
`packages/runtime/src/security/request-execution-context.conformance.test.ts`:
- A `HONO_ROW`. Its drive registers the current-user endpoints on a
`HonoHttpServer` over the world's services plus a metadata service and a
security service. It sends `GET /api/v1/auth/me/permissions` and reads
the context the route hands to
`security.resolvePermissionSetsForContext`.
- `expectedPrincipal` learned `credentials`: a request carrying no
session resolves nobody at a session-only door, so an API key alone
admits nobody there.
- One request joined the six: a session read naming its user by
`session.userId` alone. It holds all five doors to one reading of who a
session names, and all five resolve `u_ses`. It sits before the last
request, which the CONTROLs read by position.
- The hono CONTROL tells apart `anonymous`, `authGate`, `accessToken`,
`localization`, `requestLocale` and `credentials`. For `tenancy` it 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 that
`sessionApi` is not observable at a session-only door: with no session
read the door reads no credential at all, so `'optional'` admits nobody
either.
- No alias: runtime's `KNOWN_UNALIASED_TEST_IMPORTS` entry already lists
`@objectstack/plugin-hono-server`. The row reads the door from `dist/`,
and `check:test-source-alias` stays green.
- Core's module docblock table (`request-execution-context.ts`), read
row by row against the converted door: no row turns false. Every row
already names the `/auth/me` endpoints for the value this door passes.
Not edited.
- `scripts/slot-lookup-baseline.json` does not name the hono file. Not
touched.
- The 14 hono test files that import `./current-user-endpoints`: none
reads its source text. The 11 that fake a session read answer with
`user.id` or 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 real `HonoServerPlugin`
through `init()`, `start()` and its `kernel:ready` hooks. It drives the
three real routes and reads each body and what the security service
received. 9 rows:
- five ADR-0096 rows: no credential, an unknown cookie, a throwing
session read, an API key alone, and no auth service. Each gets every
route's own signed-out body, and the security service is never asked;
- one ADR-0069 row: a held session is served on these allow-listed
routes, and its context carries no `authGate`;
- one outage row: all three routes answer the host's `503
SERVICE_UNAVAILABLE`;
- the ruled reading: a `session.userId`-only read is served as that
user;
- a CONTROL: an ungated session is served, and its context carries no
bearer, locale or time zone, although the session carries a bearer, the
request a language and the host a settings service.
-
`packages/plugins/plugin-hono-server/src/current-user-endpoints.resolver-wirings.pin.test.ts`
is 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 own `catch`, and the file's CONTROL shows the composition
rejecting with `getApi down` there. The three `session.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.
- Before, on the hand-written door (door blob `c0e104d2c`, `main`
`8baeda520`, pins at `348b11ba7`): the two pin files read `Tests 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 }`.
- After, on the converted door (blob `e7cc167d5`): the whole hono suite
reads `Test Files 34 passed (34)`, `Tests 422 passed (422)`, both pins
included.
- ABLATION, declared direction RED, at `86cd71673` (door blob
`e7cc167d5`): two legs in one verify-lock call, through
`scripts/ablation-replace.mjs`. Each leg rebuilt
`@objectstack/plugin-hono-server` and proved its marker in `dist/` with
`scripts/ablation-dist-preflight.mjs`, because the conformance row reads
the door from `dist/`. Each restore was rebuilt and preflighted with
`--absent`.
- Leg 1 reverts the door to its hand-written composition: the import
block through `makeExecutionContextResolver` replaced by `main`'s text.
Marker `session?.user?.id ? session : null`, present in 2 built files.
Wirings pin `Tests 3 failed | 17 passed (20)`, route pin `Tests 1 failed
| 8 passed (9)`, conformance `Tests 1 failed | 12 passed (13)`. Each
failure is a ruled-reading row or the `session.userId`-only request.
- Leg 2 changes `credentials: 'session-only'` to
`'api-key-and-session'`. Marker `credentials: "api-key-and-session"` in
`dist/`. Wirings pin `2 failed | 18 passed (20)`: the API key alone, and
the key beside a session. Route pin `1 failed | 8 passed (9)`: the API
key alone. Conformance `3 failed | 10 passed (13)`: the API key, the
ex-member's key, and the hono CONTROL.
- Each restore: the blob equals HEAD (`e7cc167d5`), `git diff HEAD` is
empty, and the dist marker is absent after the rebuild. `git status
--porcelain` is empty at the end. Observed direction equals declared.
## Tests (HEAD `2f7f09a8e`, after merging `origin/main` `e22defb13`)
- `@objectstack/plugin-hono-server`: `Test Files 34 passed (34)`, `Tests
422 passed (422)`. `typecheck` exit 0 (test layer 0 / 0 / 0), and `tsc
-p tsconfig.test.json --listFiles` includes both new pins.
- `@objectstack/runtime`: the conformance test reads `Test Files 1
passed (1)`, `Tests 13 passed (13)` (7 requests, the identity test, 5
CONTROLs). `typecheck` exit 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/` reads `Test Files 43 passed
(43)`, `Tests 961 passed (961)`; the ledger file reads 36, which is 37
minus the hono row. `typecheck` exit 0 (test layer 4 / 4 / 4 held).
Core's source is untouched.
- Build after the merge: `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-debt` ran under the verify lock: exit 0, `none above
its recorded number`.
- `--ran` reads `90 derived, 90 run, 0 NOT-MEASURED, 0 UNRUN`, exit 0.
- Among the derived commands:
- `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-changeset` and
`check:pm-dispatch-gates`.
- The declared wide population, all 10 families, all exit 0:
`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-in` and `check:wildcard-fallthrough`.
- Edited gate script: `node scripts/check-single-authz-resolver.mjs`
exit 0, and `--self-test` exit 0.
- `pnpm exec eslint --no-inline-config` over the five changed `.ts` /
`.mjs` files: exit 0, no output.
## The card's acceptance, measured (HEAD `2f7f09a8e`)
`git grep -c -F` over the five door files:
| line | rest | runtime | settings | storage | hono |
|:--|:-:|:-:|:-:|:-:|:-:|
| `resolveAuthzContext(` | 0 | 0 | 0 | 0 | 0 |
| `assembleExecutionContext(` / `assembleExecutionContextOrGuest(` | 0 |
0 | 0 | 0 | 0 |
| `classifyAdmissionTenancyPosture(` | 0 | 0 | 1 | 1 | 0 |
| `inProcessSessionReadInput(` | 0 | 0 | 0 | 1 | 0 |
| `resolveApiKeyAdmission(` | 0 | 0 | 0 | 1 | 0 |
| `resolveRequestExecutionContext(` | 1 | 1 | 1 | 1 | 1 |
- The hono door reaches 0 copies and adopts the composition's reading.
- Storage's three hits are its ADR-0069 gate reader, the acceptance
line's one exception (copy + parity pin): the key arm
(`resolveApiKeyAdmission(`, about `:1283`), the gate reader's posture
read (`resolveAdmissionTenancyPosture`, about `:1432`), and
`buildGetSession` (about `:973`), the session read the gate reader
shares with the upload session resolver.
- The settings hit (`resolveAdmissionTenancyPosture`, about `:469`)
feeds the settings SERVICE's own `tenancyPosture` option, not the HTTP
door. The door calls the composition (slice 3), and that method's
docblock records why the resolution stayed there.
- The ADR-0096 refusal and the ADR-0069 allow-list read the same on this
door before and after: 8 of the route pin's 9 rows were green on both
trees. The ninth is the ruled reading.
## Acceptance notes
- This PR closes the card on the reading above. If the seat reads the
settings service-option hit or storage's `buildGetSession` as outside
the acceptance line's one exception, the first line should read `Part of
#23217` instead. The seat writes that change from the report, since this
body is written once.
- Out of lane:
`packages/core/src/security/authz-store-unavailable.test.ts` (declared
on #6367, comment `6254881518`) and
`scripts/check-single-authz-resolver.mjs` (declared on #6017, comment
`6254886923`). Every other path is in `domain:cli` or is the changeset.
- Nothing filed: no defect, contract violation or authoring trap turned
up outside this slice.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01SYEJqDL3NXxrJRBVqaMu9T)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent d9d41dd commit 8ed3786
7 files changed
Lines changed: 851 additions & 153 deletions
File tree
- .changeset
- packages
- core/src/security
- plugins/plugin-hono-server/src
- runtime/src/security
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
183 | 183 | | |
184 | 184 | | |
185 | 185 | | |
186 | | - | |
187 | | - | |
188 | | - | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
189 | 190 | | |
190 | 191 | | |
191 | 192 | | |
192 | 193 | | |
193 | | - | |
194 | 194 | | |
195 | 195 | | |
196 | 196 | | |
| |||
297 | 297 | | |
298 | 298 | | |
299 | 299 | | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
300 | 307 | | |
301 | | - | |
| 308 | + | |
302 | 309 | | |
303 | 310 | | |
304 | 311 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
0 commit comments