@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.
- package/dist/capability.d.ts +2 -0
- package/dist/capability.d.ts.map +1 -1
- package/dist/capability.js +2 -0
- package/dist/capability.js.map +1 -1
- package/dist/delivery-refusal.d.ts +21 -0
- package/dist/delivery-refusal.d.ts.map +1 -0
- package/dist/delivery-refusal.js +21 -0
- package/dist/delivery-refusal.js.map +1 -0
- package/dist/index.d.ts +23 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -12
- package/dist/index.js.map +1 -1
- package/dist/invocation-log.d.ts +25 -1
- package/dist/invocation-log.d.ts.map +1 -1
- package/dist/invocation-log.js +45 -6
- package/dist/invocation-log.js.map +1 -1
- package/dist/job-run.d.ts +370 -47
- package/dist/job-run.d.ts.map +1 -1
- package/dist/job-run.js +472 -81
- package/dist/job-run.js.map +1 -1
- package/dist/lifecycle-flow.d.ts.map +1 -1
- package/dist/lifecycle-flow.js +2 -1
- package/dist/lifecycle-flow.js.map +1 -1
- package/dist/membership-executor.d.ts +97 -0
- package/dist/membership-executor.d.ts.map +1 -0
- package/dist/membership-executor.js +246 -0
- package/dist/membership-executor.js.map +1 -0
- package/dist/membership-fence.d.ts +57 -0
- package/dist/membership-fence.d.ts.map +1 -0
- package/dist/membership-fence.js +90 -0
- package/dist/membership-fence.js.map +1 -0
- package/dist/operation-series.d.ts +58 -0
- package/dist/operation-series.d.ts.map +1 -0
- package/dist/operation-series.js +63 -0
- package/dist/operation-series.js.map +1 -0
- package/dist/peer.d.ts +13 -8
- package/dist/peer.d.ts.map +1 -1
- package/dist/peer.js +13 -14
- package/dist/peer.js.map +1 -1
- package/dist/permission-eval.d.ts +48 -1
- package/dist/permission-eval.d.ts.map +1 -1
- package/dist/permission-eval.js +137 -18
- package/dist/permission-eval.js.map +1 -1
- package/dist/read-only-sql.d.ts +2 -26
- package/dist/read-only-sql.d.ts.map +1 -1
- package/dist/read-only-sql.js +13 -9
- package/dist/read-only-sql.js.map +1 -1
- package/dist/refusal-query.d.ts +4 -1
- package/dist/refusal-query.d.ts.map +1 -1
- package/dist/refusal-query.js +6 -4
- package/dist/refusal-query.js.map +1 -1
- package/dist/refusals.d.ts +80 -8
- package/dist/refusals.d.ts.map +1 -1
- package/dist/refusals.js +141 -22
- package/dist/refusals.js.map +1 -1
- package/dist/scope-copy.d.ts +18 -0
- package/dist/scope-copy.d.ts.map +1 -1
- package/dist/scope-copy.js +18 -0
- package/dist/scope-copy.js.map +1 -1
- package/dist/scope-host.d.ts +266 -15
- package/dist/scope-host.d.ts.map +1 -1
- package/dist/scope-host.js +41 -0
- package/dist/scope-host.js.map +1 -1
- package/dist/spine-restore.d.ts +7 -2
- package/dist/spine-restore.d.ts.map +1 -1
- package/dist/spine-restore.js +13 -5
- package/dist/spine-restore.js.map +1 -1
- package/dist/system-switch-record.d.ts +290 -74
- package/dist/system-switch-record.d.ts.map +1 -1
- package/dist/system-switch-record.js +353 -101
- package/dist/system-switch-record.js.map +1 -1
- package/dist/system-switch.d.ts +146 -20
- package/dist/system-switch.d.ts.map +1 -1
- package/dist/system-switch.js +135 -27
- package/dist/system-switch.js.map +1 -1
- package/dist/timeline.d.ts +23 -0
- package/dist/timeline.d.ts.map +1 -1
- package/dist/timeline.js +30 -0
- package/dist/timeline.js.map +1 -1
- package/dist/ulid.d.ts +5 -0
- package/dist/ulid.d.ts.map +1 -1
- package/dist/ulid.js +9 -0
- package/dist/ulid.js.map +1 -1
- package/dist/vertical-events.d.ts +7 -1
- package/dist/vertical-events.d.ts.map +1 -1
- package/dist/vertical-events.js +14 -0
- package/dist/vertical-events.js.map +1 -1
- package/package.json +4 -4
package/dist/system-switch.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
|
31
|
-
*
|
|
32
|
-
*
|
|
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
|
|
125
|
-
*
|
|
126
|
-
*
|
|
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
|
-
/**
|
|
137
|
-
|
|
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
|
-
*
|
|
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
|
|
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
|
-
* `
|
|
148
|
-
*
|
|
149
|
-
* the
|
|
150
|
-
*
|
|
151
|
-
*
|
|
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":"AAAA
|
|
1
|
+
{"version":3,"file":"system-switch.d.ts","sourceRoot":"","sources":["../src/system-switch.ts"],"names":[],"mappings":"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"}
|
package/dist/system-switch.js
CHANGED
|
@@ -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
|
-
*
|
|
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
|
|
31
|
-
*
|
|
32
|
-
*
|
|
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(
|
|
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
|
|
138
|
-
*
|
|
139
|
-
*
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
|
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
|
|
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
|
-
* `
|
|
159
|
-
*
|
|
160
|
-
* the
|
|
161
|
-
*
|
|
162
|
-
*
|
|
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
|
-
|
|
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, {
|
|
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":"AAAA
|
|
1
|
+
{"version":3,"file":"system-switch.js","sourceRoot":"","sources":["../src/system-switch.ts"],"names":[],"mappings":"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"}
|
package/dist/timeline.d.ts
CHANGED
|
@@ -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.
|
package/dist/timeline.d.ts.map
CHANGED
|
@@ -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;
|
|
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.
|