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
Commit df231b4
Browse filesBrowse the repository at this point in the historyBrowse files
chore(docs): re-derive the isSystem census after merging origin/main
The merge of origin/main routed content/docs/permissions/system-context.mdx
through the os-regen driver, which exits 0 without text-merging and leaves
git's pre-filled OURS side in place. That silently dropped the 16 anchor
re-points main had landed (#13829, #13934, #13910, #13857) while keeping this
branch's single re-point.
This commit takes main's side of the page and re-derives every anchor from the
merged tree with `pnpm gen:system-context-census`, which re-pointed row 21's
metadata-protocol/src/protocol.ts anchor to 1736. Prose is byte-identical on
both sides once line numbers are normalised, so nothing but line numbers moved.
Copy file name to clipboardExpand all lines: content/docs/permissions/system-context.mdx
+16-16Lines changed: 16 additions & 16 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -64,7 +64,7 @@ not on any flag.
64
64
## How the flag is set
65
65
66
66
`isSystem` is **server-constructed and never client-supplied**. Inbound HTTP
67
-
cannot set it (`packages/rest/src/rest-server.ts:1302`, `:1331`), and neither
67
+
cannot set it (`packages/rest/src/rest-server.ts:1389`, `:1418`), and neither
68
68
can an action body (`packages/runtime/src/domains/actions.ts:404`). It is
69
69
written by internal callers only, as an option on the engine call:
70
70
@@ -103,24 +103,24 @@ that silently does not happen.
103
103
| 14 | MCP stdio bridge skips the object API-exposure gate | mcp | Get: the bridge reaches objects whose `apiEnabled` / `apiMethods` would refuse an external caller |`stdio-data-bridge.ts:246`|
104
104
| 15 |**Read-audit rows are not written**| plugin-audit | Lose: the "a person opened this record" trail. `sudo()` keeps the caller's `userId`, so this flag is the only thing separating a human read from a platform one |`read-audit.ts:556`|
105
105
| 16 | Approval snapshot payload redaction skipped | plugin-approvals | Get: the whole snapshot on `find` / `findOne` — the audit/replay channel. Lose: field-visibility redaction over approval payloads |`payload-redaction-middleware.ts:115`|
106
-
| 17 | REST anonymous-deny seam satisfied | rest | Get: `enforceAuth` passes with no `userId`. Not reachable from the wire — `isSystem` is never set on an inbound request |`rest-server.ts:1334`|
106
+
| 17 | REST anonymous-deny seam satisfied | rest | Get: `enforceAuth` passes with no `userId`. Not reachable from the wire — `isSystem` is never set on an inbound request |`rest-server.ts:1421`|
107
107
108
108
### 2. Write pipeline and data integrity
109
109
110
110
| # | Behaviour when `isSystem`| Package | What you get / what you lose | Anchor |
111
111
|:--|:---|:---|:---|:---|
112
-
| 18 |**`readonly` strip bypassed — UPDATE, single row**| objectql | Get: a `readonly` field CAN be written. Lose: the protection that stops a caller seeding e.g. `approval_status`|`objectql/src/engine.ts:10712`|
113
-
| 19 |**`readonly` strip bypassed — UPDATE, bulk/predicate**| objectql | Same, on the multi-row path |`objectql/src/engine.ts:10874`|
| 18 |**`readonly` strip bypassed — UPDATE, single row**| objectql | Get: a `readonly` field CAN be written. Lose: the protection that stops a caller seeding e.g. `approval_status`|`objectql/src/engine.ts:10787`|
113
+
| 19 |**`readonly` strip bypassed — UPDATE, bulk/predicate**| objectql | Same, on the multi-row path |`objectql/src/engine.ts:10949`|
| 21 |**`readonly` strip bypassed — INSERT (protocol ingress)**| metadata-protocol |`isSystem` is the **only** exemption here. `preserveAudit` is deliberately not read on this path (#6640) — a non-system historical import is still stripped on create |`metadata-protocol/src/protocol.ts:1736`|
116
-
| 22 | Strict-drop refusal never fires | objectql | Lose: a caller that opted into loud refusal gets **silence** — strict refuses exactly what the strip would have taken, and the strip took nothing |`objectql/src/engine.ts:9642`, `readonly-strict-errors.ts:66`|
117
-
| 23 |**Referential-integrity check skipped**| objectql | Get: writes proceed against unreachable/unresolvable targets. Lose: an `isSystem` caller can write a **dangling reference**|`objectql/src/engine.ts:5639`|
116
+
| 22 | Strict-drop refusal never fires | objectql | Lose: a caller that opted into loud refusal gets **silence** — strict refuses exactly what the strip would have taken, and the strip took nothing |`objectql/src/engine.ts:9717`, `readonly-strict-errors.ts:66`|
117
+
| 23 |**Referential-integrity check skipped**| objectql | Get: writes proceed against unreachable/unresolvable targets. Lose: an `isSystem` caller can write a **dangling reference**|`objectql/src/engine.ts:5705`|
118
118
| 24 | Tenant-audit warning silenced; `bypassTenantAudit` threaded to the driver | objectql | Get: unscoped system writes stop warning. Lose: the signal that would flag a genuine user-path scoping bug |`objectql/src/engine.ts:3574`, `:3584`, `:3611`|
| 26 | Identity write guard bypassed (ADR-0092) | plugin-auth | Get: direct writes to identity tables through the generic data path |`identity-write-guard.ts:98`|
121
-
| 27 | Search-companion column **kept** in a read's rows when it was explicitly requested | objectql | Get: the internal companion column is readable. Lose: nothing for app code — this is the engine reading its own index |`objectql/src/engine.ts:6337`|
122
-
| 28 | Dependent-count disclosure on a blocked delete | objectql | Get: the count of blocking children. Nothing was elevated past the caller, so nothing is withheld |`objectql/src/engine.ts:11460`|
123
-
| 29 | Reference-cleanup log attributes the write to `'system'`| objectql | Get: an honest actor label instead of `anonymous` when the context carries neither `userId` nor `actor`|`objectql/src/engine.ts:11389`|
121
+
| 27 | Search-companion column **kept** in a read's rows when it was explicitly requested | objectql | Get: the internal companion column is readable. Lose: nothing for app code — this is the engine reading its own index |`objectql/src/engine.ts:6403`|
122
+
| 28 | Dependent-count disclosure on a blocked delete | objectql | Get: the count of blocking children. Nothing was elevated past the caller, so nothing is withheld |`objectql/src/engine.ts:11535`|
123
+
| 29 | Reference-cleanup log attributes the write to `'system'`| objectql | Get: an honest actor label instead of `anonymous` when the context carries neither `userId` nor `actor`|`objectql/src/engine.ts:11464`|
124
124
125
125
### 3. Sharing (`plugin-sharing`)
126
126
@@ -135,7 +135,7 @@ The largest single consumer — **20 of the 109 sites**.
135
135
| 34 |`revoke()` deletes directly, **before** the non-manual-source guard | Get: the evaluator can revoke its own grants. Lose: the `CONFLICT` guard that warns a rule-materialised grant will be silently re-granted on the next reconcile |`plugin-sharing/src/sharing-service.ts:1286` (guard at `:1311`) |
136
136
| 35 |`listShares()` skips the management gate | Get: full enumeration of who can see a record |`plugin-sharing/src/sharing-service.ts:1338`|
137
137
| 36 |`sys_record_share` reads are **not** self-scoped | Get: tenant-wide share listing without `manage_sharing`|`sharing-plugin.ts:1077`|
138
-
| 37 | Share-link policy `enabled` check bypassed; system callers re-enter under a system context | Get: link creation/resolution while the policy is off |`plugin-sharing/src/share-link-service.ts:413`, `:467`, `:471`, `:544`, `:574`|
138
+
| 37 | Share-link policy `enabled` check bypassed; system callers re-enter under a system context | Get: link creation/resolution while the policy is off |`plugin-sharing/src/share-link-service.ts:423`, `:477`, `:481`, `:554`, `:584`|
139
139
| 38 | Sharing-rule provenance stamp skipped | Lose: the row is not marked as an admin customization — seeder / `defineRule` / boot reconcilers are "the package door" |`sharing-rule-provenance.ts:47`|
140
140
| 39 | Sharing-rule service write + delete paths return early | Lose: the manage-rules gate on the service surface, and the platform-global-rule delete guard |`sharing-rule-service.ts:157`, `:382`|
141
141
@@ -145,7 +145,7 @@ The largest single consumer — **20 of the 109 sites**.
145
145
|:--|:---|:---|:---|:---|
146
146
| 40 |**Approval record lock released** — a locked record is writable | plugin-approvals | Get: engine self-writes (the status mirror) pass. Lose: the lock that stops edits while an approval is live. Note there is deliberately **no admin exemption** here — only `isSystem`|`lifecycle-hooks.ts:333`|
147
147
| 41 | Delegation write guard bypassed | plugin-approvals | Get: service / seed / import may write delegation rows naming another delegator |`lifecycle-hooks.ts:440`|
148
-
| 42 | Approval actor / submitter / pending-approver checks bypassed (8 sites) | plugin-approvals | Get: approve, reject, recall, reassign without being a pending approver or the submitter |`plugin-approvals/src/approval-service.ts:850`, `:959`, `:2916`, `:3062`, `:3229`, `:3300`, `:3489`, `:3529`|
148
+
| 42 | Approval actor / submitter / pending-approver checks bypassed (8 sites) | plugin-approvals | Get: approve, reject, recall, reassign without being a pending approver or the submitter |`plugin-approvals/src/approval-service.ts:931`, `:1040`, `:2997`, `:3143`, `:3310`, `:3381`, `:3570`, `:3610`|
149
149
| 43 | Saved-report ownership is **assignable**, and an update may reassign it | plugin-reports | Get: `ownerId` from input is honoured. A non-system caller always owns what it creates and can never reassign |`plugin-reports/src/report-service.ts:404`, `:425`|
| 50 |`manage_metadata` bypassed on metadata writes | runtime, rest | Get: schema writes without the capability |`domains/meta.ts:471`, `:874`, `rest-server.ts:4470`, `:5833`, `:6081`, `:6512`, `:6705`|
161
+
| 50 |`manage_metadata` bypassed on metadata writes | runtime, rest | Get: schema writes without the capability |`domains/meta.ts:471`, `:874`, `rest-server.ts:4573`, `:5936`, `:6184`, `:6615`, `:6808`|
162
162
| 51 | The shared metadata-write verdict itself returns `allowed`| metadata-core | Get: the one function all of row 50's doors consult answers yes before any capability is examined |`meta-write-capability.ts:134`|
163
163
| 52 | Anonymous-deny seam satisfied on the domain dispatchers and the package/federation routes | runtime, rest | Get: passes with no `userId`|`domains/actions.ts:411`, `domains/ai.ts:60`, `domains/automation.ts:989`, `domains/meta.ts:232`, `domains/security.ts:78`, `domains/packages.ts:246`, `external-datasource-routes.ts:302`, `package-routes.ts:97`|
164
164
| 53 | MCP principal check satisfied | runtime | Get: MCP surface reachable with no user |`domains/mcp.ts:61`|
@@ -180,7 +180,7 @@ a reader tracing where elevation travels needs them.
180
180
| # | Site | Package | What it does |
181
181
|:--|:---|:---|:---|
182
182
| 62 |`objectql/src/engine.ts:3406`| objectql | Propagates `isSystem` into the hook session so hooks can tell engine self-writes from user writes |
183
-
| 63 |`objectql/src/engine.ts:13801`| objectql |`ScopedContext.isSystem` getter — re-exposes the underlying execution context's flag |
183
+
| 63 |`objectql/src/engine.ts:13876`| objectql |`ScopedContext.isSystem` getter — re-exposes the underlying execution context's flag |
184
184
| 64 |`plugin-reports/src/report-service.ts:556`| plugin-reports | Threads the flag into the engine call that runs a report |
185
185
| 65 |`body-runner.ts:279`| runtime | Rebuilds an `ExecutionContext` from a hook session, carrying the flag across |
186
186
@@ -195,11 +195,11 @@ assuming `isSystem` covers it is a documented source of bugs.
195
195
|:---|:---|:---|
196
196
| "It suppresses triggers / record-change automation" |**No.** Only `skipTriggers` does. A bare `{ isSystem: true }` on a seed write re-fired automation on freshly seeded rows and wedged first boot |`metadata-protocol/src/seed-loader.ts:1909` (rationale at `:1819`–`1821`, #3760), `flow.zod.ts:685`|
197
197
| "It skips the state machine" |**No.** That is `skipStateMachine`, carried by seed replay and by `treatAsHistorical` imports |`objectql/src/engine.ts` FSM gate; see [State Machine](/docs/protocol/objectql/state-machine)|
198
-
| "It skips validation rules" |**No.** Field shape, `format`, `script` and the rest still run. The `readonly` strip runs *before* validation precisely so a discarded value is not judged |`objectql/src/engine.ts:9588`–`9605`|
198
+
| "It skips validation rules" |**No.** Field shape, `format`, `script` and the rest still run. The `readonly` strip runs *before* validation precisely so a discarded value is not judged |`objectql/src/engine.ts:9663`–`9680`|
199
199
| "It preserves a supplied `updated_at` / `updated_by`" |**No.** That is `preserveAudit`, a separate opt-in — and an UPDATE-path exemption only |`field.zod.ts:1516` (#3493 / #6640) |
200
200
| "It stamps `created_by`" |**No.** Audit stamping reads `userId` from the context. A user-less system write stamps nothing — that is today's behaviour, not an error |`runtime-identity.ts:280`–`281`|
201
201
| "It bypasses every guard" |**No.** The last-admin guard applies to **every** context, `isSystem` included — the deprovision path that actually locks an org out is the system one |`last-admin-guard.ts:286`|
202
-
| "A client can request it" |**No.** Never settable from inbound HTTP or from an action body |`rest-server.ts:1302`, `:1331`; `domains/actions.ts:404`|
202
+
| "A client can request it" |**No.** Never settable from inbound HTTP or from an action body |`rest-server.ts:1389`, `:1418`; `domains/actions.ts:404`|
0 commit comments