Commit e43d63a
* fix(identity): mint API keys against the minter's active organization (#8287)
`sys_api_key` carried no organization, so under `OS_TENANCY_POSTURE=isolated`
a minted key authenticated a user with no active organization and the Layer 0
wall (`organization_id = activeOrganizationId`) could match nothing: every
org-scoped read answered `200` with `total 0` while the console went on
offering minting.
The column was absent by an inherited rule, not by oversight —
`resolveInjectedSystemColumns` skips `managedBy: 'better-auth'` objects, and
`sys_api_key` carries that flag even though better-auth's `apiKey` plugin is
not loaded and the table is hand-rolled ObjectStack.
- declare `active_organization_id` on `sys_api_key` (+ index, list columns)
- register it as an ADR-0105 D7 managed extension field, and correct that
registry's long-standing drift (its comment said every column here is an
extension field; the set listed one)
- mint (`POST /keys`) inherits the caller's active organization, re-checks
membership against `sys_member` at mint time, and refuses under a walled
posture rather than handing back a key that cannot read
- the verifier reads ONE spelling (PD #12), refuses an org-less key under
`isolated`, and the shared resolver fails an ex-member's key closed using
the membership set it had already read — zero extra queries
The column is deliberately NOT named `organization_id`: that name would make
`sys_api_key` itself org-walled, hiding pre-existing org-less rows from their
own owners while they keep authenticating under `group`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MX1qcBzfwZb5wkRrJTNbhH
* test(core): resolve @objectstack/types to source in typecheck and tests (#8287)
Adding the dependency made `check:test-source-alias` and
`check:type-source-resolution` both fire: a unit test or typecheck that reads a
sibling's built `dist/` is a verdict about a build, not about the code in this
checkout, and the dangerous direction is the one that PASSES. Anchored regex
aliases (array form) so the bare entry cannot swallow the `/node` subpath.
Also adds the changeset.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MX1qcBzfwZb5wkRrJTNbhH
* fix(identity): take the tenancy posture from the `tenancy` service, not the env (#8287)
Fixes the red `Build Core` at 2b993c9 and, underneath it, a correctness bug
the build failure exposed.
The build break: `check:type-source-resolution` requires a cross-package type
import to resolve to SOURCE, so adding `@objectstack/types` to `core` forced a
`paths` rule — which collides with core's `rootDir: "./src"` under the tsup DTS
build (TS6059). That gate's own header documents this exact cost.
The bug it exposed is the more important half. `resolveTenancyPosture()` reads
`OS_TENANCY_POSTURE`, which is what the operator ASKED for — not what is
ENFORCED. Under ADR-0093 D4/D5 a deployment requesting `isolated` without the
enterprise organizations runtime resolves to `single` and runs with no wall at
all, so the env-reading version would have refused org-less API keys on a
deployment that has no organization boundary to enforce.
The posture is now an explicit input, resolved from the kernel's `tenancy`
service — the same source plugin-security reconciles before handing a posture
to the Layer 0 wall, so admission and the wall can never disagree. `core` drops
the `@objectstack/types` dependency entirely, and both gates go quiet on their
own rather than by registry widening.
An ABSENT posture disables the two posture-conditional refusals, leaving
behaviour exactly as before: that is a question about the deployment, not about
the credential, so an unwired transport is never made worse — only less strict.
Wired here: the runtime dispatcher/MCP path and the REST data API, which are
the surfaces the card measured.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MX1qcBzfwZb5wkRrJTNbhH
* chore(core): drop the dead @objectstack/types source-resolution config (#8287)
The tsconfig `paths` and vitest `alias` entries were added to satisfy
check:type-source-resolution / check:test-source-alias when core briefly
depended on @objectstack/types. That dependency is gone — the tenancy posture
now arrives from the kernel's `tenancy` service — so both entries resolve
nothing, and their comments describe a `resolveTenancyPosture` call that no
longer exists.
Left in place they would mislead the next author and re-arm the TS6059
rootDir collision the moment anyone re-added the import. Both gates stay green
without them, because the predicate is the IMPORT, not the file.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MX1qcBzfwZb5wkRrJTNbhH
* test(runtime): pin the new #8287 key-mint engine double to ObjectQL's dispatch predicates (#8287)
`check:engine-double-contract` went red at 339a1b6: the organization-inheritance
suite added a second engine double to http-dispatcher.keys.test.ts, taking the file
from 1 unguarded double to 2 on both the delete and the update slice while the
shrink-only baseline records 1.
The baseline is NOT raised — that is an explicit gate-weakening action and the
gate's own message rules it out ("pin the new one rather than raising it"). The
new `makeOrgKernel` double now routes both write verbs through
assertEngineUpdateDispatch / assertEngineDeleteDispatch from
@objectstack/metadata-core, so it cannot be looser than ObjectQL itself. The
file's pre-existing makeKernel double keeps its measured DEBT entry untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MX1qcBzfwZb5wkRrJTNbhH
* test(runtime): type the new #8287 key-mint suite against HttpDispatcherResult (#8287)
`check:type-check-debt --re-measure` went red at 339a1b6: @objectstack/runtime's
TEST_DEBT records 227 raw tsc errors and the tree measured 235 (+8). All eight are
TS18048 'res.response' is possibly 'undefined' in the organization-inheritance
suite this PR added — `HttpDispatcherResult.response` is optional, and the runtime
test layer is hidden from tsc at the package level, so `pnpm test` going green said
nothing about them.
The ledger is NOT raised — it is a shrink-only ratchet and raising it is on the
maintainer's floor. A `responseOf()` helper narrows once and throws a named error
when a dispatcher answers no response at all, so the failure stays distinguishable
from a wrong status. Scoped to the new suite: the older suite's nine reads are the
file's share of the frozen number, and pressing that down is a separate improvement
to bank, not a rider on this repair. Re-measured: runtime back to exactly 227.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MX1qcBzfwZb5wkRrJTNbhH
* test(runtime): the new #8287 key-mint matcher refuses combinators instead of misreading them (#8287)
`check:where-matcher-conformance` went red once the engine-double gate stopped
aborting the ESLint job ahead of it: `makeOrgKernel`'s find matcher is an
`Object.entries(where).every(...)` body with no combinator branch, so it read
`$or`/`$and` as an ordinary FIELD NAME, compared `row.$or` (undefined) against
the array, matched nothing, and would have handed a suite an empty result set
with nothing erroring — shape (b) in that gate's header.
Fixed by refusal, not by implementing the combinator: the `makeKernel` matcher
160 lines above in this same file already refuses with this exact message, so
this keeps one convention in one file, and refusal is what 140 of the 233
discovered matchers already do. The baseline is NOT touched — third shrink-only
ratchet on this branch, same rule. Gate now reports 233/233 conforming, 141 by
refusing (+1, exactly this matcher).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MX1qcBzfwZb5wkRrJTNbhH
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7c2f386 commit e43d63a
18 files changed
Lines changed: 1037 additions & 29 deletions
File tree
- .changeset
- packages
- core/src/security
- platform-objects/src
- apps/translations
- identity
- plugins
- plugin-auth/src
- plugin-security/src
- rest/src
- runtime/src
- domains
- security
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
| 13 | + | |
12 | 14 | | |
13 | 15 | | |
14 | 16 | | |
| |||
69 | 71 | | |
70 | 72 | | |
71 | 73 | | |
72 | | - | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
73 | 79 | | |
74 | 80 | | |
75 | 81 | | |
| |||
103 | 109 | | |
104 | 110 | | |
105 | 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 | + | |
0 commit comments