You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[security] settings: tenant-scope settings are not isolated per organization on multi-organization deployments — detail withheld pending maintainer #22261
Filing gate: ① a product defect, reach measured through public doors on a multi-organization deployment in production posture. Reader: triage routes it. By its package it lands in the services lane (service-settings). Filed by the repo:cloud seat (repo:cloud#1, R45, session session_011jobP72PwN3whNm55GetXQ) as the 裁后执行 of a maintainer ruling. ⛔ Not a claim.
Detail withheld pending maintainer. Ruling, verbatim letter: D3 withheld, maintainer 「同意」 (2026-10-08T09:23Z, director batch #292 item 1), following the objectstack#21124 precedent. The full reading, the door table and the fix draft live on the originating private card. The deployment that surfaced it is closing its own exposure separately, at p0.
What is known publicly
SettingsService's row identity omits the organization that the sys_setting object declares in its own identity.
On a multi-organization deployment, an organization administrator's tenant-scope settings write is therefore visible to, and replaceable by, other organizations.
A second, read-side half: the generic data API's read of sys_setting and sys_setting_audit does not apply each namespace's declared readPermission.
Fix direction (option A, as ruled on the originating card)
Carry the organization in the row identity for tenant and user rows.
Filter reads explicitly by the caller's organization (plus global rows), and prefer the caller organization's own row in the cascade.
Refuse an organization-less tenant write under a walled posture.
Enforce each namespace's readPermission on the generic read door.
Triage: routed domain:services · area:access · pm:queue; priority:p1 and security stand, as ruled
Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-08T10:52Z. ⛔ Not a claim, ⛔ not a dispatch.
Triage: lands in packages/services/service-settings (the settings row identity and its reads) and the generic read door's readPermission enforcement ⇒ domain:services; rationale: packages/services/* is that lane's. If the generic-door half sits in another lane's package, the claiming seat names it and that seat reviews.
Filed as ruled: D3 withheld, the objectstack#21124 precedent. The direction is option A, as ruled on the originating card. Nothing here is the lane's to re-decide.
⛔ Disclosure: keep this card and every follow-up abstract (classes, positions, files and functions) until the fix lands. The full reading stays on the originating card.
The generic read door: each namespace's readPermission enforced on the data API's read of sys_setting and sys_setting_audit. The seat's lead is an engine hook or middleware registered by service-settings itself. If the measured seam sits in another lane's package, the report names it and that seat is declared before any edit.
Tests and changeset: tests in service-settings, plus the card's HTTP acceptance on a two-organization kernel with a positive control per refusal, and one @objectstack/service-settings changeset.
settings-service-plugin.ts: wiring only (the posture source and the read door's registration).
content/docs/permissions/system-context.mdx: row 18b plus the regenerated census counts. check:system-context-census requires one row per isSystem read site, and the read door adds one.
packages/qa/dogfood/test/settings-organization-isolation.dogfood.test.ts: the card's "measured over HTTP" acceptance.
Two existing fixtures follow the row shape: settings-getmany.test.ts and settings-routes.test.ts.
The read door also covers sys_platform_setting. That is an in-place adjacent fix: the same class, mechanical (one more entry in the same module), no other claimant, the same gate family.
⛔ No stored-row rewrite unless measured necessary and reported first.
Stop on breach; explain in the report.
Container & model: M, mode:subagent, model: opus (dispatch-gates --tier: no path-derived mandate; default tier, a p1 security card)
Clause-②: no (narrowing)
An organization-less tenant write under a walled posture is refused, and the generic read door refuses a principal lacking the namespace's readPermission. That is declared breaking. Nothing widens.
Responsibility: this repository's own code: SettingsService's row identity and reads omit the organization the sys_setting object declares, and the generic read door does not apply a namespace's readPermission | none: the walled driver scope does not reach the service's system-context reads | organization administrators on multi-organization deployments (measured in production posture; detail withheld)
Thread-read: 6058273893
Serial constraints cleared:
os-dev-report
{
"issue": 22261,
"status": "done",
"branch": "claude/issue-22261-settings-tenant-isolation",
"pr": "#22295",
"session": "session_01WkL6Eijt432S1Y7ekb6ovQ (dispatching seat's session; this executor ran as its subagent)",
"premise_still_valid": true,
"summary": "Option A executed as ruled, at class level. SettingsService now carries the caller's organization in the row identity of tenant and user rows (rowIdentity, setMany), filters its loads explicitly by the caller's organization plus organization-less rows (loadScopedRows and the in-memory store), and prefers the caller organization's own row in the cascade and the lock pre-flight (preferredRow). Under a walled posture an organization-less tenant write is refused whole with SettingsValidationError (SETTINGS_VALIDATION, HTTP 400, field code invalid_value); the posture comes from the tenancy service through bindEngine and is read only for callers naming no organization on keys below the global rung. The generic read door is an engine middleware registered by service-settings on sys_setting, sys_setting_audit and sys_platform_setting that ANDs each namespace's readPermission into every non-system read (SettingsService.namespaceReadScope); no file in objectql, runtime or plugin-security is edited. Posture single keeps the default organization's answers (pinned). Stored rows are not rewritten; the H3 reading and the decision it raises went to the seat privately.",
"tests": "HEAD 61911cf (service-settings rebuilt, dist proven to carry HEAD): service-settings vitest 41 files / 752 passed; settings-organization-isolation.pin 18 passed; settings-read-door.pin 27 passed; dogfood settings-organization-isolation (two organizations, isolated posture, over HTTP) 6 passed, each refusal with its positive control; single-posture HTTP regression (3 existing settings dogfood files) 14 passed; typecheck service-settings and dogfood exit 0; narrowed eslint over the 9 changed .ts files 0/0. Ablations (trap restore from HEAD, blob == HEAD and git diff HEAD empty after each): settings-service.ts at the base commit -> isolation pin 11 red / 5 green (the 5 are the global-row and single-posture guards); namespaceReadScope answering no predicate -> read-door pin 19 red / 8 green; positional preferredRow -> 2 red; dogfood with service and plugin at the base commit, rebuilt, ablation-dist-preflight --absent passed -> 4 of 6 red, restored and rebuilt -> 6 of 6 green; dogfood with only the door predicate disabled, plant proven in dist -> only the read-door case red, restored, rebuilt, --absent passed, tree clean. Gates: dispatch-gates --commands derived 94 at HEAD 61911cf, all run exit 0, plus check:settings-bind-window; --ran: 94 derived, 94 run, 0 NOT-MEASURED, 0 UNRUN.",
"mcp_calls": "0",
"api_writes": "3 REST writes, all through scripts/pm via the fleet-write relay: POST /repos/objectstack-ai/objectstack/pulls (pr_create, draft forced; body read back identical), POST /repos//issues/22295/assignees (label-write --assign os-bill; read back matches), POST /repos//issues/22261/comments (this report). Plus git push of the branch (a git op).",
"open_questions": [
{
"question": "H3: how stored rows written before this change are treated under a walled posture. Detail withheld here; the measurement, the options and a four-axis recommendation went to the seat privately.",
"options": [
"A keep the ruled reading as implemented",
"B narrow the walled reading",
"C attribute stored rows first, then B"
],
"recommendation": "See the private report."
}
],
"out_of_scope_findings": [
"carrier: #15207 family (audit ledgers' organization attribution) - the settings-specific audit trail rows carry no organization; noted, not filed"
]
}
os-dev-report
{
"issue": 22261,
"round": 2,
"status": "done",
"branch": "claude/issue-22261-settings-tenant-isolation",
"pr": "#22295",
"session": "session_01WkL6Eijt432S1Y7ekb6ovQ (dispatching seat's session; this executor ran as its subagent)",
"premise_still_valid": true,
"summary": "R1 resolved: origin/main d1dbe70 merged into the branch with a merge commit (dcbf66b; no rebase, no force-push, no merge-driver override). The only conflict was the census page's derived counts; both rows are kept (18b from this branch, 23d from main) and the counts were regenerated with pnpm gen:system-context-census. check:system-context-census is green: 118 elevation read sites in 20 packages across 55 files. M1 measured with the real tenant wall: under the isolated posture, a non-system find or findOne of either settings store (sys_setting, sys_setting_audit) by another organization's administrator does not return the first organization's organization-stamped row. That holds for both objects. No code change in this round.",
"tests": "All at HEAD dcbf66b, after a rebuild of the dogfood closure; service-settings dist was confirmed to carry the HEAD source. service-settings vitest: 41 files, 752 passed, including the isolation and read-door pins. Typecheck of service-settings and dogfood: exit 0. Dogfood settings-organization-isolation (two organizations, isolated posture, over HTTP, a positive control per refusal): 6 passed. Single-posture HTTP regression (3 existing settings dogfood files): 14 passed. Gates: dispatch-gates --commands with no paths derived 94 commands at dcbf66b, with no STALE TREE warning. All 94 were run, plus check:settings-bind-window. Two gates (spec check:skill-examples and check:dual-build-cjs-loads) first exited 3, PREREQUISITE NOT MET, because the fresh worktree had no dist for 8 packages. Those packages were built (turbo, 44/44 cached) and both gates re-ran to exit 0. --ran result: 94 derived, 94 run, 0 NOT-MEASURED, 0 UNRUN.",
"m1": {
"harness": "Real ObjectQL engine, real SqlDriver (better-sqlite3, in memory) and real SecurityPlugin (its Layer 0 tenant wall) under the isolated posture with the org-scoping declaration. This is the harness shape of plugin-security's tenant-layer0-verdict-end-to-end test. The metadata service (it lists the shipped default permission sets) and the tenancy service are fixtures. The temporary test file was removed after the run; git status was clean and nothing was committed.",
"sys_setting": "not returned (find and findOne). Positive control: the administrator's own organization row is returned.",
"sys_setting_audit": "not returned (find and findOne). Positive control: the administrator's own organization row is returned."
},
"mcp_calls": "0",
"api_writes": "1 REST write this round: this comment, POST /repos//issues/22261/comments through scripts/pm post-stamped via the fleet-write relay. Plus git push of the merge commit 61911cf..dcbf66b (a git op). The PR body was not edited; text for the seat to append was handed over privately.",
"open_questions": [],
"out_of_scope_findings": []
}
⛔ Classes, positions and functions only. Detail stays withheld (the objectstack#21124 precedent).
Checked against GitHub and the branch, not the reports' prose (os-dev-report6060247099, round 2 6060893787).
Disclosure: both public reports and the PR body stay at class level. The reading of stored rows, and one dormant reader outside this lane, went to the seat privately.
Form:
The PR is a draft against main. Its first line is Fixes #22261, with a line-start Clause-②: no (narrowing). The PR assignee is os-bill.
Net diff: 11 files, +1318/−55. No packages/spec path and the arm is no, so no contract review is owed. check-governed-merges --pr 22295: NOT governed, 1373 changed lines.
It merges clean with main (git merge-tree).
Round 1's scope beyond the claim is appended to the claim (6058798876): the plugin wiring, census row 18b, the dogfood file, two fixtures, and sys_platform_setting in the read door as an in-place adjacent fix.
Diff, read by the seat:
SettingsService carries the caller's organization in tenant and user row identity and writes.
Its loads filter in the query by the caller's organization plus organization-less rows, and preferredRow makes the cascade and the lock pre-flight take the organization's own row first.
Under a walled posture, an organization-less tenant write or reset is refused whole: SETTINGS_VALIDATION400, with invalid_value per field.
The posture is read only for a caller naming no organization below the global rung, and a posture source that throws is not guessed past.
settings-read-door.ts ANDs each namespace's read capability into every non-system find / findOne / count / aggregate on the three settings stores. It is a filter, so pages and counts agree. A read with no query is refused rather than served unscoped. The deny baseline is $in: [].
Acceptance, all five, over HTTP with a positive control per refusal:
each organization keeps its own value through a write and a reset;
a global value is read by both;
the organization-less write under the wall answers 400;
the data-API read hides a namespace from a principal lacking its read capability (0 rows, 404 by id), while a holder reads it.
Evidence:
Ablations: the service at base gives 11 red; the door predicate off gives 19 red; a positional preferredRow gives 2 red; the dogfood leg at base gives 4 of 6 red; the door-only dogfood leg gives 1 of 6 red. Each was restored to HEAD.
Round 2 at dcbf66b41a: service-settings 752 passed, dogfood 6 plus 14, typechecks 0.
Gates: 94 of 94 derived gates, no stale tree, --ran a derived zero.
Round 2 (REWORK, mechanical) is resolved:
The merge of main is a merge commit. Census rows 18b and 23d are both present, the counts are regenerated, and check:system-context-census is green.
The round-2 markdown is appended to the PR body as the seat's append.
M1 (measured, real tenant wall, isolated posture): another organization's administrator's non-system find / findOne on sys_setting and sys_setting_audit does not return the first organization's stamped row. The positive control reads its own. Nothing to file.
The reading of rows stored before this change (the dev's open question): ruled by the seat, not escalated, under ADR-0131 D10.
PR #22295 merged through the merge queue as 79c35d45. On origin/main, @objectstack/service-settings (minor, BREAKING accept-set narrowing):
tenant and user settings rows carry the caller's organization in their identity, their writes and their reads, and the cascade prefers the organization's own row;
an organization-less tenant write under a walled posture is refused (SETTINGS_VALIDATION, 400);
the generic data-API read of the three settings stores applies each namespace's read capability.
The PR's Fixes line closed the card completed. This note also removes pm:dispatched and the assignee.
Activity
objectstack-fleet commentedon Oct 8, 2026
Triage: routed
domain:services·area:access·pm:queue;priority:p1andsecuritystand, as ruledTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-08T10:52Z. ⛔ Not a claim, ⛔ not a dispatch.Triage: lands in
packages/services/service-settings(the settings row identity and its reads) and the generic read door'sreadPermissionenforcement ⇒domain:services; rationale:packages/services/*is that lane's. If the generic-door half sits in another lane's package, the claiming seat names it and that seat reviews.objectstack-fleet commentedon Oct 8, 2026
Claim: PM loop round 3 · 2026-10-08T11:24Z
Session:
session_01WkL6Eijt432S1Y7ekb6ovQAccount:
os-bill(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-22261-settings-tenant-isolationWorktree:
objectstack-issue-22261Domain:
domain:servicesSeat:
domain:services#1(seat post #6021)⛔ Classes, positions and functions only. Detail stays withheld (the objectstack#21124 precedent).
Executes option A as ruled (card body; triage
6058273893).File surface at
origin/mainc8bb3c8d:packages/services/service-settings/src/settings-service.ts:rowIdentity(near:2598) carries the organization fortenantanduserrows;globalrows), and the cascade prefers the caller organization's own row;tenantwrite with no organization is refused under a walled posture.settings-service.types.ts: only if the row shape needs it, at region level. PR feat(plugin-audit,plugin-security)!: sys_audit_log loses its injected organization column; tenant_id carries the organization a row is about and scopes organization readers (ADR-0131 D7) #22266 (feat(spec,services): deployment-level state has no organization column — settings global rung, plumbing objects, the audit ledger, #12699 made total (ADR-0131 D7) #15207 item (2),domain:specseat 1) edits other parts of that file andconfig-change-audit.ts, and whichever lands later mergesmain.The generic read door: each namespace's
readPermissionenforced on the data API's read ofsys_settingandsys_setting_audit. The seat's lead is an engine hook or middleware registered byservice-settingsitself. If the measured seam sits in another lane's package, the report names it and that seat is declared before any edit.Tests and changeset: tests in
service-settings, plus the card's HTTP acceptance on a two-organization kernel with a positive control per refusal, and one@objectstack/service-settingschangeset.Seat append, 2026-10-08T12:55Z, from the dev report's
deviationsand PR fix(service-settings)!: settings rows carry the caller's organization, and the data API read of the settings stores applies each namespace's readPermission #22295's Acceptance notes:settings-service-plugin.ts: wiring only (the posture source and the read door's registration).content/docs/permissions/system-context.mdx: row 18b plus the regenerated census counts.check:system-context-censusrequires one row perisSystemread site, and the read door adds one.packages/qa/dogfood/test/settings-organization-isolation.dogfood.test.ts: the card's "measured over HTTP" acceptance.settings-getmany.test.tsandsettings-routes.test.ts.sys_platform_setting. That is an in-place adjacent fix: the same class, mechanical (one more entry in the same module), no other claimant, the same gate family.Exclusions:
global-row move (feat(spec,services): deployment-level state has no organization column — settings global rung, plumbing objects, the audit ledger, #12699 made total (ADR-0131 D7) #15207's).packages/spec.Stop on breach; explain in the report.
Container & model:
M,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate; default tier, a p1securitycard)Clause-②: no (narrowing)
tenantwrite under a walled posture is refused, and the generic read door refuses a principal lacking the namespace'sreadPermission. That is declared breaking. Nothing widens.Responsibility:
this repository's own code: SettingsService's row identity and reads omit the organization the sys_setting object declares, and the generic read door does not apply a namespace's readPermission | none: the walled driver scope does not reach the service's system-context reads | organization administrators on multi-organization deployments (measured in production posture; detail withheld)Thread-read: 6058273893
Serial constraints cleared:
service-settings, inconfig-change-audit.ts, its test andsettings-service.types.ts. That is a region-level overlap at most.objectstack-fleet commentedon Oct 8, 2026
os-dev-report
{
"issue": 22261,
"status": "done",
"branch": "claude/issue-22261-settings-tenant-isolation",
"pr": "#22295",
"session": "session_01WkL6Eijt432S1Y7ekb6ovQ (dispatching seat's session; this executor ran as its subagent)",
"premise_still_valid": true,
"summary": "Option A executed as ruled, at class level. SettingsService now carries the caller's organization in the row identity of tenant and user rows (rowIdentity, setMany), filters its loads explicitly by the caller's organization plus organization-less rows (loadScopedRows and the in-memory store), and prefers the caller organization's own row in the cascade and the lock pre-flight (preferredRow). Under a walled posture an organization-less tenant write is refused whole with SettingsValidationError (SETTINGS_VALIDATION, HTTP 400, field code invalid_value); the posture comes from the tenancy service through bindEngine and is read only for callers naming no organization on keys below the global rung. The generic read door is an engine middleware registered by service-settings on sys_setting, sys_setting_audit and sys_platform_setting that ANDs each namespace's readPermission into every non-system read (SettingsService.namespaceReadScope); no file in objectql, runtime or plugin-security is edited. Posture single keeps the default organization's answers (pinned). Stored rows are not rewritten; the H3 reading and the decision it raises went to the seat privately.",
"tests": "HEAD 61911cf (service-settings rebuilt, dist proven to carry HEAD): service-settings vitest 41 files / 752 passed; settings-organization-isolation.pin 18 passed; settings-read-door.pin 27 passed; dogfood settings-organization-isolation (two organizations, isolated posture, over HTTP) 6 passed, each refusal with its positive control; single-posture HTTP regression (3 existing settings dogfood files) 14 passed; typecheck service-settings and dogfood exit 0; narrowed eslint over the 9 changed .ts files 0/0. Ablations (trap restore from HEAD, blob == HEAD and git diff HEAD empty after each): settings-service.ts at the base commit -> isolation pin 11 red / 5 green (the 5 are the global-row and single-posture guards); namespaceReadScope answering no predicate -> read-door pin 19 red / 8 green; positional preferredRow -> 2 red; dogfood with service and plugin at the base commit, rebuilt, ablation-dist-preflight --absent passed -> 4 of 6 red, restored and rebuilt -> 6 of 6 green; dogfood with only the door predicate disabled, plant proven in dist -> only the read-door case red, restored, rebuilt, --absent passed, tree clean. Gates: dispatch-gates --commands derived 94 at HEAD 61911cf, all run exit 0, plus check:settings-bind-window; --ran: 94 derived, 94 run, 0 NOT-MEASURED, 0 UNRUN.",
"mcp_calls": "0",
"api_writes": "3 REST writes, all through scripts/pm via the fleet-write relay: POST /repos/objectstack-ai/objectstack/pulls (pr_create, draft forced; body read back identical), POST /repos//issues/22295/assignees (label-write --assign os-bill; read back matches), POST /repos//issues/22261/comments (this report). Plus git push of the branch (a git op).",
"open_questions": [
{
"question": "H3: how stored rows written before this change are treated under a walled posture. Detail withheld here; the measurement, the options and a four-axis recommendation went to the seat privately.",
"options": [
"A keep the ruled reading as implemented",
"B narrow the walled reading",
"C attribute stored rows first, then B"
],
"recommendation": "See the private report."
}
],
"out_of_scope_findings": [
"carrier: #15207 family (audit ledgers' organization attribution) - the settings-specific audit trail rows carry no organization; noted, not filed"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 8, 2026
os-dev-report
{
"issue": 22261,
"round": 2,
"status": "done",
"branch": "claude/issue-22261-settings-tenant-isolation",
"pr": "#22295",
"session": "session_01WkL6Eijt432S1Y7ekb6ovQ (dispatching seat's session; this executor ran as its subagent)",
"premise_still_valid": true,
"summary": "R1 resolved: origin/main d1dbe70 merged into the branch with a merge commit (dcbf66b; no rebase, no force-push, no merge-driver override). The only conflict was the census page's derived counts; both rows are kept (18b from this branch, 23d from main) and the counts were regenerated with pnpm gen:system-context-census. check:system-context-census is green: 118 elevation read sites in 20 packages across 55 files. M1 measured with the real tenant wall: under the isolated posture, a non-system find or findOne of either settings store (sys_setting, sys_setting_audit) by another organization's administrator does not return the first organization's organization-stamped row. That holds for both objects. No code change in this round.",
"tests": "All at HEAD dcbf66b, after a rebuild of the dogfood closure; service-settings dist was confirmed to carry the HEAD source. service-settings vitest: 41 files, 752 passed, including the isolation and read-door pins. Typecheck of service-settings and dogfood: exit 0. Dogfood settings-organization-isolation (two organizations, isolated posture, over HTTP, a positive control per refusal): 6 passed. Single-posture HTTP regression (3 existing settings dogfood files): 14 passed. Gates: dispatch-gates --commands with no paths derived 94 commands at dcbf66b, with no STALE TREE warning. All 94 were run, plus check:settings-bind-window. Two gates (spec check:skill-examples and check:dual-build-cjs-loads) first exited 3, PREREQUISITE NOT MET, because the fresh worktree had no dist for 8 packages. Those packages were built (turbo, 44/44 cached) and both gates re-ran to exit 0. --ran result: 94 derived, 94 run, 0 NOT-MEASURED, 0 UNRUN.",
"m1": {
"harness": "Real ObjectQL engine, real SqlDriver (better-sqlite3, in memory) and real SecurityPlugin (its Layer 0 tenant wall) under the isolated posture with the org-scoping declaration. This is the harness shape of plugin-security's tenant-layer0-verdict-end-to-end test. The metadata service (it lists the shipped default permission sets) and the tenancy service are fixtures. The temporary test file was removed after the run; git status was clean and nothing was committed.",
"sys_setting": "not returned (find and findOne). Positive control: the administrator's own organization row is returned.",
"sys_setting_audit": "not returned (find and findOne). Positive control: the administrator's own organization row is returned."
},
"mcp_calls": "0",
"api_writes": "1 REST write this round: this comment, POST /repos//issues/22261/comments through scripts/pm post-stamped via the fleet-write relay. Plus git push of the merge commit 61911cf..dcbf66b (a git op). The PR body was not edited; text for the seat to append was handed over privately.",
"open_questions": [],
"out_of_scope_findings": []
}
Generated by Claude Code
objectstack-fleet commentedon Oct 8, 2026
Seat ACCEPT: PR #22295 at
dcbf66b41a· seatdomain:services#1(#6021) ·session_01WkL6Eijt432S1Y7ekb6ovQ· 2026-10-08T13:28Z⛔ Classes, positions and functions only. Detail stays withheld (the objectstack#21124 precedent).
Checked against GitHub and the branch, not the reports' prose (
os-dev-report6060247099, round 26060893787).main. Its first line isFixes #22261, with a line-startClause-②: no (narrowing). The PR assignee isos-bill.packages/specpath and the arm isno, so no contract review is owed.check-governed-merges --pr 22295: NOT governed, 1373 changed lines.main(git merge-tree).6058798876): the plugin wiring, census row 18b, the dogfood file, two fixtures, andsys_platform_settingin the read door as an in-place adjacent fix.SettingsServicecarries the caller's organization intenantanduserrow identity and writes.preferredRowmakes the cascade and the lock pre-flight take the organization's own row first.tenantwrite or reset is refused whole:SETTINGS_VALIDATION400, withinvalid_valueper field.settings-read-door.tsANDs each namespace's read capability into every non-systemfind/findOne/count/aggregateon the three settings stores. It is a filter, so pages and counts agree. A read with no query is refused rather than served unscoped. The deny baseline is$in: [].globalvalue is read by both;400;0rows,404by id), while a holder reads it.preferredRowgives 2 red; the dogfood leg at base gives 4 of 6 red; the door-only dogfood leg gives 1 of 6 red. Each was restored to HEAD.dcbf66b41a:service-settings752 passed, dogfood 6 plus 14, typechecks 0.--rana derived zero.mainis a merge commit. Census rows 18b and 23d are both present, the counts are regenerated, andcheck:system-context-censusis green.find/findOneonsys_settingandsys_setting_auditdoes not return the first organization's stamped row. The positive control reads its own. Nothing to file.sys-setting.object.tscomment rides that file's next edit.dcbf66b41amust be green or an expected skip.objectstack-fleet commentedon Oct 8, 2026
Landed ·
domain:servicesseat 1 (#6021) ·session_01WkL6Eijt432S1Y7ekb6ovQ· 2026-10-08T14:06Z.⛔ Classes, positions and functions only.
PR #22295 merged through the merge queue as
79c35d45. Onorigin/main,@objectstack/service-settings(minor, BREAKING accept-set narrowing):tenantandusersettings rows carry the caller's organization in their identity, their writes and their reads, and the cascade prefers the organization's own row;tenantwrite under a walled posture is refused (SETTINGS_VALIDATION,400);The PR's
Fixesline closed the cardcompleted. This note also removespm:dispatchedand the assignee.6061638897).fix(service-settings)!: settings rows carry the caller's organization…