@substrat-run/kernel 0.136.0 → 0.138.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/capability.d.ts +2 -0
  2. package/dist/capability.d.ts.map +1 -1
  3. package/dist/capability.js +2 -0
  4. package/dist/capability.js.map +1 -1
  5. package/dist/delivery-refusal.d.ts +21 -0
  6. package/dist/delivery-refusal.d.ts.map +1 -0
  7. package/dist/delivery-refusal.js +21 -0
  8. package/dist/delivery-refusal.js.map +1 -0
  9. package/dist/index.d.ts +23 -17
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +18 -12
  12. package/dist/index.js.map +1 -1
  13. package/dist/invocation-log.d.ts +25 -1
  14. package/dist/invocation-log.d.ts.map +1 -1
  15. package/dist/invocation-log.js +45 -6
  16. package/dist/invocation-log.js.map +1 -1
  17. package/dist/job-run.d.ts +370 -47
  18. package/dist/job-run.d.ts.map +1 -1
  19. package/dist/job-run.js +472 -81
  20. package/dist/job-run.js.map +1 -1
  21. package/dist/lifecycle-flow.d.ts.map +1 -1
  22. package/dist/lifecycle-flow.js +2 -1
  23. package/dist/lifecycle-flow.js.map +1 -1
  24. package/dist/membership-executor.d.ts +97 -0
  25. package/dist/membership-executor.d.ts.map +1 -0
  26. package/dist/membership-executor.js +246 -0
  27. package/dist/membership-executor.js.map +1 -0
  28. package/dist/membership-fence.d.ts +57 -0
  29. package/dist/membership-fence.d.ts.map +1 -0
  30. package/dist/membership-fence.js +90 -0
  31. package/dist/membership-fence.js.map +1 -0
  32. package/dist/operation-series.d.ts +58 -0
  33. package/dist/operation-series.d.ts.map +1 -0
  34. package/dist/operation-series.js +63 -0
  35. package/dist/operation-series.js.map +1 -0
  36. package/dist/peer.d.ts +13 -8
  37. package/dist/peer.d.ts.map +1 -1
  38. package/dist/peer.js +13 -14
  39. package/dist/peer.js.map +1 -1
  40. package/dist/permission-eval.d.ts +48 -1
  41. package/dist/permission-eval.d.ts.map +1 -1
  42. package/dist/permission-eval.js +137 -18
  43. package/dist/permission-eval.js.map +1 -1
  44. package/dist/read-only-sql.d.ts +2 -26
  45. package/dist/read-only-sql.d.ts.map +1 -1
  46. package/dist/read-only-sql.js +13 -9
  47. package/dist/read-only-sql.js.map +1 -1
  48. package/dist/refusal-query.d.ts +4 -1
  49. package/dist/refusal-query.d.ts.map +1 -1
  50. package/dist/refusal-query.js +6 -4
  51. package/dist/refusal-query.js.map +1 -1
  52. package/dist/refusals.d.ts +80 -8
  53. package/dist/refusals.d.ts.map +1 -1
  54. package/dist/refusals.js +141 -22
  55. package/dist/refusals.js.map +1 -1
  56. package/dist/scope-copy.d.ts +18 -0
  57. package/dist/scope-copy.d.ts.map +1 -1
  58. package/dist/scope-copy.js +18 -0
  59. package/dist/scope-copy.js.map +1 -1
  60. package/dist/scope-host.d.ts +266 -15
  61. package/dist/scope-host.d.ts.map +1 -1
  62. package/dist/scope-host.js +41 -0
  63. package/dist/scope-host.js.map +1 -1
  64. package/dist/spine-restore.d.ts +7 -2
  65. package/dist/spine-restore.d.ts.map +1 -1
  66. package/dist/spine-restore.js +13 -5
  67. package/dist/spine-restore.js.map +1 -1
  68. package/dist/system-switch-record.d.ts +290 -74
  69. package/dist/system-switch-record.d.ts.map +1 -1
  70. package/dist/system-switch-record.js +353 -101
  71. package/dist/system-switch-record.js.map +1 -1
  72. package/dist/system-switch.d.ts +146 -20
  73. package/dist/system-switch.d.ts.map +1 -1
  74. package/dist/system-switch.js +135 -27
  75. package/dist/system-switch.js.map +1 -1
  76. package/dist/timeline.d.ts +23 -0
  77. package/dist/timeline.d.ts.map +1 -1
  78. package/dist/timeline.js +30 -0
  79. package/dist/timeline.js.map +1 -1
  80. package/dist/ulid.d.ts +5 -0
  81. package/dist/ulid.d.ts.map +1 -1
  82. package/dist/ulid.js +9 -0
  83. package/dist/ulid.js.map +1 -1
  84. package/dist/vertical-events.d.ts +7 -1
  85. package/dist/vertical-events.d.ts.map +1 -1
  86. package/dist/vertical-events.js +14 -0
  87. package/dist/vertical-events.js.map +1 -1
  88. package/package.json +4 -4
@@ -19,19 +19,25 @@
19
19
  * `scope:<id>` marker. The gate is OFF while that marker is live, whatever grants are
20
20
  * live beside it. **Restore is the lever; a grant is not.** The marker is an ordinary
21
21
  * K-21 tuple: switching back on tombstones it, so the row stays as evidence of when the
