@substrat-run/kernel 0.134.0 → 0.136.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/async-invocation-log.d.ts +61 -0
- package/dist/async-invocation-log.d.ts.map +1 -0
- package/dist/async-invocation-log.js +149 -0
- package/dist/async-invocation-log.js.map +1 -0
- package/dist/attachment-extractor.d.ts +10 -2
- package/dist/attachment-extractor.d.ts.map +1 -1
- package/dist/attachment-extractor.js +16 -3
- package/dist/attachment-extractor.js.map +1 -1
- package/dist/base64url.d.ts +6 -0
- package/dist/base64url.d.ts.map +1 -0
- package/dist/base64url.js +19 -0
- package/dist/base64url.js.map +1 -0
- package/dist/capability.d.ts +36 -1
- package/dist/capability.d.ts.map +1 -1
- package/dist/capability.js +67 -24
- package/dist/capability.js.map +1 -1
- package/dist/carried-copy.d.ts +117 -0
- package/dist/carried-copy.d.ts.map +1 -0
- package/dist/carried-copy.js +134 -0
- package/dist/carried-copy.js.map +1 -0
- package/dist/index.d.ts +98 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +65 -12
- package/dist/index.js.map +1 -1
- package/dist/invocation-log.d.ts +90 -8
- package/dist/invocation-log.d.ts.map +1 -1
- package/dist/invocation-log.js +34 -10
- package/dist/invocation-log.js.map +1 -1
- package/dist/lifecycle-flow.d.ts +6 -1
- package/dist/lifecycle-flow.d.ts.map +1 -1
- package/dist/lifecycle-flow.js +1 -1
- package/dist/lifecycle-flow.js.map +1 -1
- package/dist/list-index.d.ts +19 -4
- package/dist/list-index.d.ts.map +1 -1
- package/dist/list-index.js +150 -8
- package/dist/list-index.js.map +1 -1
- package/dist/platform-call.d.ts +4 -37
- package/dist/platform-call.d.ts.map +1 -1
- package/dist/platform-call.js +5 -37
- package/dist/platform-call.js.map +1 -1
- package/dist/platform-sweep.d.ts +17 -0
- package/dist/platform-sweep.d.ts.map +1 -1
- package/dist/platform-sweep.js +22 -4
- package/dist/platform-sweep.js.map +1 -1
- package/dist/provider-error.d.ts.map +1 -1
- package/dist/provider-error.js +2 -1
- package/dist/provider-error.js.map +1 -1
- package/dist/refusal-query.d.ts +42 -0
- package/dist/refusal-query.d.ts.map +1 -0
- package/dist/refusal-query.js +79 -0
- package/dist/refusal-query.js.map +1 -0
- package/dist/routed-node.d.ts +3 -2
- package/dist/routed-node.d.ts.map +1 -1
- package/dist/routed-node.js.map +1 -1
- package/dist/scope-copy.d.ts +122 -0
- package/dist/scope-copy.d.ts.map +1 -0
- package/dist/scope-copy.js +187 -0
- package/dist/scope-copy.js.map +1 -0
- package/dist/scope-host.d.ts +142 -21
- package/dist/scope-host.d.ts.map +1 -1
- package/dist/scope-host.js +50 -14
- package/dist/scope-host.js.map +1 -1
- package/dist/scope-lifecycle.d.ts +70 -0
- package/dist/scope-lifecycle.d.ts.map +1 -0
- package/dist/scope-lifecycle.js +124 -0
- package/dist/scope-lifecycle.js.map +1 -0
- package/dist/timeline.d.ts +65 -1
- package/dist/timeline.d.ts.map +1 -1
- package/dist/timeline.js +136 -1
- package/dist/timeline.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { CAPABILITY_TABLE_NAMES } from './capability.js';
|
|
2
|
+
/**
|
|
3
|
+
* What a dump leaves behind when it lands in a scope other than the one it was captured from
|
|
4
|
+
* (#1686). One definition for both adapters' loaders, so the DO and the pure host agree.
|
|
5
|
+
*
|
|
6
|
+
* A load is a **copy** when the destination is not the source: a fork, a snapshot, a preview, or
|
|
7
|
+
* one scope's backup restored onto another. It is a **return** when the dump goes back into the
|
|
8
|
+
* scope it came from: a backup restore, a carry onto a new version, adopt, rebind. A copy runs as
|
|
9
|
+
* a scope of its own, so whatever in the dump is bound to being the source (a live link's secret,
|
|
10
|
+
* a side effect the source asked for) must not work in the copy. A return keeps all of it, because
|
|
11
|
+
* restoring a backup must not end live links or lose requests that were waiting.
|
|
12
|
+
*
|
|
13
|
+
* An unknown source (`sourceScopeId` or `destScopeId` absent) counts as a copy: a load that cannot
|
|
14
|
+
* say where the dump came from cannot show it is a return. Only a control plane that predates the
|
|
15
|
+
* field sends a restore with no source.
|
|
16
|
+
*/
|
|
17
|
+
export const isCopyLoad = (destScopeId, sourceScopeId) => destScopeId === undefined || sourceScopeId !== destScopeId;
|
|
18
|
+
/** The capability tables, as `capabilitiesForLoad` matches a dump's (lowercased) names. */
|
|
19
|
+
const CAPABILITY_TABLES = new Set(CAPABILITY_TABLE_NAMES);
|
|
20
|
+
/**
|
|
21
|
+
* A dump's tables as a load into `destScopeId` takes them: **capability rows never cross a scope
|
|
22
|
+
* id.** A copy loads both capability tables empty, so a live link share opens the scope it was
|
|
23
|
+
* minted in and never a copy of it; a return keeps them.
|
|
24
|
+
*
|
|
25
|
+
* Dropped, not loaded as revoked: the copy never minted those links, so a revocation recorded
|
|
26
|
+
* there would be evidence of something that did not happen in it. Names are matched without
|
|
27
|
+
* case, as SQLite resolves a table name.
|
|
28
|
+
*/
|
|
29
|
+
export function capabilitiesForLoad(tables, destScopeId, sourceScopeId) {
|
|
30
|
+
if (!isCopyLoad(destScopeId, sourceScopeId))
|
|
31
|
+
return tables;
|
|
32
|
+
return tables.map((t) => (CAPABILITY_TABLES.has(t.name.toLowerCase()) ? { ...t, rows: [] } : t));
|
|
33
|
+
}
|
|
34
|
+
/** The reason copied work is settled with. Names the source, so the journal says where it runs. */
|
|
35
|
+
const notCarried = (sourceScopeId) => `not carried: copied from ${sourceScopeId ? `scope ${sourceScopeId}` : 'another scope'} before it ran; ` +
|
|
36
|
+
'it runs in the scope that asked for it, never in a copy';
|
|
37
|
+
/** A copied intent's failure: the platform's own refusal, never the provider's answer. */
|
|
38
|
+
const NOT_CARRIED_FAILURE = JSON.stringify({
|
|
39
|
+
origin: 'platform',
|
|
40
|
+
code: 'precondition_failed',
|
|
41
|
+
permission: null,
|
|
42
|
+
});
|
|
43
|
+
/**
|
|
44
|
+
* Where a scope's data came from, and whether it is a copy: at most one row, which holds two
|
|
45
|
+
* facts that are set and corrected apart (#2009).
|
|
46
|
+
*
|
|
47
|
+
* - **`events_through`, the copied-events mark.** The highest event id a load brought in from
|
|
48
|
+
* another scope, or `''` when it brought none. Every event at or below it was emitted in another
|
|
49
|
+
* scope; every event the scope emits itself sorts above it, because a loader re-seeds the
|
|
50
|
+
* scope's event-id floor from `MAX(id)` (#1335) once the rows are in. Written by a load into a
|
|
51
|
+
* scope id other than the dump's (`settleCopiedWork`), and by nothing else.
|
|
52
|
+
* - **`is_copy`, the classification.** Whether the scope is a copy (a fork, a snapshot, a
|
|
53
|
+
* preview), which a host with no control-plane directory reads for primacy and holds inert
|
|
54
|
+
* (#2005). Written on the directory's word only (`markCopyOrigin`), never inferred from a load:
|
|
55
|
+
* one install's backup restored onto another install is a cross-scope load, so it carries the
|
|
56
|
+
* source's events mark, yet the directory still says primary, and its own effects must run.
|
|
57
|
+
* Cleared by staff's correction (`clearCopyMarker`) without touching the events mark.
|
|
58
|
+
*
|
|
59
|
+
* A scope that was never loaded from another scope and never marked holds no row. A row written
|
|
60
|
+
* before the column existed holds `is_copy` NULL, and NULL reads as a copy: such a row meant both
|
|
61
|
+
* facts at once, so every legacy store keeps exactly the behaviour it had (`IS_COPY_SQL`). The
|
|
62
|
+
* column is nullable with no DEFAULT for the reason every additive spine column is (#1883,
|
|
63
|
+
* `lint:spine-ddl`), so its "default" lives in that read. Kernel-owned and shared by both
|
|
64
|
+
* adapters' `KERNEL_DDL`, so the two cannot part company.
|
|
65
|
+
*/
|
|
66
|
+
export const COPY_ORIGIN_DDL = `
|
|
67
|
+
CREATE TABLE IF NOT EXISTS _substrat_copy_origin (
|
|
68
|
+
-- Always 1: one origin per scope. No CHECK, because lint:spine-ddl compares none.
|
|
69
|
+
id INTEGER PRIMARY KEY,
|
|
70
|
+
source_scope_id TEXT,
|
|
71
|
+
events_through TEXT NOT NULL,
|
|
72
|
+
copied_at TEXT NOT NULL,
|
|
73
|
+
-- #2009: 1 a copy, 0 not one (a load's events mark only, or a cleared marker), NULL a row
|
|
74
|
+
-- from before the column, which read as a copy and still does.
|
|
75
|
+
is_copy INTEGER
|
|
76
|
+
);
|
|
77
|
+
`;
|
|
78
|
+
/**
|
|
79
|
+
* The predicate every read that turns an outbox row into WORK carries (consumer dispatch, executor
|
|
80
|
+
* dispatch, the Tier-2 drain): the event was emitted in this scope, not copied into it. `alias` is
|
|
81
|
+
* the outbox's alias in the caller's query (`'o.'`), or empty. A scope that was never a copy has no
|
|
82
|
+
* key, and every id compares above the empty string.
|
|
83
|
+
*/
|
|
84
|
+
export const emittedHere = (alias = '') => `${alias}id > COALESCE((SELECT events_through FROM _substrat_copy_origin WHERE id = 1), '')`;
|
|
85
|
+
/**
|
|
86
|
+
* On a copy, nothing that originated in the source produces an effect in the destination. Run
|
|
87
|
+
* inside the load's transaction, after the rows are in and BEFORE the loader queues any work of
|
|
88
|
+
* its own (the attachment-text re-extraction is a job run), or that work is settled with the
|
|
89
|
+
* source's. The history stays: every row is still
|
|
90
|
+
* there and reads as what happened at the source; only its power to cause something here goes.
|
|
91
|
+
*
|
|
92
|
+
* The platform's drain walks every active scope, and a fork, a snapshot or a preview is one, so
|
|
93
|
+
* each of these would otherwise run a second time from the copy (an email sent twice, a connector
|
|
94
|
+
* delivery repeated, a usage line billed twice):
|
|
95
|
+
*
|
|
96
|
+
* - **Pending intents** settle `failed`, "not carried", attributed to the platform.
|
|
97
|
+
* - **Executor retries** (`_substrat_deliveries` rows with a `next_attempt_at`) become terminal,
|
|
98
|
+
* with "not carried" as their error, as a dead letter reads.
|
|
99
|
+
* - **Running job runs** settle `failed`, "not carried"; their step ledger stays as evidence.
|
|
100
|
+
* - **Events no consumer or executor has reached yet** cannot be settled row by row: an executor is
|
|
101
|
+
* registered on the coordinator, not in the scope, so the loader cannot know who still owes one.
|
|
102
|
+
* Instead the copy records the highest id it brought in (`_substrat_copy_origin`), and every
|
|
103
|
+
* read that dispatches work carries `emittedHere()`. That also keeps the copy from shipping the
|
|
104
|
+
* source's events to Tier 2 a second time.
|
|
105
|
+
*
|
|
106
|
+
* A return leaves all of it as it was, because the scope that asked for the work is the one it is
|
|
107
|
+
* back in. Settled rather than dropped: a journal row saying "not carried" explains why the event
|
|
108
|
+
* that raised it caused nothing here, where a missing row would leave a gap.
|
|
109
|
+
*/
|
|
110
|
+
export function settleCopiedWork(sql, destScopeId, sourceScopeId, now) {
|
|
111
|
+
if (!isCopyLoad(destScopeId, sourceScopeId))
|
|
112
|
+
return;
|
|
113
|
+
const reason = notCarried(sourceScopeId);
|
|
114
|
+
sql.run(`UPDATE _substrat_platform_requests
|
|
115
|
+
SET status = 'failed', last_error = ?, last_failure = ?, settled_at = ?
|
|
116
|
+
WHERE status = 'pending'`, reason, NOT_CARRIED_FAILURE, now);
|
|
117
|
+
sql.run(`UPDATE _substrat_deliveries SET next_attempt_at = NULL, error = ?, delivered_at = ?
|
|
118
|
+
WHERE next_attempt_at IS NOT NULL`, reason, now);
|
|
119
|
+
sql.run(`UPDATE _substrat_job_runs
|
|
120
|
+
SET status = 'failed', last_error = ?, next_attempt_at = NULL, updated_at = ?, ended_at = ?
|
|
121
|
+
WHERE status = 'running'`, reason, now, now);
|
|
122
|
+
const highest = sql.all('SELECT MAX(id) AS id FROM _substrat_outbox')[0]?.id;
|
|
123
|
+
// Written for an empty copy too: `''` is below every id, so `emittedHere()` passes all of the
|
|
124
|
+
// scope's own events exactly as no row would. An origin the dump already carried with the same
|
|
125
|
+
// source and mark is kept as it was, its `copied_at` included, so re-loading the same export
|
|
126
|
+
// changes nothing in it.
|
|
127
|
+
//
|
|
128
|
+
// `is_copy` is 0 (#2009): the load moves the events mark and does not classify. Whatever the
|
|
129
|
+
// dump's row said described the scope it came FROM; whether THIS scope is a copy is the
|
|
130
|
+
// directory's to say, through `markCopyOrigin`, which the loader runs after this when the
|
|
131
|
+
// directory says so.
|
|
132
|
+
sql.run(`INSERT INTO _substrat_copy_origin (id, source_scope_id, events_through, copied_at, is_copy) VALUES (1, ?, ?, ?, 0)
|
|
133
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
134
|
+
source_scope_id = excluded.source_scope_id,
|
|
135
|
+
events_through = excluded.events_through,
|
|
136
|
+
copied_at = excluded.copied_at,
|
|
137
|
+
is_copy = excluded.is_copy
|
|
138
|
+
WHERE _substrat_copy_origin.source_scope_id IS NOT excluded.source_scope_id
|
|
139
|
+
OR _substrat_copy_origin.events_through IS NOT excluded.events_through
|
|
140
|
+
OR _substrat_copy_origin.is_copy IS NOT excluded.is_copy`, sourceScopeId ?? null, typeof highest === 'string' ? highest : '', now);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Mark this scope a copy (#2005), on the directory's word that it is not primary: a load the
|
|
144
|
+
* platform flags, the `mark-copies` repair, a reactivation, a carry's wipe or release. Sets the
|
|
145
|
+
* classification and nothing else (#2009): an origin row a load wrote keeps its events mark and
|
|
146
|
+
* source, and a scope with no row gets one whose events mark is `''`, which passes every event
|
|
147
|
+
* exactly as no row does, so dispatch is unchanged. Idempotent: answers whether this call changed
|
|
148
|
+
* anything, and a store that already reads as a copy (a legacy row included) is left as it is.
|
|
149
|
+
*/
|
|
150
|
+
export function markCopyOrigin(sql, now) {
|
|
151
|
+
if (sql.all(IS_COPY_SQL).length > 0)
|
|
152
|
+
return false;
|
|
153
|
+
sql.run(MARK_COPY_ORIGIN_SQL, now);
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* The one statement that marks a store a copy (#2005, #2009): the origin row with no events mark
|
|
158
|
+
* where none exists, or the classification set on the row that does. It only ever makes a store
|
|
159
|
+
* MORE restricted — a copy's executors are held inert — and changes no data and no events mark,
|
|
160
|
+
* which is why the scope DO lets this statement, and no other, through without advancing the
|
|
161
|
+
* write revision a carry fences on (#1722, `isCopyMarkInsert`). Clearing a marker is the opposite
|
|
162
|
+
* and is a write like any other.
|
|
163
|
+
*/
|
|
164
|
+
export const MARK_COPY_ORIGIN_SQL = "INSERT INTO _substrat_copy_origin (id, source_scope_id, events_through, copied_at, is_copy) VALUES (1, NULL, '', ?, 1) " +
|
|
165
|
+
'ON CONFLICT (id) DO UPDATE SET is_copy = 1';
|
|
166
|
+
/**
|
|
167
|
+
* Correct a MISTAKEN copy classification (#2005, #2009): for a scope the directory says is
|
|
168
|
+
* primary, marked by a misclassification, a race, an operator, or a row from before #2009 that
|
|
169
|
+
* a cross-scope load wrote (one install's backup restored onto another). Clears the
|
|
170
|
+
* classification only: the events mark stays, so the source's queued work is still never run
|
|
171
|
+
* here (`emittedHere()`), and the scope's own effects run again. `absent` when the store does not
|
|
172
|
+
* read as a copy.
|
|
173
|
+
*/
|
|
174
|
+
export function clearCopyMarker(sql) {
|
|
175
|
+
if (sql.all(IS_COPY_SQL).length === 0)
|
|
176
|
+
return 'absent';
|
|
177
|
+
sql.run('UPDATE _substrat_copy_origin SET is_copy = 0 WHERE id = 1');
|
|
178
|
+
return 'cleared';
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Whether this scope is a copy (#2005, #2009): the classification on its copy-origin row, with
|
|
182
|
+
* a row from before the column (NULL) read as one. The one primacy fact a host with no
|
|
183
|
+
* control-plane directory can read from the scope's own storage — a CP-less hosted vertical's
|
|
184
|
+
* coordinator asks it before it runs an executor.
|
|
185
|
+
*/
|
|
186
|
+
export const IS_COPY_SQL = 'SELECT 1 AS copy FROM _substrat_copy_origin WHERE id = 1 AND COALESCE(is_copy, 1) = 1';
|
|
187
|
+
//# sourceMappingURL=scope-copy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-copy.js","sourceRoot":"","sources":["../src/scope-copy.ts"],"names":[],"mappings":"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"}
|
package/dist/scope-host.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ModuleLog } from './module-log.js';
|
|
2
|
-
import type { OnBehalfOf, ExportReadInput, ExportedBatch, ImportBatch, ImportedEvent, ImportResult, ImportState, ImportCursorMove, ImportCursorMoved, AdminAction, BecomeCapabilityInput, CapabilityExchange, CapabilityId, MintedCapability, ListPage, Connection, ConnectionFilter, ConnectionId, ConnectionGrant, ConnectionGrantRecord, ConnectionSecret, CreateConnectionInput, OpenConnection, ProjectedConnectionGrant, ProjectedConnectionKey, AccessLogEntry, DelegatedReadRecord, OwnerTransferAudit, BindHostnameInput, AdminLogEntry, OpsFailureEntry, PlatformRequestDrainTotalsEntry, SweepRunEntry, SweepRunKind, SweepRunOutcome, ModelUsageEntry, ModelUsageSummary, CapabilityGrant, CreateTenantInput, Decision, Instant, DomainEvent, DomainEventInput, PlatformRequestInput, PlatformRequestId, PlatformRequest, PlatformRequestFilter, PlatformRequestStatus, PlatformRequestFailure, EntitlementGrant, EntitlementGrantInput, EntitlementView, MeterReading, EntityRef, IdentityLink, IdentityPool, Jurisdiction, ModuleId, ModuleManifest, ScheduleSpec, SystemGrant, PeerGrantsStatusEntry, SystemGrantsStatusEntry, SystemSwitchRecord, SystemSwitch, SystemSwitchResult, PeerCoverage, PeerSwitch, PeerSwitchResult, VerticalCaller, VerticalResolution, CreateOrgInput, Node, Org, OrgId, OrgMembership, PermissionKey, PlatformActorId, ChannelName, ExportBreak, ChannelHistoryEntry, DnsRecord, HostnameBinding, HostnameStatus, PromotionAcknowledgement, BindAcknowledgement, PublishVersionInput, RegisterVerticalInput, VerticalServingState, RouteTarget, DirectoryDump, IdentityMembership, PrincipalId, ResolvedIdentity, RoleAssignment, RoleDefinition, QueryScopeInput, ReadScopeTableInput, Scope, ScopeDump, SubjectShredReceipt, ScopeQueryResult, ScopeId, ScopeStatus, ScopeTable, DenialFilter, DenialSummary, PermissionDenial, Coverage, BeginImpersonationInput, ImpersonationFilter, ImpersonationSession, ImpersonationSessionId, ScopeTablePage, StorageShape, Tenant, TenantId, TenantRole, TenantStoreHandle, AttachmentRecord, BlobStoreHandle, Visibility, Vertical, VerticalChannel, VerticalVersion, TenantStatus, Page, CountedPage, FreshnessSpec, EntityHistoryInput, EventFacetInput, EventCauseInput, EventEffectsInput, EffectsTree, InvocationEventsInput, InvocationEvents, DeadLettersInput, DeadLetter, LifecycleFlowInput, LifecycleFlowResult, CauseChain, EventFacetResult, HistoryEntry, ErrorCode, PlatformRequestFailureOrigin, IssueEntry, IssueStatus, IssueStatusInput, DeclaredMigration, CheckSubject } from '@substrat-run/contracts';
|
|
2
|
+
import type { OnBehalfOf, ExportReadInput, ExportedBatch, ImportBatch, ImportedEvent, ImportResult, ImportState, ImportCursorMove, ImportCursorMoved, AdminAction, BecomeCapabilityInput, CapabilityExchange, CapabilityFilter, CapabilityId, CapabilityPage, MintedCapability, ListPage, Connection, ConnectionFilter, ConnectionId, ConnectionGrant, ConnectionGrantRecord, ConnectionSecret, CreateConnectionInput, OpenConnection, ProjectedConnectionGrant, ProjectedConnectionKey, AccessLogEntry, DelegatedReadRecord, OwnerTransferAudit, CopyMarkAudit, BindHostnameInput, AdminLogEntry, OpsFailureEntry, PlatformRequestDrainTotalsEntry, SweepRunEntry, SweepRunKind, SweepRunOutcome, ModelUsageEntry, ModelUsageSummary, CapabilityGrant, CreateTenantInput, Decision, Instant, DomainEvent, DomainEventInput, PlatformRequestInput, PlatformRequestId, PlatformRequest, PlatformRequestFilter, PlatformRequestStatus, PlatformRequestFailure, EntitlementGrant, EntitlementGrantInput, EntitlementView, MeterReading, EntityRef, IdentityLink, IdentityPool, Jurisdiction, ModuleId, ModuleManifest, ScheduleSpec, SystemGrant, PeerGrantsStatusEntry, SystemGrantsStatusEntry, SystemSwitchRecord, SystemSwitch, SystemSwitchResult, PeerCoverage, PeerSwitch, PeerSwitchResult, VerticalCaller, VerticalResolution, CreateOrgInput, Node, Org, OrgId, OrgMembership, PermissionKey, PlatformActorId, ChannelName, ExportBreak, ChannelHistoryEntry, DnsRecord, HostnameBinding, HostnameStatus, PromotionAcknowledgement, BindAcknowledgement, PublishVersionInput, RegisterVerticalInput, VerticalServingState, RouteTarget, DirectoryDump, IdentityMembership, PrincipalId, ResolvedIdentity, RoleAssignment, RoleDefinition, QueryScopeInput, ReadScopeTableInput, Scope, ScopeDump, SubjectShredReceipt, ScopeQueryResult, ScopeId, ScopeStatus, ScopeTable, DenialFilter, DenialSummary, RefusalFilter, RefusalRecord, PermissionDenial, Coverage, BeginImpersonationInput, ImpersonationFilter, ImpersonationSession, ImpersonationSessionId, ScopeTablePage, StorageShape, Tenant, TenantId, TenantRole, TenantStoreHandle, AttachmentRecord, BlobStoreHandle, Visibility, Vertical, VerticalChannel, VerticalVersion, TenantStatus, Page, CountedPage, FreshnessSpec, EntityHistoryInput, EventFacetInput, EventCauseInput, EventEffectsInput, EffectsTree, InvocationEventsInput, InvocationEvents, DeadLettersInput, DeadLetter, LifecycleFlowInput, LifecycleFlowResult, CauseChain, EventFacetResult, HistoryEntry, ErrorCode, PlatformRequestFailureOrigin, IssueEntry, IssueStatus, IssueStatusInput, DeclaredMigration, CheckSubject } from '@substrat-run/contracts';
|
|
3
3
|
import type { ConnectionUseOutcome } from './connector-calls.js';
|
|
4
4
|
import type { CapabilityVerbs } from './capability.js';
|
|
5
5
|
import type { ModelUsageFilter, ModelUsageInput, ModelUsageWindow } from './model-usage.js';
|
|
@@ -264,6 +264,10 @@ export interface OperationContext {
|
|
|
264
264
|
* `SortNotDeclared` for a `?sort=` outside the vocabulary, and
|
|
265
265
|
* `FilterNotDeclared` for a filter outside it — never a silently-ignored
|
|
266
266
|
* parameter, which is how a caller comes to believe a filter is applied.
|
|
267
|
+
*
|
|
268
|
+
* `params.order` unset walks ascending; an operation's declared order arrives
|
|
269
|
+
* already applied by `operationInputsOf`. A cursor replayed under another sort
|
|
270
|
+
* or order throws `CursorMismatch` (`reason: 'cursor_restart'`) — #2001, K-44.
|
|
267
271
|
*/
|
|
268
272
|
page<T>(entityType: string, params: PageParams): Page<T> | CountedPage<T>;
|
|
269
273
|
/**
|
|
@@ -775,6 +779,19 @@ export interface ExecutorDrainReport {
|
|
|
775
779
|
* the response so the router kicks an immediate drain.
|
|
776
780
|
*/
|
|
777
781
|
routedToPlatform?: number;
|
|
782
|
+
/**
|
|
783
|
+
* Deliveries journaled as not executed because the scope is not primary — a fork, a
|
|
784
|
+
* snapshot or a preview, which cause no outbound effects (#2005, `INERT_SCOPE_REASON`).
|
|
785
|
+
* Terminal and counted apart from `deadLettered`: nothing failed, the platform declined.
|
|
786
|
+
*/
|
|
787
|
+
inert?: number;
|
|
788
|
+
/**
|
|
789
|
+
* The scope's lifecycle holds its work (#1713): a suspended or archived scope, or one under a
|
|
790
|
+
* suspended or deleting tenant, as a CP-less host reads it from the scope's own storage.
|
|
791
|
+
* Nothing was attempted and every due delivery is still due, so the first pass after the scope
|
|
792
|
+
* is live again delivers it. Absent otherwise.
|
|
793
|
+
*/
|
|
794
|
+
lifecycleHeld?: true;
|
|
778
795
|
}
|
|
779
796
|
/**
|
|
780
797
|
* A module's recurring-work declarations, as `registeredSchedules` reports them
|
|
@@ -832,6 +849,12 @@ export interface ScheduleRunReport {
|
|
|
832
849
|
* scope (#1819, Cloudflare only) reports the same.
|
|
833
850
|
*/
|
|
834
851
|
switchedOff?: true;
|
|
852
|
+
/**
|
|
853
|
+
* The scope's lifecycle holds its work (#1713, a CP-less host reading the scope's own
|
|
854
|
+
* storage): every schedule is `skipped` and no cadence row moved, so a schedule that came
|
|
855
|
+
* due meanwhile fires once on the first pass after the scope is live again. Absent otherwise.
|
|
856
|
+
*/
|
|
857
|
+
lifecycleHeld?: true;
|
|
835
858
|
/**
|
|
836
859
|
* Per-schedule failures on this scope: the operation name and the error. One entry is
|
|
837
860
|
* not a schedule's: `operation: 'switch-hold'` says the rewind hold (#1819) could not be
|
|
@@ -1900,6 +1923,14 @@ export interface HostAdmin {
|
|
|
1900
1923
|
* deliberately deleted (the "absent = pinned" the snapshot body already models). Audited.
|
|
1901
1924
|
*/
|
|
1902
1925
|
setScopeExpiresAt(actor: PlatformActorId, tenantId: TenantId, scopeId: ScopeId, expiresAt: string | null): Promise<void>;
|
|
1926
|
+
/**
|
|
1927
|
+
* Record a staff resolution of a kept copy (#1722, Codex #2008 r7) in the admin log. A kept
|
|
1928
|
+
* copy is one a carry's wipe refused because it took a write the carry never copied; the
|
|
1929
|
+
* vertical's store holds it, so the resolution itself (a discard, or a restore forward over the
|
|
1930
|
+
* live store) runs there, and this is its record: which script held it, what was done, and the
|
|
1931
|
+
* write revision before and after. The directory changes nothing else. Refuses an unknown scope.
|
|
1932
|
+
*/
|
|
1933
|
+
recordKeptCopyResolution(actor: PlatformActorId, tenantId: TenantId, scopeId: ScopeId, resolution: KeptCopyResolution): Promise<void>;
|
|
1903
1934
|
/**
|
|
1904
1935
|
* When each of a CO-LOCATED scope's migrations actually ran (#1236), newest
|
|
1905
1936
|
* first. `_substrat_migrations.applied_at` has been written since the table
|
|
@@ -2350,6 +2381,36 @@ export interface HostAdmin {
|
|
|
2350
2381
|
* getting refused" — and the answer says which grouping it carries.
|
|
2351
2382
|
*/
|
|
2352
2383
|
summarizeDenials(actor: PlatformActorId, tenantId: TenantId, scopeId: ScopeId, filter?: DenialFilter): Promise<DenialSummary>;
|
|
2384
|
+
/**
|
|
2385
|
+
* #1745: the refusal log — every lifecycle move `assertTransition` refused and the
|
|
2386
|
+
* operation failed with, recorded after the rollback exactly as a denial is. A bounded
|
|
2387
|
+
* page of raw rows, newest first; narrow with `entityType`/`entityId` (which record),
|
|
2388
|
+
* `actor`, `operation`, `invocationId` (which call) and a `since`/`until` window.
|
|
2389
|
+
*
|
|
2390
|
+
* The row read beneath `lifecycleFlow`'s `refused` counts, read like the denial log:
|
|
2391
|
+
* a `PlatformActorId`, a K-24 access-log entry and the K-3 (tenantId, scopeId)
|
|
2392
|
+
* cross-check. Unlike a denial row it names a record and the state it was in — a key
|
|
2393
|
+
* and a status value, never the record's fields.
|
|
2394
|
+
*/
|
|
2395
|
+
listRefusals(actor: PlatformActorId, tenantId: TenantId, scopeId: ScopeId, filter?: RefusalFilter): Promise<RefusalRecord[]>;
|
|
2396
|
+
/**
|
|
2397
|
+
* The operator's read of a scope's capabilities (#1686): the directory `ctx.capabilities.list`
|
|
2398
|
+
* reads from inside a module, for someone who has no operation to stand in. Newest first,
|
|
2399
|
+
* live (unrevoked) ones only unless `includeRevoked`, narrowable to one `entity`, and
|
|
2400
|
+
* keyset-paged: `limit` 1..200 (default 50) and a `cursor`, the previous page's `nextCursor`.
|
|
2401
|
+
* `nextCursor` is set only when more records follow, so a walk never truncates silently and
|
|
2402
|
+
* a full last page ends it. A malformed cursor is refused.
|
|
2403
|
+
*
|
|
2404
|
+
* **Never a secret, never a hash.** A record carries what the capability may do (mode, entity,
|
|
2405
|
+
* keys, operation allowlist, or the principal a `become` yields), who minted and revoked it,
|
|
2406
|
+
* its expiry, use limit and use count. The kernel's one query (`capabilityListQuery`) does not
|
|
2407
|
+
* select `token_hash`, and the record schema has no field to hold one.
|
|
2408
|
+
*
|
|
2409
|
+
* Read like the denial log: a `PlatformActorId`, a K-24 access-log entry and the K-3
|
|
2410
|
+
* (tenantId, scopeId) cross-check. Pull-only — it changes nothing, so it is not in the admin
|
|
2411
|
+
* log; `mintCapability` and `revokeCapability` are.
|
|
2412
|
+
*/
|
|
2413
|
+
listCapabilities(actor: PlatformActorId, tenantId: TenantId, scopeId: ScopeId, filter?: CapabilityFilter): Promise<CapabilityPage>;
|
|
2353
2414
|
/**
|
|
2354
2415
|
* A COMPLETE dump of the directory itself: tenants, scopes, hostnames, verticals,
|
|
2355
2416
|
* entitlements, identities, and the audit spine. The platform-level analogue of
|
|
@@ -2889,6 +2950,14 @@ export interface HostAdmin {
|
|
|
2889
2950
|
* `id` and `at`. Throws when the row cannot be written.
|
|
2890
2951
|
*/
|
|
2891
2952
|
recordOwnerTransfer(actor: PlatformActorId, entry: OwnerTransferAudit): Promise<void>;
|
|
2953
|
+
/**
|
|
2954
|
+
* Record one change to a scope's copy marker (#2005) on the admin log — `markScopeCopy` for a
|
|
2955
|
+
* `mark`, `clearScopeCopyMark` for a `clear`. The marker lives in the vertical's deployment, so
|
|
2956
|
+
* the control plane writes this around its call there — `recordOwnerTransfer`'s shape: fixed
|
|
2957
|
+
* actions, parsed entry, request actor, adapter-stamped `id` and `at`. Throws when the row
|
|
2958
|
+
* cannot be written.
|
|
2959
|
+
*/
|
|
2960
|
+
recordCopyMark(actor: PlatformActorId, entry: CopyMarkAudit): Promise<void>;
|
|
2892
2961
|
/**
|
|
2893
2962
|
* Stamp `drainedAt` on every not-yet-drained access row up to and including
|
|
2894
2963
|
* `upToId`, marking them shipped to Tier 2. Returns how many rows moved.
|
|
@@ -3055,6 +3124,53 @@ export declare function attachmentBlobKey(scopeId: string, attachmentId: string)
|
|
|
3055
3124
|
* both adapters' `open`, and the extraction job (#1575).
|
|
3056
3125
|
*/
|
|
3057
3126
|
export declare function attachmentSha256(body: Uint8Array): Promise<string>;
|
|
3127
|
+
/**
|
|
3128
|
+
* Who binds an operation, as the §4.3 entitlement gate needs to know it: the owning
|
|
3129
|
+
* module, the SKU flag its manifest declares, and the operations that module declares
|
|
3130
|
+
* as its own recurring schedules (#383). Each adapter records one per bound operation
|
|
3131
|
+
* at `registerModule`, and hands it to {@link requiredEntitlementFor} at every invoke.
|
|
3132
|
+
*/
|
|
3133
|
+
export interface OperationEntitlement {
|
|
3134
|
+
moduleId: string;
|
|
3135
|
+
entitlementKey: string;
|
|
3136
|
+
scheduledOperations: ReadonlySet<string>;
|
|
3137
|
+
}
|
|
3138
|
+
/**
|
|
3139
|
+
* The SKU flag the §4.3 gate demands for ONE invoke — `undefined` when it demands none.
|
|
3140
|
+
* One predicate, run by every place the gate lives (the Cloudflare coordinator, the
|
|
3141
|
+
* ScopeDO it hands the key to, the SQLite stub), so no adapter can be the lenient one.
|
|
3142
|
+
*
|
|
3143
|
+
* **The rule:** a module loads for a tenant only if the tenant holds its SKU flag. An
|
|
3144
|
+
* operation with no recorded binding (a bare `defineOperation`, tests and glue) is ungated.
|
|
3145
|
+
*
|
|
3146
|
+
* **The one exception (#1654): a module's own declared schedule, fired through the
|
|
3147
|
+
* system door.** All three must hold:
|
|
3148
|
+
* 1. the invoke came through the system door (`getSystemScope`, so `systemModuleId`
|
|
3149
|
+
* is set) — a door with no route; no HTTP request, principal, connection or peer
|
|
3150
|
+
* reaches it;
|
|
3151
|
+
* 2. the operation is bound by THAT module (`moduleId === systemModuleId`) — the system
|
|
3152
|
+
* principal of one module never borrows the exception for another module's operation;
|
|
3153
|
+
* 3. the module declares that operation in its own `schedules` — a system-door invoke of
|
|
3154
|
+
* any other operation is still gated, so a job run or a direct `getSystemScope` call
|
|
3155
|
+
* is not widened.
|
|
3156
|
+
*
|
|
3157
|
+
* Why the SKU is not the switch there: a composed engine's schedule runs in the engine's
|
|
3158
|
+
* name, but the engine is loaded into the scope by the VERTICAL that composes it, and a
|
|
3159
|
+
* standard install grants the vertical's keys, not the engine's (meridian composes
|
|
3160
|
+
* `engine-absence` by call; its `absence/expire-stale` was refused on every install).
|
|
3161
|
+
* The schedule's authority is already the `system:<moduleId>` grant — seated only by
|
|
3162
|
+
* provisioning a scope with that module registered, holding exactly the permissions the
|
|
3163
|
+
* schedules declare, and pulled by the kill switch (#1666). "The grant IS the switch"
|
|
3164
|
+
* (#383) now holds for loading as well as for permissions. What does NOT change: every
|
|
3165
|
+
* user-reachable door — `getScope`, connector, capability, peer, impersonation — still
|
|
3166
|
+
* demands the operation's own key, so the engine's operations stay unreachable on an
|
|
3167
|
+
* install that does not hold the engine's SKU.
|
|
3168
|
+
*
|
|
3169
|
+
* The cost, stated: on a tenant whose SKU for a module lapses, that module's own declared
|
|
3170
|
+
* schedules keep running on scopes that still hold the system grant. Stopping them is the
|
|
3171
|
+
* schedule switch (`revokeFromSystem`, #1666), or archiving the scope.
|
|
3172
|
+
*/
|
|
3173
|
+
export declare function requiredEntitlementFor(operation: string, binding: OperationEntitlement | undefined, systemModuleId: string | undefined): string | undefined;
|
|
3058
3174
|
/**
|
|
3059
3175
|
* The §4.3 entitlement-gate denial, worded identically wherever the gate lives — the
|
|
3060
3176
|
* coordinator against the shared CP, a scope DO against its projection, the SQLite
|
|
@@ -3401,6 +3517,22 @@ export interface OpsFailureInput {
|
|
|
3401
3517
|
/** The upstream provider's trace reference, when the message carried one. */
|
|
3402
3518
|
reference?: string | null;
|
|
3403
3519
|
}
|
|
3520
|
+
/** One staff resolution of a kept copy (#1722), as `recordKeptCopyResolution` logs it. */
|
|
3521
|
+
export interface KeptCopyResolution {
|
|
3522
|
+
/** `discard` wiped the kept copy; `restore-forward` restored it over the live store, then wiped it;
|
|
3523
|
+
* `release` cleared the marker of a kept copy that is the live store after all. */
|
|
3524
|
+
action: 'discard' | 'restore-forward' | 'release';
|
|
3525
|
+
/** The script that held the kept copy. */
|
|
3526
|
+
script: string;
|
|
3527
|
+
/** The script the scope routes to, which a restore forward replaced; null for a discard. */
|
|
3528
|
+
liveScript: string | null;
|
|
3529
|
+
/** When the copy was kept, from its marker. */
|
|
3530
|
+
keptAt: string;
|
|
3531
|
+
/** The kept copy's write revision the operator acted on. */
|
|
3532
|
+
revisionBefore: string | null;
|
|
3533
|
+
/** The write revision of the store the resolution changed, after it. */
|
|
3534
|
+
revisionAfter: string | null;
|
|
3535
|
+
}
|
|
3404
3536
|
/** Filter for `listOpsFailures` — cursor/order/limit exactly as `AuditLogFilter`. */
|
|
3405
3537
|
export interface OpsFailureFilter {
|
|
3406
3538
|
tenantId?: TenantId;
|
|
@@ -3640,6 +3772,10 @@ export interface ScopeHost {
|
|
|
3640
3772
|
* not. A failure is retried with backoff, dead-lettered at `maxAttempts`, and
|
|
3641
3773
|
* surfaced through `drainDue`/`executorDeadLetters` — never thrown at whoever happened
|
|
3642
3774
|
* to be holding the request.
|
|
3775
|
+
*
|
|
3776
|
+
* **Never on a scope that is not primary** (#2005). A fork, a snapshot or a preview causes
|
|
3777
|
+
* no outbound effects, so its deliveries are journaled terminal with `INERT_SCOPE_REASON`
|
|
3778
|
+
* (counted in `ExecutorDrainReport.inert`) and no handler — executor or connector — runs.
|
|
3643
3779
|
*/
|
|
3644
3780
|
registerExecutor(id: string, eventType: string, handler: ExecutorHandler, retry?: ExecutorRetryPolicy): void;
|
|
3645
3781
|
/**
|
|
@@ -4217,29 +4353,14 @@ export interface LiveReadSurface<Req extends LiveUpgradeRequest = LiveUpgradeReq
|
|
|
4217
4353
|
request: Req;
|
|
4218
4354
|
}): Promise<Res>;
|
|
4219
4355
|
}
|
|
4220
|
-
/**
|
|
4221
|
-
* Set on every refusal a live-read door returns, so a client can tell "no push here,
|
|
4222
|
-
* poll" from "your request was wrong" without parsing a body or guessing from a status.
|
|
4223
|
-
*
|
|
4224
|
-
* Here rather than in the hosted adapter because both ends of a refusal need to spell it
|
|
4225
|
-
* the same way: the hosted adapter sets it on its own refusals, and a vertical's mount
|
|
4226
|
-
* sets it on the pure host's `501` (the ask-don't-assume recipe on `ScopeHost.liveReads`).
|
|
4227
|
-
* A dev server should not have to import the Cloudflare adapter to say "poll".
|
|
4228
|
-
*/
|
|
4229
|
-
export declare const LIVE_MODE_HEADER = "x-substrat-live";
|
|
4230
|
-
/** Why a subscription was refused — the value of `LIVE_MODE_HEADER` on a refusal. */
|
|
4231
|
-
export type LiveRefusal =
|
|
4232
|
-
/** This host or connection cannot carry a WebSocket; the client should keep polling. */
|
|
4233
|
-
'poll'
|
|
4234
|
-
/** The request was not an upgrade at all — a programming error at the caller. */
|
|
4235
|
-
| 'not-an-upgrade';
|
|
4236
4356
|
/**
|
|
4237
4357
|
* Is this request asking to be upgraded to a WebSocket?
|
|
4238
4358
|
*
|
|
4239
|
-
*
|
|
4240
|
-
* anything else it decides: only a WebSocket handshake is a
|
|
4241
|
-
* always sends `Origin` on one, so everything past this check
|
|
4242
|
-
* `Origin` did not come from a browser page. A plain GET with a
|
|
4359
|
+
* Paired with `LIVE_MODE_HEADER` (`@substrat-run/contracts`) because a vertical's live
|
|
4360
|
+
* route asks it first, before anything else it decides: only a WebSocket handshake is a
|
|
4361
|
+
* live read, and a browser always sends `Origin` on one, so everything past this check
|
|
4362
|
+
* can trust that a missing `Origin` did not come from a browser page. A plain GET with a
|
|
4363
|
+
* cookie on it cannot.
|
|
4243
4364
|
*/
|
|
4244
4365
|
export declare function isUpgradeRequest(request: LiveUpgradeRequest): boolean;
|
|
4245
4366
|
/**
|