22
- * switch was last pulled. The permission walk reads only `role:` and `granted:` relations
23
- * (`permission-eval.ts`), so the marker authorizes nothing and denies nothing by itself.
22
+ * switch was last pulled. The permission walk reads only `role:` and `granted:` relations,
23
+ * so the marker authorizes nothing; what it denies, it denies through the one question the
24
+ * evaluator asks before the walk (below).
24
25
  *
25
26
  * Switching off ALSO tombstones every live `granted:` tuple the module holds on the scope.
26
27
  * That is what makes it a kill switch rather than a schedule pause: anything that acts
27
28
  * with the module's system authority — a resumable job run (#1577), a `getSystemScope`
28
29
  * invoke — is denied by its own `ctx.check` while the switch is off.
29
30
  *
30
- * **And nothing may write a new one while it is off**, which is what makes that denial
31
- * hold. The checker is deliberately NOT marker-aware; the switch is closed on the WRITE
32
- * side instead, at the only two writers of a scope-level `system:` grant:
31
+ * **And the checker reads the marker too** (#1823), which is what makes that denial hold
32
+ * against a grant OFF cannot tombstone. A TENANT-level `system:` grant lives in the directory
33
+ * (and in every scope's projection of it), not at `scope:<id>`, so OFF leaves it live. The
34
+ * permission evaluator asks the scope reader's `switchedOff` before it reads any tuple, and a
35
+ * live marker denies the subject on that scope whatever it holds (`permission-eval.ts`). The
36
+ * write side stays closed as well, so a switched-off scope does not accumulate grants that
37
+ * ON would then hand back:
33
38
  * - `grantToSystem` refuses while the module is switched off on that scope (the adapters
34
- * ask `systemSwitchedOff` in the same unit as the write);
39
+ * ask `systemSwitchedOff` in the same unit as the write), and a tenant-level grant while
40
+ * the directory records it off on any scope (#1743);
35
41
  * - provisioning's seat (`seatScopeTuple`) seats nothing for a subject whose marker is
36
42
  * live, so a reconcile cannot create a grant a newer version declares.
37
43
  *
@@ -62,6 +68,15 @@ export interface SwitchSql {
62
68
  * write. Quiet, exactly as before the switch existed: no run, no skip, no error.
63
69
  */
64
70
  export type SystemScheduleState = 'on' | 'off' | 'ungranted';
71
+ /**
72
+ * Which kill switch: a module's system authority (`system:<module>`, #1666) or a peer
73
+ * vertical's (`vertical:<slug>`, #1706). Both move the same marker through `switchSubjectGrants`.
74
+ */
75
+ export type SwitchKind = 'system' | 'peer';
76
+ /** The tuple-subject prefix of a peer vertical (#1706) — `vertical:acme/board-room`. Re-exported by `peer.ts`. */
77
+ export declare const PEER_SUBJECT_PREFIX = "vertical:";
78
+ /** The tuple subject a peer's grants are seated under. The slug only, never the instance. Re-exported by `peer.ts`. */
79
+ export declare const peerSubjectRef: (vertical: string) => string;
65
80
  /**
66
81
  * "Is this subject's OFF marker live?", as a SQL predicate over ONE bound parameter (the
67
82
  * subject). The one spelling the gate, the grant refusal and the provisioning seat share,
@@ -97,8 +112,15 @@ export interface SystemGrantsEntry {
97
112
  export declare function systemGrantsStatus(db: SwitchSql, now: string): SystemGrantsEntry[];
98
113
  /** Is this module switched off on the scope `db` is? What `grantToSystem` refuses on. */
99
114
  export declare function systemSwitchedOff(db: SwitchSql, moduleId: string): boolean;
115
+ /**
116
+ * `SYSTEM_SWITCH_OFF_PREDICATE` as a whole statement answering `off` (0/1), over the one bound
117
+ * subject — what `subjectSwitchedOff` runs, and what each adapter's permission reader prepares
118
+ * once and runs on every check of a switchable subject (#1823).
119
+ */
120
+ export declare const SYSTEM_SWITCH_OFF_QUERY = "SELECT EXISTS (SELECT 1 FROM _substrat_tuples\n WHERE subject = ? AND relation = 'switch:off' AND revoked_at IS NULL) AS off";
100
121
  /** Is this subject's OFF marker live on the scope `db` is? Any subject — see `switchSubjectGrants`. */
101
122
  export declare function subjectSwitchedOff(db: SwitchSql, subject: string): boolean;
123
+ export declare const isSwitchableSubjectKind: (kind: string) => boolean;
102
124
  /** The refusal `grantToSystem` throws while the switch is off — one wording, both adapters. */
103
125
  export declare function systemSwitchedOffMessage(moduleId: string, scopeId: string): string;
104
126
  /**
@@ -111,7 +133,41 @@ export interface SwitchOutcome {
111
133
  held: boolean;
112
134
  changed: boolean;
113
135
  permissions: string[];
136
+ /**
137
+ * #1823: this code's evaluator denies a module on a scope whose marker is live, its
138
+ * TENANT-level grants included. Set by `switchSystemSchedules` on every answer, because it
139
+ * is a fact about the code that answered, not about the call: a deployment built before
140
+ * #1823 omits it, and its OFF leaves a tenant-level grant authorizing. The platform reads
141
+ * its absence on an OFF of a module it found tenant-held as "this deployment cannot hold
142
+ * that OFF", and refuses it rather than record a scope off that is not.
143
+ */
144
+ deniesTenantGrants?: true;
145
+ /**
146
+ * #2045: the move was refused, and nothing written, because the scope has already applied a
147
+ * NEWER switch call on this subject (`SWITCH_FENCES_DDL`). The scope stays where the newer call
148
+ * put it, which is also where the directory's record is. Absent from a deployment built before
149
+ * the fence, which applies every move it is sent.
150
+ */
151
+ superseded?: true;
152
+ /**
153
+ * #2045 (Codex r2): this code honoured the call's fence — set on every answer to a move that
154
+ * carried one, superseded or not. A deployment built before the fence drops the field and the
155
+ * fence with it, and would apply an older call's move after a newer one, so the platform refuses
156
+ * a fenced move whose answer lacks this, as it refuses an OFF without `deniesTenantGrants`.
157
+ */
158
+ fenced?: true;
114
159
  }
160
+ /**
161
+ * #2045: the fence a scope keeps per switched subject — the operation id (a ULID) of the newest
162
+ * switch call it has applied. The directory's record (`system-switch-record.ts`) refuses a write
163
+ * older than the one it holds, and the scope refuses a move older than the one it applied, so the
164
+ * two always end on the SAME call's position, whatever order two concurrent calls' writes and moves
165
+ * arrive in. Without it, operator A recording OFF, then B recording ON and moving ON, then A's OFF
166
+ * landing, left the record ON beside a scope that is OFF, and a later restore re-admitted the
167
+ * subject. Keyed by (subject, object) as the marker is, so a copy of another scope's rows fences
168
+ * nothing here. Part of the scope's own storage: a restore or rewind takes it back with the marker.
169
+ */
170
+ export declare const SWITCH_FENCES_DDL = "\n CREATE TABLE IF NOT EXISTS _substrat_switch_fences (\n subject TEXT NOT NULL,\n object TEXT NOT NULL,\n -- The operation id of the newest switch call applied here. A ULID, so it orders by text.\n fence TEXT NOT NULL,\n PRIMARY KEY (subject, object)\n );\n";
115
171
  /**
116
172
  * Move one module's switch on one scope. Idempotent: a repeat changes nothing and says so.
117
173
  *
@@ -121,9 +177,13 @@ export interface SwitchOutcome {
121
177
  * marker. A repeated OFF re-asserts the whole position: a grant that became live meanwhile
122
178
  * (only a raw write can do that now) is tombstoned and recorded too.
123
179
  *
124
- * `held: false`, with nothing written, when the scope holds neither a grant nor a marker
125
- * for the module: the caller named something this scope never ran, and turning "nothing"
126
- * off must not write a marker that silently disables a module the day it is installed.
180
+ * `held: false`, with nothing written, when the module has no authority reaching the scope:
181
+ * no grant and no marker here, and no live TENANT-level grant (`tenantHeld`, which the caller
182
+ * reads from the directory, since the scope's storage cannot answer it). The caller named
183
+ * something this scope never ran, and turning "nothing" off must not write a marker that
184
+ * silently disables a module the day it is installed. A module whose only authority here is a
185
+ * tenant-level grant IS held (#1823): OFF writes the marker, which the evaluator then denies
186
+ * the module on, and tombstones nothing, because nothing of it is here to tombstone.
127
187
  *
128
188
  * Run it inside one transaction; the reads and writes here are meant to be one unit.
129
189
  */
@@ -132,29 +192,87 @@ export declare function switchSystemSchedules(db: SwitchSql, input: {
132
192
  scopeId: string;
133
193
  to: 'on' | 'off';
134
194
  at: string;
195
+ tenantHeld?: boolean;
196
+ fence?: string;
135
197
  }): SwitchOutcome;
136
- /** One module `switchRecordedOff` switched off — `SwitchedOffInUnit`'s shape. */
137
- export interface SwitchedOff extends SwitchOutcome {
198
+ /**
199
+ * One subject `switchRecordedOff` switched off — `SwitchedOffInUnit`'s shape: a module
200
+ * (`moduleId`), or since #2029 a peer vertical (`vertical`).
201
+ */
202
+ export type SwitchedOff = SwitchOutcome & ({
138
203
  moduleId: string;
204
+ vertical?: never;
205
+ } | {
206
+ vertical: string;
207
+ moduleId?: never;
208
+ });
209
+ /**
210
+ * What the platform carries into a unit that re-creates a scope's grants (#1742): the subjects
211
+ * its directory records OFF on that scope, and of those, the ones held there only by a
212
+ * tenant-level grant (#1823, #2030). Subjects only, never a scope: the unit names its own.
213
+ */
214
+ export interface RecordedOffCarry {
215
+ moduleIds: readonly string[];
216
+ tenantHeld?: readonly string[];
217
+ /** #2029: the recorded-off peers. Absent from a platform that predates the peer record. */
218
+ verticals?: readonly string[];
219
+ tenantHeldVerticals?: readonly string[];
220
+ /**
221
+ * #2045: the fence of each recorded-off subject, by tuple subject (`system:<m>`, `vertical:<v>`):
222
+ * the operation id of the call the record holds. A subject the scope has since moved under a newer
223
+ * call is left as that call put it. Absent from a platform built before the fence.
224
+ */
225
+ fences?: Readonly<Record<string, string>>;
139
226
  }
140
227
  /**
141
- * Switch the directory's recorded-off modules off again, inside the unit that re-created the
228
+ * The same carry as it crosses the wire to a vertical's deployment (#1742): the provision,
229
+ * reconcile and restore bodies, and `SwitchCarry` on the platform side. `switchedOffPeers` and
230
+ * `tenantHeldPeers` are #2029's; a deployment built before them strips both, and the platform's
231
+ * re-assert after the call switches those peers off instead.
232
+ */
233
+ export interface SwitchCarryWire {
234
+ switchedOff?: readonly string[];
235
+ tenantHeld?: readonly string[];
236
+ switchedOffPeers?: readonly string[];
237
+ tenantHeldPeers?: readonly string[];
238
+ /** #2045: `RecordedOffCarry.fences`, as it crosses the wire. A deployment built before it strips it. */
239
+ switchFences?: Readonly<Record<string, string>>;
240
+ }
241
+ /** A wire carry as the unit runs it, or undefined when it names nothing to switch off. */
242
+ export declare function recordedOffFromWire(wire: SwitchCarryWire): RecordedOffCarry | undefined;
243
+ /**
244
+ * Switch the directory's recorded-off subjects off again, inside the unit that re-created the
142
245
  * scope's grants (#1742): a provision's or reconcile's seat, or a restore's replay. The caller
143
246
  * runs it in that same unit, AFTER the seat, so the grants the seat created are the ones OFF
144
247
  * tombstones and records, and a later restore gives back exactly those (#1674's order). No
145
- * schedule can run between the seat and this, because nothing can run between them.
248
+ * schedule can run, and no peer call be admitted, between the seat and this, because nothing
249
+ * can run between them.
146
250
  *
147
- * `scopeId` is the scope `db` is, and the caller passes the one the request provisions or
148
- * restores, never a second id: the list names modules, never scopes, so it can only reach
149
- * the scope that unit is already writing. A module the scope holds nothing for answers
150
- * `held: false` and writes nothing, as the switch always does. Off only: nothing here turns
151
- * a module on.
251
+ * `moduleIds` are the recorded-off modules, `verticals` the recorded-off peers (#2029). `scopeId`
252
+ * is the scope `db` is, and the caller passes the one the request provisions or restores, never
253
+ * a second id: the lists name subjects, never scopes, so they can only reach the scope that unit
254
+ * is already writing. A subject the scope holds nothing for answers `held: false` and writes
255
+ * nothing, as the switch always does — unless it is in `tenantHeld` / `tenantHeldVerticals`, the
256
+ * recorded-off subjects the caller found a live tenant-level grant for (#1823, #2030). Off only:
257
+ * nothing here turns anything on.
152
258
  */
153
- export declare function switchRecordedOff(db: SwitchSql, input: {
259
+ export declare function switchRecordedOff(db: SwitchSql, input: RecordedOffCarry & {
154
260
  scopeId: string;
155
- moduleIds: readonly string[];
156
261
  at: string;
157
262
  }): SwitchedOff[];
263
+ /**
264
+ * Move one kill switch of either kind on one scope (#2029): `switchSystemSchedules` for a
265
+ * module, `switchSubjectGrants` over `vertical:<key>` for a peer. `key` is the module id or the
266
+ * peer's slug. The one mover the adapters' shared switch body and their re-asserts call.
267
+ */
268
+ export declare function moveSwitch(db: SwitchSql, kind: SwitchKind, input: {
269
+ key: string;
270
+ scopeId: string;
271
+ to: 'on' | 'off';
272
+ at: string;
273
+ tenantHeld?: boolean;
274
+ fence?: string;
275
+ }): SwitchOutcome;
158
276
  /**
159
277
  * The switch itself, for ANY non-person subject's scope-level grants — `switchSystemSchedules`
160
278
  * for a schedule (`system:<module>`), and the peer kill switch for another vertical
@@ -169,5 +287,13 @@ export declare function switchSubjectGrants(db: SwitchSql, input: {
169
287
  scopeId: string;
170
288
  to: 'on' | 'off';
171
289
  at: string;
290
+ tenantHeld?: boolean;
291
+ /**
292
+ * #2045: the switch call this move belongs to — its operation id, or for a re-assert the one the
293
+ * directory's record holds. A move older than the fence the scope keeps is refused (`superseded`);
294
+ * one at or past it applies and moves the fence to it. Absent, the move applies and leaves the
295
+ * fence alone: a caller from before the fence.
296
+ */
297
+ fence?: string;
172
298
  }): SwitchOutcome;
173
299
  //# sourceMappingURL=system-switch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"system-switch.d.ts","sourceRoot":"","sources":["../src/system-switch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"system-switch.d.ts","sourceRoot":"","sources":["../src/system-switch.ts"],"names":[],"mappings":"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"}
@@ -19,19 +19,25 @@
19
19
  * `scope:<id>` marker. The gate is OFF while that marker is live, whatever grants are
20
20
  * live beside it. **Restore is the lever; a grant is not.** The marker is an ordinary
21
21
  * K-21 tuple: switching back on tombstones it, so the row stays as evidence of when the
22
- * switch was last pulled. The permission walk reads only `role:` and `granted:` relations
23
- * (`permission-eval.ts`), so the marker authorizes nothing and denies nothing by itself.
22
+ * switch was last pulled. The permission walk reads only `role:` and `granted:` relations,
23
+ * so the marker authorizes nothing; what it denies, it denies through the one question the
24
+ * evaluator asks before the walk (below).
24
25
  *
25
26
  * Switching off ALSO tombstones every live `granted:` tuple the module holds on the scope.
26
27
  * That is what makes it a kill switch rather than a schedule pause: anything that acts
27
28
  * with the module's system authority — a resumable job run (#1577), a `getSystemScope`
28
29
  * invoke — is denied by its own `ctx.check` while the switch is off.
29
30
  *
30
- * **And nothing may write a new one while it is off**, which is what makes that denial
31
- * hold. The checker is deliberately NOT marker-aware; the switch is closed on the WRITE
32
- * side instead, at the only two writers of a scope-level `system:` grant:
31
+ * **And the checker reads the marker too** (#1823), which is what makes that denial hold
32
+ * against a grant OFF cannot tombstone. A TENANT-level `system:` grant lives in the directory
33
+ * (and in every scope's projection of it), not at `scope:<id>`, so OFF leaves it live. The
34
+ * permission evaluator asks the scope reader's `switchedOff` before it reads any tuple, and a
35
+ * live marker denies the subject on that scope whatever it holds (`permission-eval.ts`). The
36
+ * write side stays closed as well, so a switched-off scope does not accumulate grants that
37
+ * ON would then hand back:
33
38
  * - `grantToSystem` refuses while the module is switched off on that scope (the adapters
34
- * ask `systemSwitchedOff` in the same unit as the write);
39
+ * ask `systemSwitchedOff` in the same unit as the write), and a tenant-level grant while
40
+ * the directory records it off on any scope (#1743);
35
41
  * - provisioning's seat (`seatScopeTuple`) seats nothing for a subject whose marker is
36
42
  * live, so a reconcile cannot create a grant a newer version declares.
37
43
  *
@@ -48,6 +54,10 @@ export const SYSTEM_SWITCH_OFF_RELATION = 'switch:off';
48
54
  /** `switched:<permission>` — "the switch revoked this grant, and ON gives it back". */
49
55
  const SWITCHED_PREFIX = 'switched:';
50
56
  const subjectOf = (moduleId) => `system:${moduleId}`;
57
+ /** The tuple-subject prefix of a peer vertical (#1706) — `vertical:acme/board-room`. Re-exported by `peer.ts`. */
58
+ export const PEER_SUBJECT_PREFIX = 'vertical:';
59
+ /** The tuple subject a peer's grants are seated under. The slug only, never the instance. Re-exported by `peer.ts`. */
60
+ export const peerSubjectRef = (vertical) => `${PEER_SUBJECT_PREFIX}${vertical}`;
51
61
  /**
52
62
  * "Is this subject's OFF marker live?", as a SQL predicate over ONE bound parameter (the
53
63
  * subject). The one spelling the gate, the grant refusal and the provisioning seat share,
@@ -101,11 +111,24 @@ export function systemGrantsStatus(db, now) {
101
111
  export function systemSwitchedOff(db, moduleId) {
102
112
  return subjectSwitchedOff(db, subjectOf(moduleId));
103
113
  }
114
+ /**
115
+ * `SYSTEM_SWITCH_OFF_PREDICATE` as a whole statement answering `off` (0/1), over the one bound
116
+ * subject — what `subjectSwitchedOff` runs, and what each adapter's permission reader prepares
117
+ * once and runs on every check of a switchable subject (#1823).
118
+ */
119
+ export const SYSTEM_SWITCH_OFF_QUERY = `SELECT ${SYSTEM_SWITCH_OFF_PREDICATE} AS off`;
104
120
  /** Is this subject's OFF marker live on the scope `db` is? Any subject — see `switchSubjectGrants`. */
105
121
  export function subjectSwitchedOff(db, subject) {
106
- const row = db.all(`SELECT ${SYSTEM_SWITCH_OFF_PREDICATE} AS off`, subject)[0];
122
+ const row = db.all(SYSTEM_SWITCH_OFF_QUERY, subject)[0];
107
123
  return Number(row?.off) === 1;
108
124
  }
125
+ /**
126
+ * The subject kinds a kill switch can name: a module's system authority (#1666, `system:`) and
127
+ * a peer vertical's (#1706, `vertical:`). A principal, connection or capability never carries a
128
+ * marker, so the evaluator asks the switch only for these (#1823).
129
+ */
130
+ const SWITCHABLE_KINDS = new Set(['system', 'vertical']);
131
+ export const isSwitchableSubjectKind = (kind) => SWITCHABLE_KINDS.has(kind);
109
132
  /** How many scopes a tenant-level refusal names before it says "and N more". */
110
133
  const NAMED_SCOPES = 5;
111
134
  const switchedOffMessage = (moduleId, where, reach) => `module '${moduleId}' is switched off on ${where} (#1666) — restore it first ` +
@@ -125,6 +148,25 @@ export function tenantSystemSwitchedOffMessage(moduleId, scopeIds) {
125
148
  const where = scopeIds.length === 1 ? `scope ${named}` : `scopes ${named}${more}`;
126
149
  return switchedOffMessage(moduleId, where, 'a tenant-level grant reaches every scope of the tenant, so ');
127
150
  }
151
+ /**
152
+ * #2045: the fence a scope keeps per switched subject — the operation id (a ULID) of the newest
153
+ * switch call it has applied. The directory's record (`system-switch-record.ts`) refuses a write
154
+ * older than the one it holds, and the scope refuses a move older than the one it applied, so the
155
+ * two always end on the SAME call's position, whatever order two concurrent calls' writes and moves
156
+ * arrive in. Without it, operator A recording OFF, then B recording ON and moving ON, then A's OFF
157
+ * landing, left the record ON beside a scope that is OFF, and a later restore re-admitted the
158
+ * subject. Keyed by (subject, object) as the marker is, so a copy of another scope's rows fences
159
+ * nothing here. Part of the scope's own storage: a restore or rewind takes it back with the marker.
160
+ */
161
+ export const SWITCH_FENCES_DDL = `
162
+ CREATE TABLE IF NOT EXISTS _substrat_switch_fences (
163
+ subject TEXT NOT NULL,
164
+ object TEXT NOT NULL,
165
+ -- The operation id of the newest switch call applied here. A ULID, so it orders by text.
166
+ fence TEXT NOT NULL,
167
+ PRIMARY KEY (subject, object)
168
+ );
169
+ `;
128
170
  /**
129
171
  * Move one module's switch on one scope. Idempotent: a repeat changes nothing and says so.
130
172
  *
@@ -134,38 +176,87 @@ export function tenantSystemSwitchedOffMessage(moduleId, scopeIds) {
134
176
  * marker. A repeated OFF re-asserts the whole position: a grant that became live meanwhile
135
177
  * (only a raw write can do that now) is tombstoned and recorded too.
136
178
  *
137
- * `held: false`, with nothing written, when the scope holds neither a grant nor a marker
138
- * for the module: the caller named something this scope never ran, and turning "nothing"
139
- * off must not write a marker that silently disables a module the day it is installed.
179
+ * `held: false`, with nothing written, when the module has no authority reaching the scope:
180
+ * no grant and no marker here, and no live TENANT-level grant (`tenantHeld`, which the caller
181
+ * reads from the directory, since the scope's storage cannot answer it). The caller named
182
+ * something this scope never ran, and turning "nothing" off must not write a marker that
183
+ * silently disables a module the day it is installed. A module whose only authority here is a
184
+ * tenant-level grant IS held (#1823): OFF writes the marker, which the evaluator then denies
185
+ * the module on, and tombstones nothing, because nothing of it is here to tombstone.
140
186
  *
141
187
  * Run it inside one transaction; the reads and writes here are meant to be one unit.
142
188
  */
143
189
  export function switchSystemSchedules(db, input) {
144
- return switchSubjectGrants(db, {
145
- subject: subjectOf(input.moduleId),
146
- scopeId: input.scopeId,
147
- to: input.to,
148
- at: input.at,
149
- });
190
+ const { moduleId, ...rest } = input;
191
+ const outcome = switchSubjectGrants(db, { subject: subjectOf(moduleId), ...rest });
192
+ return { ...outcome, deniesTenantGrants: true };
193
+ }
194
+ /** A wire carry as the unit runs it, or undefined when it names nothing to switch off. */
195
+ export function recordedOffFromWire(wire) {
196
+ if (!wire.switchedOff?.length && !wire.switchedOffPeers?.length)
197
+ return undefined;
198
+ return {
199
+ moduleIds: wire.switchedOff ?? [],
200
+ tenantHeld: wire.tenantHeld,
201
+ verticals: wire.switchedOffPeers,
202
+ tenantHeldVerticals: wire.tenantHeldPeers,
203
+ fences: wire.switchFences,
204
+ };
150
205
  }
151
206
  /**
152
- * Switch the directory's recorded-off modules off again, inside the unit that re-created the
207
+ * Switch the directory's recorded-off subjects off again, inside the unit that re-created the
153
208
  * scope's grants (#1742): a provision's or reconcile's seat, or a restore's replay. The caller
154
209
  * runs it in that same unit, AFTER the seat, so the grants the seat created are the ones OFF
155
210
  * tombstones and records, and a later restore gives back exactly those (#1674's order). No
156
- * schedule can run between the seat and this, because nothing can run between them.
211
+ * schedule can run, and no peer call be admitted, between the seat and this, because nothing
212
+ * can run between them.
157
213
  *
158
- * `scopeId` is the scope `db` is, and the caller passes the one the request provisions or
159
- * restores, never a second id: the list names modules, never scopes, so it can only reach
160
- * the scope that unit is already writing. A module the scope holds nothing for answers
161
- * `held: false` and writes nothing, as the switch always does. Off only: nothing here turns
162
- * a module on.
214
+ * `moduleIds` are the recorded-off modules, `verticals` the recorded-off peers (#2029). `scopeId`
215
+ * is the scope `db` is, and the caller passes the one the request provisions or restores, never
216
+ * a second id: the lists name subjects, never scopes, so they can only reach the scope that unit
217
+ * is already writing. A subject the scope holds nothing for answers `held: false` and writes
218
+ * nothing, as the switch always does — unless it is in `tenantHeld` / `tenantHeldVerticals`, the
219
+ * recorded-off subjects the caller found a live tenant-level grant for (#1823, #2030). Off only:
220
+ * nothing here turns anything on.
163
221
  */
164
222
  export function switchRecordedOff(db, input) {
165
- return [...new Set(input.moduleIds)].map((moduleId) => ({
223
+ const tenantHeld = new Set(input.tenantHeld ?? []);
224
+ const fenceOf = (subject) => input.fences?.[subject];
225
+ const modules = [...new Set(input.moduleIds)].map((moduleId) => ({
166
226
  moduleId,
167
- ...switchSystemSchedules(db, { moduleId, scopeId: input.scopeId, to: 'off', at: input.at }),
227
+ ...switchSystemSchedules(db, {
228
+ moduleId,
229
+ scopeId: input.scopeId,
230
+ to: 'off',
231
+ at: input.at,
232
+ tenantHeld: tenantHeld.has(moduleId),
233
+ fence: fenceOf(subjectOf(moduleId)),
234
+ }),
235
+ }));
236
+ const peersHeld = new Set(input.tenantHeldVerticals ?? []);
237
+ const peers = [...new Set(input.verticals ?? [])].map((vertical) => ({
238
+ vertical,
239
+ ...moveSwitch(db, 'peer', {
240
+ key: vertical,
241
+ scopeId: input.scopeId,
242
+ to: 'off',
243
+ at: input.at,
244
+ tenantHeld: peersHeld.has(vertical),
245
+ fence: fenceOf(peerSubjectRef(vertical)),
246
+ }),
168
247
  }));
248
+ return [...modules, ...peers];
249
+ }
250
+ /**
251
+ * Move one kill switch of either kind on one scope (#2029): `switchSystemSchedules` for a
252
+ * module, `switchSubjectGrants` over `vertical:<key>` for a peer. `key` is the module id or the
253
+ * peer's slug. The one mover the adapters' shared switch body and their re-asserts call.
254
+ */
255
+ export function moveSwitch(db, kind, input) {
256
+ const { key, ...rest } = input;
257
+ return kind === 'system'
258
+ ? switchSystemSchedules(db, { moduleId: key, ...rest })
259
+ : switchSubjectGrants(db, { subject: peerSubjectRef(key), ...rest });
169
260
  }
170
261
  /**
171
262
  * The switch itself, for ANY non-person subject's scope-level grants — `switchSystemSchedules`
@@ -177,13 +268,30 @@ export function switchRecordedOff(db, input) {
177
268
  * for exactly the subject it names.
178
269
  */
179
270
  export function switchSubjectGrants(db, input) {
180
- const subject = input.subject;
181
271
  const object = `scope:${input.scopeId}`;
272
+ const stored = input.fence === undefined
273
+ ? undefined
274
+ : db.all(`SELECT fence FROM _substrat_switch_fences WHERE subject = ? AND object = ?`, input.subject, object)[0];
275
+ if (input.fence !== undefined && stored !== undefined && stored.fence > input.fence) {
276
+ return { held: true, changed: false, permissions: [], superseded: true, fenced: true };
277
+ }
278
+ const outcome = applySwitch(db, input, object);
279
+ if (input.fence !== undefined)
280
+ outcome.fenced = true;
281
+ if (input.fence !== undefined && outcome.held) {
282
+ db.run(`INSERT INTO _substrat_switch_fences (subject, object, fence) VALUES (?, ?, ?)
283
+ ON CONFLICT (subject, object) DO UPDATE SET fence = excluded.fence`, input.subject, object, input.fence);
284
+ }
285
+ return outcome;
286
+ }
287
+ /** `switchSubjectGrants`' move itself, once the fence has let it through. */
288
+ function applySwitch(db, input, object) {
289
+ const subject = input.subject;
182
290
  const grants = db.all(`SELECT relation, revoked_at FROM _substrat_tuples
183
291
  WHERE subject = ? AND object = ? AND substr(relation, 1, 8) = 'granted:'
184
292
  ORDER BY relation`, subject, object);
185
293
  const marker = db.all(`SELECT revoked_at FROM _substrat_tuples WHERE subject = ? AND relation = ? AND object = ?`, subject, SYSTEM_SWITCH_OFF_RELATION, object)[0];
186
- if (grants.length === 0 && !marker)
294
+ if (grants.length === 0 && !marker && !input.tenantHeld)
187
295
  return { held: false, changed: false, permissions: [] };
188
296
  const permissionOf = (relation) => relation.slice('granted:'.length);
189
297
  if (input.to === 'off') {
@@ -1 +1 @@
1
- {"version":3,"file":"system-switch.js","sourceRoot":"","sources":["../src/system-switch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"system-switch.js","sourceRoot":"","sources":["../src/system-switch.ts"],"names":[],"mappings":"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"}
@@ -240,6 +240,29 @@ export declare function readInvocation(ctx: TimelineReader, invocationId: string
240
240
  * Same permission posture as every read here: the caller checks, this does not.
241
241
  */
242
242
  export declare function readDeadLetters(ctx: TimelineReader, page?: Pick<ListPage, 'limit' | 'cursor'>): Page<DeadLetter>;
243
+ /** What one executor did with one event, as `readExecutorDelivery` reads it (#1184). */
244
+ export type ExecutorDeliveryState = 'unattempted' | 'retrying' | 'delivered' | 'refused' | 'dead-lettered';
245
+ export interface ExecutorDelivery {
246
+ eventId: string;
247
+ state: ExecutorDeliveryState;
248
+ /** A refusal's reason, or the last failure. Null when nothing has failed. */
249
+ error: string | null;
250
+ }
251
+ /**
252
+ * The newest `eventType` event about `entity`, and what executor `executorId` did with it
253
+ * (#1184) — or null when there is no such event. What a module reads to show the effect it
254
+ * asked for as it actually stands: still pending, refused and why, or done.
255
+ *
256
+ * The journal's own predicates, in one place: no delivery row is `unattempted`; a next
257
+ * attempt is `retrying`; no error is `delivered`; an error with no next attempt is `refused`
258
+ * when the handler refused it (`REFUSAL_JOURNAL_PREFIX`) and `dead-lettered` when retries ran
259
+ * out. Same permission posture as every read here: the caller checks, this does not.
260
+ */
261
+ export declare function readExecutorDelivery(ctx: TimelineReader, input: {
262
+ executorId: string;
263
+ eventType: string;
264
+ entity: EntityRef;
265
+ }): ExecutorDelivery | null;
243
266
  /**
244
267
  * Facet a scope's own outbox (#1239 stage 1): narrow by type and window, group by
245
268
  * one envelope column or one payload field, count.
@@ -1 +1 @@
1
- {"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../src/timeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,SAAS,EACd,KAAK,OAAO,EAEZ,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,WAAW,EAEhB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EAMf,KAAK,QAAQ,EACb,KAAK,IAAI,EACT,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,SAAS,EAAY,MAAM,iBAAiB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAEH,oFAAoF;AACpF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;CACxC;AAmID;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,SAAS,EACjB,IAAI,CAAC,EAAE,QAAQ,GACd,IAAI,CAAC,aAAa,CAAC,CAKrB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,SAAS,EACjB,IAAI,CAAC,EAAE,QAAQ,GACd,IAAI,CAAC,YAAY,CAAC,CAKpB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAc,SAAQ,QAAQ;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA4ED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,cAAc,EACnB,IAAI,CAAC,EAAE,aAAa,GACnB,IAAI,CAAC,kBAAkB,CAAC,CAS1B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,cAAc,EACnB,IAAI,CAAC,EAAE,aAAa,GACnB,IAAI,CAAC,iBAAiB,CAAC,CAazB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,OAAO,EAChB,QAAQ,SAAK,GACZ,UAAU,CAiEZ;AAkDD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,OAAO,EAChB,QAAQ,SAAK,GACZ,WAAW,CAoFb;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,cAAc,EACnB,YAAY,EAAE,MAAM,EACpB,KAAK,SAAM,GACV,gBAAgB,CAOlB;AA+DD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,QAAQ,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,CAwChH;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,eAAe,GAAG,gBAAgB,CA6EzF"}
1
+ {"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../src/timeline.ts"],"names":[],"mappings":"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"}
package/dist/timeline.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { deadLetter, entityRef, eventDelivery, historyEntry, listLimitOf, pageOf, timelineEntry, } from '@substrat-run/contracts';
2
2
  import { rowDecoder, UNDECODED_ACTOR } from './row-decode.js';
3
+ import { REFUSAL_JOURNAL_PREFIX } from './delivery-refusal.js';
3
4
  /** The envelope columns, in the order `mapTimelineRow` expects. */
4
5
  const TIMELINE_COLUMNS = 'id, type, occurred_at, actor';
5
6
  /** …plus what a history VIEW needs. See `historyEntry` for why two are nullable. */
@@ -600,6 +601,35 @@ export function readDeadLetters(ctx, page) {
600
601
  const entries = rows.map(deadLetterOf);
601
602
  return pageOf(entries, limit, (e) => `${e.eventId}${DEAD_LETTER_CURSOR_SEPARATOR}${e.consumer}`);
602
603
  }
604
+ /**
605
+ * The newest `eventType` event about `entity`, and what executor `executorId` did with it
606
+ * (#1184) — or null when there is no such event. What a module reads to show the effect it
607
+ * asked for as it actually stands: still pending, refused and why, or done.
608
+ *
609
+ * The journal's own predicates, in one place: no delivery row is `unattempted`; a next
610
+ * attempt is `retrying`; no error is `delivered`; an error with no next attempt is `refused`
611
+ * when the handler refused it (`REFUSAL_JOURNAL_PREFIX`) and `dead-lettered` when retries ran
612
+ * out. Same permission posture as every read here: the caller checks, this does not.
613
+ */
614
+ export function readExecutorDelivery(ctx, input) {
615
+ const row = ctx.sql.query(`SELECT o.id, d.event_id AS journaled, d.error, d.next_attempt_at
616
+ FROM _substrat_outbox o
617
+ LEFT JOIN _substrat_deliveries d ON d.event_id = o.id AND d.consumer_module = ?
618
+ WHERE o.entity_type = ? AND o.entity_id = ? AND o.type = ?
619
+ ORDER BY o.id DESC
620
+ LIMIT 1`, [`executor:${input.executorId}`, input.entity.entityType, input.entity.entityId, input.eventType])[0];
621
+ if (!row)
622
+ return null;
623
+ if (row.journaled === null)
624
+ return { eventId: row.id, state: 'unattempted', error: null };
625
+ if (row.next_attempt_at !== null)
626
+ return { eventId: row.id, state: 'retrying', error: row.error };
627
+ if (row.error === null)
628
+ return { eventId: row.id, state: 'delivered', error: null };
629
+ return row.error.startsWith(REFUSAL_JOURNAL_PREFIX)
630
+ ? { eventId: row.id, state: 'refused', error: row.error.slice(REFUSAL_JOURNAL_PREFIX.length) }
631
+ : { eventId: row.id, state: 'dead-lettered', error: row.error };
632
+ }
603
633
  /**
604
634
  * Facet a scope's own outbox (#1239 stage 1): narrow by type and window, group by
605
635
  * one envelope column or one payload field, count.