@substrat-run/adapter-cloudflare 0.159.0 → 0.161.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/host.d.ts CHANGED
@@ -1,12 +1,756 @@
1
1
  import type { EntityGrantShape } from '@substrat-run/contracts';
2
- import { type ExportedBatch, type ExportReadInput, type ImportBatch, type ImportResult, type ImportState, type ManifestImports, type ManifestConnectionLandings, type ImportCursorMoveAt, type ImportCursorMoved, type AttachmentRecord, type UploadLinkView, type BlobStoreHandle, type ImpersonationSessionId, type ConnectionId, type ModuleId, type SystemSwitchOutcome, type SystemScheduleEntry, type PeerGrantsEntry, type EntityHistoryInput, type EventCauseInput, type EventEffectsInput, type EffectsTree, type InvocationEventsInput, type InvocationEvents, type DeadLettersInput, type DeadLetter, type LifecycleFlowInput, type LifecycleFlowResult, type OperationSeriesInput, type OperationSeriesResult, type CauseChain, type EventFacetInput, type EventFacetResult, type HistoryEntry, type Page, type SweepRunsPayload, type DomainEvent, type EntitlementGrant, type EntityRef, type ProjectedConnectionGrant, type ProjectedConnectionKey, type ProjectedIdentityLink, type PermissionKey, type PlatformActorId, type OnBehalfOf, type BecomeCapabilityInput, type BoundedBecomeMint, type BoundedBecomeRevoke, type BoundedUnbind, type BecomeLinkState, type PrincipalBecomeCapabilityInput, type CapabilityExchange, type CapabilityId, type CapabilityFilter, type CapabilityPage, type CapabilityRecord, type MintedCapability, type PrincipalId, type ResolvedIdentity, type QueryScopeInput, type ReadScopeTableInput, type RoleDefinition, type ScopeDumpInput, type ScopeDumpTable, type ScopeDumpPage, type ScopeLineage, type ScopeId, type PlatformRequestEntry, type PlatformRequestFilter, type PlatformRequestId, type ScopeTable, type DenialFilter, type DenialSummary, type PermissionDenial, type ScopeQueryResult, type ScopeTablePage, type TenantId, type TenantStoreHandle, type Coverage, type PeerCoverage, type PeerSwitchOutcome, type VerticalCaller, type LifecycleDelivery, type ScopeLifecycle } from '@substrat-run/contracts';
3
- import { type RedeemedUploadLink, type UploadLinkRedeemInput, type CarriedAway, type KeptCopy, type LoadMarker, type AppliedMigration, type BlobStoreProvisionInput, type ScopeAttachments, type AttachmentUploadInput, type OpenedAttachment, type ExecutorDeadLetter, type ExecutorDrainReport, type ExecutorHandler, type ExecutorRetryPolicy, type MigrateScopeOutcome, type MigrationFrontier, type ConnectorHandler, type ConnectorOptions, type FetchLike, type SecretBox, type HostAdmin, type ModuleRegistration, type OperationHandler, type PermissionChecker, type ProvisionScopeInput, type ScheduleRegistration, type ScheduleRunReport, type ScopeHost, type ScopeStub, type ScopeStubOptions, type InvokeOptions, type TenantRelationalStore, type TenantStoreProvisionInput, type FreshnessRegistration, type FreshnessReport, type AttachmentExtractor, type AttachmentTextBounds, type JobDriveReport, type JobHandler, type JobRun, type JobRunFilter, type StartJobRunInput, type LiveReadSurface, type SubjectRedactionCounts, type UndrainedEvents, type SwitchOutcome, type SwitchedOff, type SwitchCarryWire, type ConnectorCallRecorder, type ScopeRoleHolder, type DeclaredPermissions } from '@substrat-run/kernel';
2
+ import type { TenantVerdict } from './scope-do.js';
3
+ import { type SystemDoorMoved } from './system-door.js';
4
+ import { type WireFailure, type ExportedBatch, type ExportReadInput, type ImportBatch, type ImportResult, type ImportState, type ManifestImports, type ManifestConnectionLandings, type ImportCursorMoveAt, type ImportCursorMoved, type AttachmentRecord, type UploadLinkView, type BlobStoreHandle, type ImpersonationSession, type ImpersonationSessionId, type ConnectionId, type ModuleId, type SystemSwitchOutcome, type SystemScheduleEntry, type PeerGrantsEntry, type EntityHistoryInput, type EventCauseInput, type EventEffectsInput, type EffectsTree, type InvocationEventsInput, type InvocationEvents, type DeadLettersInput, type DeadLetter, type LifecycleFlowInput, type LifecycleFlowResult, type OperationSeriesInput, type OperationSeriesResult, type CauseChain, type EventFacetInput, type EventFacetResult, type HistoryEntry, type Page, type SweepRunsPayload, type DomainEvent, type EntitlementGrant, type EntityRef, type ProjectedConnectionGrant, type ProjectedConnectionKey, type ProjectedIdentityLink, type PermissionKey, type PlatformActorId, type OnBehalfOf, type BecomeCapabilityInput, type BoundedBecomeMint, type BoundedBecomeRevoke, type BoundedUnbind, type BecomeLinkState, type PrincipalBecomeCapabilityInput, type CapabilityExchange, type CapabilityId, type CapabilityFilter, type CapabilityPage, type CapabilityRecord, type MintedCapability, type PrincipalId, type ResolvedIdentity, type QueryScopeInput, type ReadScopeTableInput, type RoleDefinition, type ScopeDumpInput, type ScopeDumpTable, type ScopeDumpPage, type ScopeLineage, type ScopeId, type PlatformRequestEntry, type PlatformRequestFilter, type PlatformRequestId, type ScopeTable, type DenialFilter, type DenialSummary, type PermissionDenial, type RefusalFilter, type RefusalRecord, type ScopeQueryResult, type ScopeTablePage, type TenantId, type TenantStoreHandle, type Coverage, type PeerCoverage, type PeerSwitchOutcome, type VerticalCaller, type LifecycleDelivery, type ScopeLifecycle, type StoredScopeLifecycle } from '@substrat-run/contracts';
5
+ import { type RedeemedUploadLink, type UploadLinkInspection, type UploadLinkRedeemInput, type CarriedAway, type KeptCopy, type LoadMarker, type AppliedMigration, type BlobStoreProvisionInput, type ScopeAttachments, type AttachmentUploadInput, type OpenedAttachment, type OpenedAttachmentStream, type ExecutorDeadLetter, type ExecutorDrainReport, type ExecutorHandler, type ExecutorRetryPolicy, type MigrateScopeOutcome, type MigrationFrontier, type ConnectorHandler, type ConnectorOptions, type FetchLike, type SecretBox, type HostAdmin, type ModuleRegistration, type OperationHandler, type PermissionChecker, type ProvisionScopeInput, type ScheduleRegistration, type ScheduleRunReport, type ScheduleTick, type ScheduleStateKind, type ScopeHost, type ScopeStub, type ScopeStubOptions, type InvokeOptions, type TenantRelationalStore, type TenantStoreProvisionInput, type FreshnessRegistration, type FreshnessReport, type AttachmentExtractor, type AttachmentTextBackfillBatch, type AttachmentTextBounds, type ExtractionOutcome, type JobDriveReport, type JobDueKey, type JobHandler, type JobRun, type JobRunClaim, type JobRunFilter, type JobRunPatch, type JobRunRow, type JobStepRow, type StartJobRunInput, type LiveReadSurface, type SubjectRedactionCounts, type LegacySubjectRedactionCounts, type PlatformRequestRawRow, type UndrainedEvents, type UndrainedRead, type SwitchOutcome, type SwitchedOff, type RecordedOffCarry, type SwitchCarryWire, type SystemScheduleState, type PeerGrantsRow, type SystemGrantsEntry, type ConnectorCallRecorder, type ScopeRoleHolder, type DumpTable, type RoleState, type DeclaredPermissions, type PurgePass } from '@substrat-run/kernel';
4
6
  import { type OrphanSweepReport, type OrphanSweepRequest } from '@substrat-run/kernel';
5
7
  import { type CopyRestoreFence } from '@substrat-run/kernel';
6
8
  import { type ScopeStoreHostOptions, type ScopeStoreStubOptions, type StoreErasureReplay, type StoreStub } from '@substrat-run/kernel';
7
- import type { PlatformRequestSettle } from '@substrat-run/kernel';
9
+ import type { PlatformRequestSettle, ShapeCursor } from '@substrat-run/kernel';
8
10
  import { type D1TenantStores } from './d1.js';
9
11
  import { type R2BlobStores } from './r2.js';
12
+ import { type DoReply } from './do-reply.js';
13
+ interface ScopeStubRpc {
14
+ /** The applied-migration count if this call applied any, else null (nothing changed). */
15
+ migrate(): Promise<number | null>;
16
+ /**
17
+ * A FRESH attempt of whatever is pending — clears the DO's memoised migration
18
+ * promise first, so a warm instance's cached rejection is defeated (#49).
19
+ * Same return contract as `migrate()`.
20
+ */
21
+ retryMigrations(): Promise<number | null>;
22
+ /** Whether the scope is classified a copy (#2005, #2009): what a CP-less coordinator reads for primacy. */
23
+ isCopy(): Promise<boolean>;
24
+ /** Mark the scope a copy (#2005); whether this call stamped it. */
25
+ markCopy(): Promise<boolean>;
26
+ /** The lifecycle the platform last delivered to this scope (#1713), or null for none. */
27
+ lifecycle(): Promise<StoredScopeLifecycle | null>;
28
+ /** #2016: how `tenantId` reads against the scope's record, and its lifecycle — one door's read. */
29
+ admission(tenantId: TenantId): Promise<{
30
+ verdict: TenantVerdict;
31
+ lifecycle: StoredScopeLifecycle | null;
32
+ }>;
33
+ /** Store a delivered lifecycle unless a newer one is held (#1713, `writeLifecycle`); #2016: a
34
+ * scope `tenantId` is foreign to refuses it, a legacy one records its receipt. */
35
+ setLifecycle(next: ScopeLifecycle, tenantId?: TenantId): Promise<LifecycleDelivery | {
36
+ refused: 'tenant';
37
+ message: string;
38
+ }>;
39
+ /** Clear a mistaken copy classification (#2005); a load's copied-events mark is kept (#2009). */
40
+ clearCopyMark(expect?: LoadMarker): Promise<'cleared' | 'absent' | 'changed'>;
41
+ /**
42
+ * The executor's due events, decoded per row (#1636): a row that will not decode is in
43
+ * `undecodable`, for the coordinator to dead-letter, and never in `events`.
44
+ */
45
+ pendingExecutorDeliveries(deliveryId: string, eventType: string): Promise<{
46
+ events: DomainEvent[];
47
+ undecodable: {
48
+ eventId: string;
49
+ error: string;
50
+ }[];
51
+ }>;
52
+ recordExecutorAttempt(eventId: string, deliveryId: string, error: string | null, nextAttemptAt: string | null,
53
+ /**
54
+ * #1525: the call THIS attempt ran in, or null. LAST, like every argument added
55
+ * to an RPC on this interface (see `recordScheduleRun` below for what a leading
56
+ * one costs). Defaulted in the DO, so an old coordinator that sends none records
57
+ * null — the honest value, since that coordinator knew of no call.
58
+ */
59
+ invocationId?: string | null): Promise<number>;
60
+ executorAttempts(eventId: string, deliveryId: string): Promise<number>;
61
+ executorDeadLetters(): Promise<ExecutorDeadLetter[]>;
62
+ pendingPlatformRequests(): Promise<PlatformRequestRawRow[]>;
63
+ /** #618: the intent JOURNAL (every status, newest first), where the read above is pending-only. */
64
+ platformRequestHistory(filter?: PlatformRequestFilter): Promise<PlatformRequestRawRow[]>;
65
+ settlePlatformRequest(id: string, status: 'pending' | 'done' | 'failed', result: string | null, lastError: string | null,
66
+ /** #841 attribution, JSON-encoded. Defaulted in the DO so an older stub still binds. */
67
+ lastFailure?: string | null): Promise<void>;
68
+ /** #2102: a settle that writes an outcome event, in the settle's transaction. */
69
+ settlePlatformRequestWithEvent(tenantId: TenantId, scopeId: ScopeId, id: PlatformRequestId, outcome: PlatformRequestSettle): Promise<void>;
70
+ /** #574 phase 3: enqueue a `connector:<provider>` intent + journal the delivery, atomically. */
71
+ routeExecutorEventToPlatform(eventId: string, deliveryId: string, kind: string, payload: string, requestedBy: string,
72
+ /** #1525: the call this routing ran in, or null. LAST and defaulted, as above. */
73
+ invocationId?: string | null): Promise<PlatformRequestId>;
74
+ /** #1232: a CP-less pass's schedule outcomes as one batched intent. Null = dropped (backpressure). */
75
+ enqueueSweepRuns(payload: string, requestedBy: string): Promise<PlatformRequestId | null>;
76
+ /** The migration that failed on this instance, read on `migrate()`'s reject path. */
77
+ migrationFailure(): Promise<{
78
+ version: string;
79
+ error: string;
80
+ applied: number;
81
+ } | null>;
82
+ invoke(operation: string, input: unknown, principal: PrincipalId, tenantId: TenantId, scopeId: ScopeId, connectionId?: string,
83
+ /** The SKU the operation requires (#304) — enforced DO-side for a scope-local scope. */
84
+ requiredEntitlement?: string,
85
+ /** Set when the caller is a MODULE's system principal on a timer (#383). */
86
+ systemModuleId?: string,
87
+ /**
88
+ * #113 phase 3: ask for failures as a value rather than a throw. A ScopeDO still
89
+ * running older code ignores the argument and throws, which the caller handles —
90
+ * so this is safe to deploy in either direction (see `scope-do.ts`).
91
+ */
92
+ failureEnvelope?: boolean,
93
+ /**
94
+ * #129, #116: the request preconditions to evaluate INSIDE the operation's
95
+ * transaction. A ScopeDO running older code ignores the argument — which is
96
+ * why the reply must say whether it honoured one; see `concurrency` and
97
+ * `idempotency` below.
98
+ */
99
+ options?: InvokeOptions,
100
+ /**
101
+ * K-42 (#868): the impersonation session, resolved by the coordinator against
102
+ * the directory and passed WHOLE. The DO cannot read the directory, and the
103
+ * alternative — sending an id and having the DO trust it — would make a
104
+ * session forgeable by anyone who can call the RPC. An older DO ignores the
105
+ * argument, which is the SAFE direction here: it would run the operation as
106
+ * the impersonated principal with no stamp, and the coordinator refuses that
107
+ * outcome rather than accepting an unrecorded one (see `getImpersonatedScope`).
108
+ */
109
+ impersonation?: ImpersonationSession,
110
+ /**
111
+ * #1672: the HASH of a capability session token (the plaintext never crosses). The DO
112
+ * resolves it to its capability inside its queue on every call. An older DO ignores the
113
+ * argument and runs the call as the fresh placeholder principal the coordinator sent,
114
+ * who holds nothing — and the coordinator refuses any success without `capability`.
115
+ */
116
+ capabilitySession?: string,
117
+ /**
118
+ * #1706: the calling PEER vertical, as the platform named it. The DO admits it inside its
119
+ * queue on every call (`admitPeer`). An older DO ignores the argument and runs the call as
120
+ * the fresh placeholder principal the coordinator sent, who holds nothing — and the
121
+ * coordinator refuses any success without `vertical`.
122
+ */
123
+ verticalCaller?: VerticalCaller,
124
+ /**
125
+ * #1834: the instance the system door's gate read. The DO refuses the call on any other
126
+ * instance (`SYSTEM_DOOR_MOVED`), and acknowledges with `systemDoor`.
127
+ */
128
+ systemDoorInstance?: string): Promise<{
129
+ result: unknown;
130
+ /** #458: platform intents this invoke enqueued — the coordinator's drain-hint feed. */
131
+ platformRequests: number;
132
+ /** Present iff the operation failed and the DO understood `failureEnvelope`. */
133
+ failure?: WireFailure;
134
+ /**
135
+ * K-42: present iff the DO understood `impersonation` — the acknowledgement the
136
+ * coordinator refuses a success without. Unlike every other argument on this
137
+ * RPC, one an old DO silently drops fails OPEN: the operation would run as the
138
+ * impersonated principal with nothing stamped and no read-only bound.
139
+ */
140
+ impersonation?: {
141
+ honoured: boolean;
142
+ };
143
+ /**
144
+ * Present iff the DO understood `preconditions` and the operation declares
145
+ * `concurrency` (#129).
146
+ *
147
+ * **Its absence is what makes version skew safe.** Every other argument added
148
+ * to this RPC has been safe to ignore: an old DO that drops `failureEnvelope`
149
+ * throws instead of returning a value, and the coordinator handles a throw. An
150
+ * old DO that drops `ifMatch` would COMMIT THE WRITE and return 200 — the
151
+ * caller's precondition silently unenforced, which is the exact lost update
152
+ * the header was sent to prevent, now with a success telling the client it
153
+ * did not happen.
154
+ *
155
+ * So the DO acknowledges rather than the coordinator assuming, and a
156
+ * coordinator that sent `ifMatch` and got no acknowledgement refuses instead
157
+ * of returning. Fail closed, in the one direction that would otherwise fail
158
+ * open silently.
159
+ */
160
+ concurrency?: {
161
+ version: string | null;
162
+ ifMatchChecked: boolean;
163
+ };
164
+ /**
165
+ * Present iff the DO understood `idempotencyKey` (#116).
166
+ *
167
+ * Absent for the same reason and with a sharper consequence than
168
+ * `concurrency` above: an old DO that drops the key does not skip a
169
+ * comparison, it RUNS THE OPERATION — a second work order, on the retry the
170
+ * header was sent to make free. So the acknowledgement is what the
171
+ * coordinator refuses on, not something it infers.
172
+ */
173
+ idempotency?: {
174
+ keyHonoured: boolean;
175
+ replayed: boolean;
176
+ };
177
+ /** #1672: present iff the DO understood `capabilitySession`, on `impersonation`'s reasoning. */
178
+ capability?: {
179
+ honoured: boolean;
180
+ };
181
+ /** #1706: present iff the DO understood `verticalCaller`, on the same reasoning. */
182
+ vertical?: {
183
+ honoured: boolean;
184
+ };
185
+ /** #1834: present iff the DO understood `systemDoorInstance`, on the same reasoning. */
186
+ systemDoor?: {
187
+ honoured: boolean;
188
+ };
189
+ /**
190
+ * #1834: the pin missed — this instance is not the one the door's gate read — so nothing ran.
191
+ * Set only by the DO's pin check, never from an operation's failure: a handler cannot reach it.
192
+ */
193
+ systemDoorMoved?: true;
194
+ /** #1705 PR 2: exported-type rows the commit added; absent means none (or an older DO). */
195
+ exported?: number;
196
+ /**
197
+ * #1746: the events the call itself emitted, when `onEmitted` asked. Advisory, so an
198
+ * older DO that drops it is safe: the invocation line reads "not recorded".
199
+ */
200
+ emitted?: {
201
+ events: {
202
+ type: string;
203
+ entity: string;
204
+ }[];
205
+ total: number;
206
+ };
207
+ }>;
208
+ /** Trade a capability secret for a session or a principal (#1672) — the kernel's
209
+ * `exchangeCapability`, run in this scope's own storage. */
210
+ exchangeCapability(secret: string, tenantId: TenantId, scopeId: ScopeId, mode?: 'act' | 'become'): Promise<CapabilityExchange | null>;
211
+ /** The platform's `become` mint (#1672), serialized in this scope's own storage. */
212
+ mintBecomeCapability(input: BecomeCapabilityInput, actor: PlatformActorId): Promise<MintedCapability>;
213
+ /** The platform's revoke (#1672) — the record as it stood before, or null. */
214
+ revokeCapabilityAsPlatform(id: string, actor: PlatformActorId): Promise<CapabilityRecord | null>;
215
+ /** A principal's bounded `become` mint (#1686) — a member invite's link. */
216
+ mintBecomeCapabilityBoundedFor(tenantId: TenantId, scopeId: ScopeId, caller: PrincipalId, input: PrincipalBecomeCapabilityInput): Promise<BoundedBecomeMint>;
217
+ /** Revoke a `become` a principal minted (#1686); false for any other capability. */
218
+ revokeBecomeCapabilityFor(tenantId: TenantId, scopeId: ScopeId, id: string, by: PrincipalId): Promise<BoundedBecomeRevoke>;
219
+ /** A principal's bounded unbind of a binding shape's record (#1686). */
220
+ unbindEntityHolderBoundedFor(tenantId: TenantId, scopeId: ScopeId, caller: PrincipalId, entity: EntityRef): Promise<BoundedUnbind>;
221
+ /** Where each named `become` link stands (#1686). */
222
+ becomeLinkStatesFor(tenantId: TenantId, scopeId: ScopeId, ids: string[]): Promise<BecomeLinkState[]>;
223
+ /** The operator's read of this scope's capabilities (#1686) — records, never a hash. */
224
+ listCapabilities(filter?: CapabilityFilter): Promise<CapabilityPage>;
225
+ /** #1834: the system door's state read — where a module's schedules stand on this scope
226
+ * (#383, #1666), the kernel's `systemScheduleState` — and the instance that answered it. */
227
+ systemDoorState(moduleId: string): Promise<{
228
+ state: SystemScheduleState;
229
+ instance: string;
230
+ }>;
231
+ /** #2029: the peer door's state read — where one peer stands on this scope (`subjectGrantState`
232
+ * over `vertical:<slug>`, `admitPeer`'s predicate) — and the instance that answered it. */
233
+ peerDoorState(vertical: string): Promise<{
234
+ state: SystemScheduleState;
235
+ instance: string;
236
+ }>;
237
+ /** Move a module's schedule switch on this scope (#1666) — the kernel's
238
+ * `switchSystemSchedules`, as one serialized unit. */
239
+ switchSystemSchedules(moduleId: string, scopeId: string, to: 'on' | 'off', at: string,
240
+ /** #1823: the directory holds a live tenant-level grant for the module. */
241
+ tenantHeld?: boolean,
242
+ /** #2045: the switch call's fence — a move older than the one the scope applied is refused. */
243
+ fence?: string): Promise<SwitchOutcome & {
244
+ instance: string;
245
+ }>;
246
+ /** Move one peer's kill switch on this scope (#1706) — the kernel's `switchPeer`. The
247
+ * instance that applied it rides along (#2029), for the rewind hold's release, as for
248
+ * `switchSystemSchedules`. The host and this object ship in one bundle. */
249
+ switchPeer(vertical: string, scopeId: string, to: 'on' | 'off', at: string,
250
+ /** #2030: the directory holds a live tenant-level grant for the peer. */
251
+ tenantHeld?: boolean,
252
+ /** #2045: as on `switchSystemSchedules`. */
253
+ fence?: string): Promise<SwitchOutcome & {
254
+ instance: string;
255
+ }>;
256
+ /** Does a peer hold each key here now (#1706) — the checker's own `covers`. #2029: pinned to
257
+ * the instance the peer door's gate read; on any other the answer is `SystemDoorMoved`. */
258
+ peerCovers(tenantId: TenantId, scopeId: ScopeId, vertical: string, permissions: PermissionKey[], doorInstance: string): Promise<PeerCoverage[] | SystemDoorMoved>;
259
+ /** `ctx.canAssign`'s bound for a named principal (#1931); `null` for a role the tenant lacks. */
260
+ canAssignFor(tenantId: TenantId, scopeId: ScopeId, principal: PrincipalId, roleKey: string, atTenant?: boolean): Promise<Coverage | null>;
261
+ assignScopeRoleBoundedFor(tenantId: TenantId, scopeId: ScopeId, caller: PrincipalId, assignee: PrincipalId, roleKey: string): Promise<Coverage | null | {
262
+ refused: string;
263
+ }>;
264
+ /** #1150: the scope's role roster, and the two bounded writes over it — see `scope-do.ts`. */
265
+ scopeRoleHoldersFor(scopeId: ScopeId, principal?: PrincipalId): Promise<ScopeRoleHolder[]>;
266
+ changeScopeRoleBoundedFor(tenantId: TenantId, scopeId: ScopeId, caller: PrincipalId, principal: PrincipalId, from: string, to: string): Promise<Coverage | 'not-held' | 'unknown-to' | {
267
+ refused: string;
268
+ }>;
269
+ /** #2430: why a role may not be conferred through a door here, or null — see `scope-do.ts`. */
270
+ reservedRoleRefusalFor(tenantId: TenantId, roleKey: string, door: 'person' | 'platform'): Promise<string | null>;
271
+ /** #2430: the same for `assignScopeRole`, which names no tenant — see `scope-do.ts`. */
272
+ reservedRoleRefusalHere(roleKey: string, door: 'person' | 'platform'): Promise<string | null>;
273
+ /** #2430: why a raw grant of these keys is refused against the scope class's reservations, or null. */
274
+ reservedGrantRefusalFor(permissions: string[]): Promise<string | null>;
275
+ /** #2430: why a reconcile of these declared shapes is refused against the scope class's reservations, or null. */
276
+ reservedShapeRefusalFor(shapes: {
277
+ entityType: string;
278
+ permissions: string[];
279
+ }[]): Promise<string | null>;
280
+ /** #2430: which of these keys the scope class's modules reserve to a system principal. */
281
+ reservedAmong(permissions: string[]): Promise<string[]>;
282
+ /** #2447: what the scope class's modules reserve and declare. */
283
+ reservationHere(): Promise<{
284
+ reserved: string[];
285
+ declared: string[];
286
+ }>;
287
+ /** #2447: the tenant's roles as the scope stores them, and the keys of those seated there. */
288
+ /** `dropped` (#2531): the tombstones of the roles a projection dropped; absent from an older ScopeDO. */
289
+ roleStateHere(tenantId: TenantId): Promise<RoleState>;
290
+ revokeScopeRolesBoundedFor(tenantId: TenantId, scopeId: ScopeId, caller: PrincipalId, principal: PrincipalId): Promise<{
291
+ coverage: Coverage;
292
+ revoked: string[];
293
+ }>;
294
+ /** Every module this scope holds or has held system authority for, and where each
295
+ * stands (#1674) — the kernel's `systemGrantsStatus`, run in the scope's own storage. */
296
+ systemGrantsStatus(): Promise<SystemGrantsEntry[]>;
297
+ /** #1819: the rewind hold, on the `SWITCH_HOLDS_NAME` object only — see `scope-do.ts`. */
298
+ switchHoldToken(): Promise<number>;
299
+ switchHoldClaim(scopeId: string, moduleIds: string[], claimId: string, token: number): Promise<void>;
300
+ switchHoldOn(scopeId: string, moduleId: string, claimIds: string[]): Promise<void>;
301
+ switchHoldJoin(scopeId: string, moduleId: string, claimIds: string[]): Promise<void>;
302
+ switchHoldYoungestMs(scopeId: string, claimId: string): Promise<number | null>;
303
+ switchHoldArm(scopeId: string, claimId: string, doomed: string | null): Promise<void>;
304
+ switchHoldDrop(scopeId: string, claimId: string): Promise<void>;
305
+ switchHoldsAll(): Promise<{
306
+ scopeId: string;
307
+ moduleId: string;
308
+ }[]>;
309
+ switchHoldClaims(scopeId: string): Promise<SwitchHoldClaim[]>;
310
+ switchHoldRelease(scopeId: string, moduleId: string | null, claimIds: string[] | null): Promise<void>;
311
+ /** #1819: which instance is serving this scope, and whether it armed a rewind. */
312
+ rewindProbe(): Promise<{
313
+ instance: string;
314
+ armed: boolean;
315
+ }>;
316
+ /** Where every peer this scope holds or has held grants for stands (#1706) — the kernel's
317
+ * `peerGrantsStatus`, run in the scope's own storage. */
318
+ peerGrantsStatus(): Promise<PeerGrantsRow[]>;
319
+ /** `grantToSystem`'s scope-level write (#1666): `false`, and nothing written, while the
320
+ * module's schedule kill switch is off on this scope. */
321
+ writeSystemGrant(moduleId: string, relation: string, object: string, expiresAt: string | null): Promise<boolean>;
322
+ /** The last time a schedule's operation ran on this scope (#383), or null if never. */
323
+ scheduleLastRun(operation: string): Promise<string | null>;
324
+ /**
325
+ * #119: one pass of a purge schedule — the DO picks what is due by its own clock and the declared
326
+ * horizon, and purges it as the module's system principal. See the DO.
327
+ */
328
+ runPurgeSweep(operation: string, tenantId: TenantId, scopeId: ScopeId, systemDoorInstance: string,
329
+ /** #2192: the schedule run's id, which every purge in the pass runs under. */
330
+ runInvocationId?: string,
331
+ /**
332
+ * #2517: the platform requests the purge runs before this one in the tick enqueued. LAST and
333
+ * optional for the positional-RPC reason above; a scope without it fails a refused head.
334
+ */
335
+ earlierPurgeRequests?: number): Promise<PurgePass | SystemDoorMoved>;
336
+ /** #1232: the freshness evaluator's one-round-trip read — evidence + evaluator state per type. */
337
+ freshnessProbe(types: string[]): Promise<Record<string, {
338
+ observedAt: string | null;
339
+ stateAt: string | null;
340
+ stateOutcome: string | null;
341
+ }>>;
342
+ /** Write one `_substrat_schedule_state` row (#383) — `unit` is either a schedule
343
+ * operation (`module/verb`: when it ran, how it ended) or a freshness key
344
+ * (`freshness:<eventType>`: when the verdict was recorded, and what it was).
345
+ * `kind` says which, and is passed rather than read off the key's shape (#1288):
346
+ * an operation may legally be spelled `freshness:…`, and only the caller knows.
347
+ *
348
+ * **LAST, like every argument added to an RPC on this interface**, and for the
349
+ * reason `invoke`'s `concurrency` acknowledgement spells out above: an old DO
350
+ * drops a trailing argument it does not know, but binds a LEADING one positionally
351
+ * — `kind` would land in `unit`, `unit` in `at`, `at` in `status`, and the row
352
+ * written would be three values in the wrong columns with no error anywhere. The
353
+ * schedule's real key is then never written, so its cadence gate finds nothing and
354
+ * the operation runs again on every pass. Appended, an old DO simply records what
355
+ * it always recorded. The reverse skew — a new DO, an old coordinator sending no
356
+ * kind — hits `kind TEXT NOT NULL` and throws, which is the loud half and is left
357
+ * loud deliberately: a default here would be the derived-from-the-key guess #1288
358
+ * exists to remove.
359
+ *
360
+ * `invocationId` (#1525) is appended for the same positional reason and defaulted
361
+ * in the DO, so an old coordinator that sends none records null — honestly, since
362
+ * that coordinator minted no id to send. The coordinator passes one only for a
363
+ * fired or failed schedule; a freshness verdict invokes nothing, so it has none. */
364
+ recordScheduleRun(unit: string, at: string, status: 'ok' | 'failed' | 'skipped', kind: ScheduleStateKind, invocationId?: string | null): Promise<void>;
365
+ jobRunStartOrJoin(moduleId: string, job: string, instance: string, row: JobRunRow): Promise<JobRunRow>;
366
+ jobRunInsert(row: JobRunRow): Promise<void>;
367
+ jobRunsDueKeys(now: string, max: number): Promise<JobDueKey[]>;
368
+ jobRunList(filter: JobRunFilter): Promise<JobRunRow[]>;
369
+ jobRunClaim(id: string, owner: string, leaseMs: number): Promise<JobRunClaim | null>;
370
+ jobRunBegin(id: string, owner: string, marginMs: number): Promise<boolean>;
371
+ jobRunMiss(id: string, owner: string, note: string): Promise<{
372
+ misses: number;
373
+ failed: boolean;
374
+ } | null>;
375
+ jobRunPatch(id: string, patch: JobRunPatch, owner?: string): Promise<boolean>;
376
+ jobCommitPass(id: string, patch: JobRunPatch, owner?: string): Promise<boolean>;
377
+ jobStepBegin(runId: string, step: string, owner: string, leaseMs: number): Promise<{
378
+ held: boolean;
379
+ row: JobStepRow | null;
380
+ }>;
381
+ jobStepRecord(runId: string, step: string, result: string | null, attempts: number, lastError: string | null, at: string, owner?: string, leaseMs?: number): Promise<boolean>;
382
+ /** This scope's live `connection:<id>` grant tuples (#726 gap 1) — the read-back.
383
+ * Unions the scope's own tuples with the projected tenant-level ones, because a
384
+ * scope check consults both (rule 2 inheritance). */
385
+ listConnectionGrants(now: string): Promise<{
386
+ subject: string;
387
+ relation: string;
388
+ expires_at: string | null;
389
+ }[]>;
390
+ /** A declared entity-grant shape's grant: marker plus keys, one unit (#2071). */
391
+ grantEntityShape(principal: PrincipalId, entity: EntityRef, permissions: readonly string[]): Promise<void>;
392
+ /**
393
+ * One bounded pass of the shape reconcile with its events (#2071, retirements #2082), resuming
394
+ * at `after` (#2083). A DO from before the cursor ignores `after`, answers `done` and no `next`.
395
+ */
396
+ topUpEntityGrantShapes(tenantId: string, scopeId: string, shapes: readonly EntityGrantShape[], limit: number, after: ShapeCursor | null): Promise<{
397
+ toppedUp: number;
398
+ retired: number;
399
+ retirementsLeftOpen?: number;
400
+ next?: ShapeCursor | null;
401
+ done: boolean;
402
+ boundEver?: string[];
403
+ }>;
404
+ /** The EXPLICIT grant: `INSERT OR REPLACE`, so it clears a tombstone. */
405
+ writeTuple(subject: string, relation: string, object: string, expiresAt: string | null): Promise<DoReply<null>>;
406
+ /** Provisioning's write (#1659): creates a missing tuple, never un-revokes one. */
407
+ seatTuple(subject: string, relation: string, object: string, expiresAt: string | null): Promise<void>;
408
+ /** Provisioning's seat as one unit, then the recorded-off modules switched off in it (#1742). */
409
+ seatTuples(tuples: {
410
+ subject: string;
411
+ relation: string;
412
+ object: string;
413
+ expires_at: string | null;
414
+ }[], switchOff?: RecordedOffCarry & {
415
+ scopeId: string;
416
+ at: string;
417
+ }): Promise<SwitchedOff[]>;
418
+ /** Tombstone a scope tuple by exact (subject, relation, object). Idempotent. */
419
+ revokeTuple(subject: string, relation: string, object: string, at: string): Promise<boolean>;
420
+ hasEffectiveRoleGrantFor(tenantId: string, subject: string): Promise<boolean>;
421
+ /** Attachment surface, metadata half (#473) — see the ScopeDO methods of the same names.
422
+ * `connectionId` (#476) gates as a connection instead of `principal` when set. Each answers
423
+ * its refusal as data (#113), so its code survives the hop. */
424
+ attachmentAddReply(record: AttachmentRecord, principal: PrincipalId, tenantId: TenantId, scopeId: ScopeId, connectionId?: string): Promise<DoReply<AttachmentRecord>>;
425
+ /** The add's write gate alone (#2516): asked before a streamed body is read, so a refusal costs no
426
+ * object. Writes nothing; a refusal is recorded as the upload's own would be. */
427
+ attachmentCheckWriteReply(entity: EntityRef, principal: PrincipalId, tenantId: TenantId, scopeId: ScopeId, connectionId?: string): Promise<DoReply<null>>;
428
+ /** An upload-link redemption's metadata half: the add above, with the link spent in the same
429
+ * transaction. Its own verb so a DO that predates links refuses rather than ignores the secret. */
430
+ attachmentRedeemUploadLinkReply(record: AttachmentRecord, secret: string, tenantId: TenantId, scopeId: ScopeId,
431
+ /** #2315: recorded (as its hash) beside the spend, so a retry carrying it is a replay. */
432
+ idempotencyKey?: string): Promise<DoReply<AttachmentRecord>>;
433
+ /** #2314: the scope side of an orphan-sweep pass — condemn (or, dry, only read) the rowless ids. */
434
+ orphanAttachmentsReply(ids: string[], now: string, tenantId: TenantId, condemn: boolean, vouched?: boolean, expectedVersionId?: string): Promise<DoReply<string[]>>;
435
+ /** #2350: the licence half of a pass — record what a dry run listed, read it, or take ids out of it. */
436
+ orphanSweepLicenceReply(op: 'record' | 'read' | 'consume', ids: string[], now: string, tenantId: TenantId, vouched?: boolean, expectedVersionId?: string): Promise<DoReply<string[]>>;
437
+ /** #2314: the receipt's tenant (or null) and whether any attachment row is held; read without migrating. */
438
+ sweepFenceFacts(): Promise<{
439
+ tenant: string | null;
440
+ holdsAttachments: boolean;
441
+ }>;
442
+ /**
443
+ * Look an upload link up without spending it; `target` is judged before its state. With
444
+ * `idempotencyKey`, a link spent by a redemption that carried it answers `replay` (#2315).
445
+ */
446
+ uploadLinkInspectReply(secret: string, tenantId: TenantId, scopeId: ScopeId, target?: EntityRef, idempotencyKey?: string): Promise<DoReply<UploadLinkInspection>>;
447
+ attachmentListReply(entity: EntityRef, principal: PrincipalId, tenantId: TenantId, scopeId: ScopeId, connectionId?: string): Promise<DoReply<AttachmentRecord[]>>;
448
+ attachmentAuthorizeReply(attachmentId: string, mode: 'read' | 'write', principal: PrincipalId, tenantId: TenantId, scopeId: ScopeId, connectionId?: string,
449
+ /** #726 remedy B: admit by ownership of THIS delivery's entity, resolved here. */
450
+ forEventId?: string): Promise<DoReply<AttachmentRecord | null>>;
451
+ /** Read-only attachment gate for a registered module's system principal. */
452
+ systemAttachmentAuthorizeReply(attachmentId: string, moduleId: ModuleId, tenantId: TenantId, scopeId: ScopeId,
453
+ /** #1834: the instance the system door's gate read; on any other it answers `SystemDoorMoved`. */
454
+ systemDoorInstance?: string): Promise<DoReply<AttachmentRecord | null | SystemDoorMoved>>;
455
+ attachmentRemoveReply(attachmentId: string, principal: PrincipalId, tenantId: TenantId, scopeId: ScopeId, connectionId?: string): Promise<DoReply<AttachmentRecord | null>>;
456
+ /**
457
+ * #1686: the capability session's reads — `sessionHash` resolved inside the DO's queue.
458
+ * Each answers an envelope whose failure is DATA (`invoke`'s discipline): a throw across
459
+ * this boundary would keep its message and lose its code.
460
+ */
461
+ capabilityAttachmentList(entity: EntityRef, sessionHash: string, tenantId: TenantId, scopeId: ScopeId): Promise<DoReply<AttachmentRecord[]>>;
462
+ capabilityAttachmentOpen(attachmentId: string, sessionHash: string, tenantId: TenantId, scopeId: ScopeId): Promise<DoReply<AttachmentRecord | null>>;
463
+ /** #1686: always a failure — the refusal of a write through a capability, recorded (K-35). */
464
+ capabilityAttachmentRefuseWrite(sessionHash: string, tenantId: TenantId, scopeId: ScopeId, operation: 'attachments.upload' | 'attachments.remove', target: {
465
+ entityType: string;
466
+ } | {
467
+ attachmentId: string;
468
+ }): Promise<DoReply<never>>;
469
+ /** #1575: extracted-text search as `principal` (or the connection), its failure as data. */
470
+ attachmentSearch(term: string, limit: number, principal: PrincipalId, tenantId: TenantId, scopeId: ScopeId, connectionId?: string): Promise<DoReply<AttachmentRecord[]>>;
471
+ /** #1575: the same search through a capability session, as an envelope. */
472
+ capabilityAttachmentSearch(term: string, limit: number, sessionHash: string, tenantId: TenantId, scopeId: ScopeId): Promise<DoReply<AttachmentRecord[]>>;
473
+ /** #1575: the extraction job's record read — no gate, the kernel's own derivation. */
474
+ attachmentTextSource(attachmentId: string): Promise<AttachmentRecord | null>;
475
+ /** #1575: write an extraction outcome; false when the attachment was removed meanwhile. */
476
+ attachmentTextRecord(attachmentId: string, outcome: ExtractionOutcome): Promise<boolean>;
477
+ /** #1575: start the one-shot backfill unless the scope is marked or holds no attachments. */
478
+ attachmentTextBackfillStart(): Promise<void>;
479
+ /** #1575: one backfill batch after `after`, in one transaction. */
480
+ attachmentTextBackfillBatch(after: string | null): Promise<AttachmentTextBackfillBatch>;
481
+ /** Scope-local projection (scope-local-permissions.md): replace the tenant's roles + tuples and flip to local.
482
+ * `entitlements` (#304) rides the same snapshot — preserve-on-undefined, so a role-only re-projection
483
+ * leaves projected entitlements untouched. */
484
+ applyProjection(tenantId: string, roles: {
485
+ role_key: string;
486
+ permissions: string;
487
+ source: string;
488
+ holder?: 'person' | 'system' | null;
489
+ }[], tuples: {
490
+ subject: string;
491
+ relation: string;
492
+ object: string;
493
+ expires_at: string | null;
494
+ revoked_at: string | null;
495
+ }[], entitlements?: {
496
+ entitlement_key: string;
497
+ expires_at: string | null;
498
+ quota: number | null;
499
+ plan: string | null;
500
+ }[],
501
+ /** Scope-level tuples (e.g. the owner grant) seated in the same unit as the flip (#332) —
502
+ * never un-revoked, except a `lockout_reseat` one on a scope with no effective role grant (#1659). */
503
+ scopeTuples?: {
504
+ subject: string;
505
+ relation: string;
506
+ object: string;
507
+ expires_at: string | null;
508
+ lockout_reseat?: boolean;
509
+ }[],
510
+ /** The tenant's identity links (#406) — same preserve-on-undefined convention as entitlements. */
511
+ identities?: {
512
+ provider: string;
513
+ external_id: string;
514
+ principal_id: string;
515
+ scope_id: string | null;
516
+ }[],
517
+ /** Live connections' PUBLIC sealing keys (#687) — same preserve-on-undefined convention. */
518
+ connectionKeys?: {
519
+ connection_id: string;
520
+ provider: string;
521
+ key_id: string;
522
+ public_key: string;
523
+ }[],
524
+ /** The recorded-off modules switched off after the seat, in the same unit (#1742). */
525
+ switchOff?: RecordedOffCarry & {
526
+ scopeId: string;
527
+ at: string;
528
+ }, serviceSubjects?: readonly string[]): Promise<SwitchedOff[]>;
529
+ /** #113: `applyProjection`, its refusal answered as data. */
530
+ applyProjectionReply(...args: Parameters<ScopeStubRpc['applyProjection']>): Promise<DoReply<SwitchedOff[]>>;
531
+ /** Resolve an external identity from this scope's projected links (#406) — the CP-less auth read. */
532
+ resolveProjectedIdentity(tenantId: string, provider: string, externalId: string): Promise<{
533
+ principal: string;
534
+ scopeId: string | null;
535
+ } | undefined>;
536
+ /** Read-only introspection of this scope's DB (§5.4 admin-query RPC). */
537
+ introspectTables(): Promise<ScopeTable[]>;
538
+ introspectTable(table: string, limit: number, offset: number): Promise<ScopeTablePage>;
539
+ /** #113: `introspectTable`, its refusal answered as data. */
540
+ introspectTableReply(table: string, limit: number, offset: number): Promise<DoReply<ScopeTablePage>>;
541
+ /** One read-only SQL statement, gated + rolled back inside the DO (#219). */
542
+ introspectQuery(sql: string): Promise<ScopeQueryResult>;
543
+ /** #113: `introspectQuery`, its refusal answered as data. */
544
+ introspectQueryReply(sql: string): Promise<DoReply<ScopeQueryResult>>;
545
+ /** The K-35 denial log, read back (#867) — raw rows and the bucketed view. */
546
+ listDenials(filter?: DenialFilter): Promise<PermissionDenial[]>;
547
+ summarizeDenials(filter?: DenialFilter): Promise<DenialSummary>;
548
+ /** #1745: the refusal log, read back — refused lifecycle moves, newest first. */
549
+ listRefusals(filter?: RefusalFilter): Promise<RefusalRecord[]>;
550
+ /** Complete logical dump of this scope's DB (preview-and-snapshots.md §3). */
551
+ exportDump(): Promise<ScopeDumpTable[]>;
552
+ /**
553
+ * Load a dump into this (freshly-provisioned) scope — the fork write side.
554
+ * `destScopeId` re-points the dump's scope-level grants at the destination.
555
+ */
556
+ importDump(tables: ScopeDumpTable[], destScopeId?: ScopeId, opts?: {
557
+ /** The recorded-off modules switched off on `destScopeId` after the replay, in its event (#1742). */
558
+ switchOff?: RecordedOffCarry & {
559
+ at: string;
560
+ };
561
+ /** The scope the dump was captured from (#1869): only its node grants are re-pointed. */
562
+ sourceScopeId?: ScopeId;
563
+ /** The platform exported the dump itself: no fallback (`RepointSource.exact`). */
564
+ exact?: boolean;
565
+ /** #1722: the stamp this load leaves; without one it leaves none. */
566
+ loadStamp?: string;
567
+ /** #2005: the directory says the scope is not primary — mark it a copy. */
568
+ markCopy?: boolean;
569
+ /** #2016: the tenant the platform says the scope belongs to, recorded as its receipt. */
570
+ provisionedFor?: TenantId;
571
+ }): Promise<SwitchedOff[]>;
572
+ /** #1722: what a carry's restore into this store expects to find unchanged. */
573
+ loadMarker(): Promise<LoadMarker>;
574
+ /** #1722: `importDump` with its refusals (`changed` under `expect`, `kept`) answered as values. */
575
+ importDumpChecked(tables: ScopeDumpTable[], destScopeId: ScopeId, opts: {
576
+ switchOff?: RecordedOffCarry & {
577
+ at: string;
578
+ };
579
+ sourceScopeId?: ScopeId;
580
+ exact?: boolean;
581
+ loadStamp?: string;
582
+ expect?: LoadMarker;
583
+ markCopy?: boolean;
584
+ provisionedFor?: TenantId;
585
+ /** #1722: the copy move's lease; `lapsed` once `notAfter` has passed. */
586
+ fence?: CopyRestoreFence;
587
+ }): Promise<{
588
+ refused: 'changed' | 'kept' | 'lapsed';
589
+ } | {
590
+ refused: 'tenant';
591
+ message: string;
592
+ } | {
593
+ refused: false;
594
+ switchedOff: SwitchedOff[];
595
+ }>;
596
+ /** #1722: the kept-copy marker, or null. */
597
+ keptCopy(): Promise<KeptCopy | null>;
598
+ /** #1722: discard a kept copy at the revision the operator acted on. */
599
+ discardKeptCopy(scopeId: ScopeId, revision: string | null, carriedAway: CarriedAway, markCopy?: boolean, loadStamp?: string | null): Promise<{
600
+ discarded: true;
601
+ } | {
602
+ refused: 'changed' | 'not-kept';
603
+ }>;
604
+ /** #1722: `exportDump` and the store's load stamp, read in one call. */
605
+ exportDumpStamped(): Promise<{
606
+ tables: ScopeDumpTable[];
607
+ loadStamp: string | null;
608
+ revision: string | null;
609
+ }>;
610
+ /** #2450: one page of `exportDumpStamped`; the first (`cursor` null) mints the load stamp. */
611
+ exportDumpPage(cursor: string | null, opts?: {
612
+ stamp?: boolean;
613
+ maxBytes?: number;
614
+ }): Promise<DoReply<ScopeDumpPage>>;
615
+ /** #2450: stage one page for `commitStagedDumpChecked`; page 0 starts (and replaces) a stage. */
616
+ stageDumpPage(stageId: string, seq: number, tables: ScopeDumpTable[]): Promise<DoReply<{
617
+ staged: boolean;
618
+ }>>;
619
+ /** #2450: `importDumpChecked`, the rows read from the stage named. */
620
+ commitStagedDumpChecked(stageId: string, destScopeId: ScopeId, opts: Parameters<ScopeStubRpc['importDumpChecked']>[2]): Promise<Awaited<ReturnType<ScopeStubRpc['importDumpChecked']>> & {
621
+ tables?: number;
622
+ }>;
623
+ /** #2450: drop the stage, if it is still this one. */
624
+ discardStagedDump(stageId: string): Promise<{
625
+ discarded: boolean;
626
+ }>;
627
+ /** #1722: wipe a carried copy if nothing was loaded since `expectLoadStamp`; false when refused. */
628
+ wipeCarried(scopeId: ScopeId, expectLoadStamp: string | null, carriedAway: CarriedAway, opts?: {
629
+ expectRevision?: string | null;
630
+ protectIfChanged?: boolean;
631
+ markCopy?: boolean;
632
+ }): Promise<boolean>;
633
+ /** #1722: clear a kept copy's marker where it is the live store, at the revision read. */
634
+ releaseKeptCopy(revision: string | null, markCopy?: boolean, loadStamp?: string | null): Promise<{
635
+ released: true;
636
+ } | {
637
+ refused: 'changed' | 'not-kept';
638
+ }>;
639
+ /** Wipe this scope's storage — the reap half of deleteSnapshot (§9). */
640
+ destroyStorage(): Promise<void>;
641
+ /**
642
+ * Redact the spine payloads keyed to one data subject (#37); returns how many moved,
643
+ * per table. Both spine copies of an event go in this one RPC (#1600) — the outbox row
644
+ * and any platform intent this CP-less host routed the event into.
645
+ *
646
+ * **The `number` arm is version skew, not an alternative contract.** This RPC answered
647
+ * with a bare outbox count until #1600, and an old DO on the other end of a new
648
+ * coordinator still does — the skew this interface's own `recordScheduleRun` note
649
+ * spells out, in the direction a RETURN type can carry it. The caller MUST fail on it
650
+ * rather than read it as a count: an old DO redacts the outbox and leaves the intent
651
+ * journal, which is precisely the defect #1600 exists to close, so treating its reply
652
+ * as `{ events, intents: 0 }` would mint a receipt claiming an erasure that did not
653
+ * happen. Typed as a union rather than cast at the call site so the skew has to be
654
+ * handled to compile.
655
+ *
656
+ * **The `LegacySubjectRedactionCounts` arm is the same skew, one release later
657
+ * (#1632).** A DO from after #1600 answers `{ events, intents }` and never looked at
658
+ * the job-run tables, so it is refused the same way rather than read as `jobRuns: 0`.
659
+ * And one from before the free-text half answers the job-run count without
660
+ * `idempotencyResults` or `intentIds`, and is refused for the same reason.
661
+ */
662
+ redactSubject(subjectId: string): Promise<SubjectRedactionCounts | LegacySubjectRedactionCounts | number | {
663
+ failure: WireFailure;
664
+ }>;
665
+ /** PITR bookmarks recorded before migration passes (#286), newest first. */
666
+ migrationBookmarks(limit?: number): Promise<{
667
+ bookmark: string;
668
+ takenAt: string;
669
+ pending: string[];
670
+ }[]>;
671
+ appliedMigrations(): Promise<AppliedMigration[]>;
672
+ /** `SqlStorage.databaseSize` of this scope (#1524). */
673
+ databaseSize(): Promise<number>;
674
+ /** What this scope's attachment rows sum to (#2501). */
675
+ attachmentBytes(): Promise<number>;
676
+ entityHistory(input: {
677
+ entityType: string;
678
+ entityId: string;
679
+ limit?: number;
680
+ cursor?: string;
681
+ }): Promise<Page<HistoryEntry>>;
682
+ /** The Tier-2 read, and what it stepped over (#1636) — an object, because it crosses the RPC. */
683
+ undrainedEventsRead(limit: number): Promise<UndrainedRead>;
684
+ markEventsDrained(eventIds: readonly string[], at: string): Promise<number>;
685
+ /** #1705: the producer's release, decided by the DO's own registered exports. */
686
+ /** #2029: `door`, the consumer peer's door — pinned to its gate's instance, or held off. */
687
+ exportedEventsRead(input: ExportReadInput, tenantId: TenantId, scopeId: ScopeId, door: {
688
+ instance: string;
689
+ } | {
690
+ held: true;
691
+ }): Promise<ExportedBatch | SystemDoorMoved>;
692
+ /** #1705: what the DO's modules import, and its watermark per producer. */
693
+ importStateRead(): Promise<ImportState>;
694
+ /** #1705: apply a batch another vertical exported, under the watermark's compare-and-set.
695
+ * #2029: `doorInstance`, the instance the peer door's gate read; on any other the answer is
696
+ * `SystemDoorMoved` and nothing is applied. */
697
+ importApply(batch: ImportBatch, tenantId: TenantId, scopeId: ScopeId, doorInstance: string): Promise<ImportResult | SystemDoorMoved>;
698
+ /** #1705 PR 2: was this scope provisioned here for this tenant — read without migrating. */
699
+ servesTenant(tenantId: TenantId): Promise<boolean>;
700
+ /** #2541: the size, or null when this deployment does not hold the scope — held check and read in one call. */
701
+ databaseSizeIfHeld(): Promise<number | null>;
702
+ /** #2541: the attachment bytes, or null when this deployment does not hold the scope — one call, as the size is. */
703
+ attachmentBytesIfHeld(): Promise<number | null>;
704
+ /**
705
+ * #2544: the introspection reads that name only a scope id, each `null` when this deployment does
706
+ * not hold the scope: the held check and the read in ONE call, as the two size reads are.
707
+ */
708
+ facetEventsIfHeld(input: EventFacetInput): Promise<EventFacetResult | null>;
709
+ eventEffectsIfHeld(input: EventEffectsInput): Promise<EffectsTree | null>;
710
+ invocationEventsIfHeld(input: InvocationEventsInput): Promise<InvocationEvents | null>;
711
+ deadLettersIfHeld(input: DeadLettersInput): Promise<Page<DeadLetter> | null>;
712
+ lifecycleFlowIfHeld(input: LifecycleFlowInput): Promise<LifecycleFlowResult | null>;
713
+ operationSeriesIfHeld(input: OperationSeriesInput): Promise<OperationSeriesResult | null>;
714
+ eventCauseIfHeld(input: EventCauseInput): Promise<CauseChain | null>;
715
+ entityHistoryIfHeld(input: {
716
+ entityType: string;
717
+ entityId: string;
718
+ limit?: number;
719
+ cursor?: string;
720
+ }): Promise<Page<HistoryEntry> | null>;
721
+ appliedMigrationsIfHeld(): Promise<AppliedMigration[] | null>;
722
+ migrationBookmarksIfHeld(): Promise<{
723
+ bookmark: string;
724
+ takenAt: string;
725
+ pending: string[];
726
+ }[] | null>;
727
+ introspectTablesIfHeld(): Promise<ScopeTable[] | null>;
728
+ introspectTableIfHeld(table: string, limit: number, offset: number): Promise<DoReply<ScopeTablePage> | null>;
729
+ introspectQueryIfHeld(sql: string): Promise<DoReply<ScopeQueryResult> | null>;
730
+ undrainedEventsReadIfHeld(limit: number): Promise<UndrainedRead | null>;
731
+ redrainCountIfHeld(drainedBefore: string): Promise<number | null>;
732
+ /** #1705 PR 3: the replay lever, on the scope's queue, in one transaction. */
733
+ importCursorMove(input: ImportCursorMoveAt & {
734
+ now: number;
735
+ }): Promise<ImportCursorMoved>;
736
+ redrainEvents(drainedBefore: string): Promise<number>;
737
+ /** How many rows that reopen WOULD touch, touching none of them (#1545). */
738
+ redrainCount(drainedBefore: string): Promise<number>;
739
+ facetEvents(input: EventFacetInput): Promise<EventFacetResult>;
740
+ eventCause(input: EventCauseInput): Promise<CauseChain>;
741
+ eventEffects(input: EventEffectsInput): Promise<EffectsTree>;
742
+ invocationEvents(input: InvocationEventsInput): Promise<InvocationEvents>;
743
+ deadLetters(input: DeadLettersInput): Promise<Page<DeadLetter>>;
744
+ lifecycleFlow(input: LifecycleFlowInput): Promise<LifecycleFlowResult>;
745
+ operationSeries(input: OperationSeriesInput): Promise<OperationSeriesResult>;
746
+ /** Rewind storage to a bookmark (#286's backout) — completes on the DO's restart. */
747
+ rewindToBookmark(bookmark: string, opts?: {
748
+ force?: boolean;
749
+ }): Promise<{
750
+ rewindingTo: string;
751
+ instance?: string;
752
+ }>;
753
+ }
10
754
  /**
11
755
  * Where a connector's scope-side effects land when the scope is served by ANOTHER
12
756
  * deployment (#574). Set only on the shared control plane's host — its own SCOPE
@@ -71,6 +815,12 @@ export interface ConnectorDelegation {
71
815
  */
72
816
  eventId?: string;
73
817
  }): Promise<OpenedAttachment | null>;
818
+ /**
819
+ * `openAttachment`, with the bytes streamed across the seam and handed on as they arrive (#2514).
820
+ * Optional for older embedders: without it a streamed open falls back to `openAttachment` and hands
821
+ * the buffer on as a stream, which works and holds the file whole, as it always did.
822
+ */
823
+ openAttachmentStream?(args: Parameters<ConnectorDelegation['openAttachment']>[0]): Promise<OpenedAttachmentStream | null>;
74
824
  /** Write the scope-local `connection:<id>` grant tuple in the serving deployment. */
75
825
  grant(args: {
76
826
  connectionId: ConnectionId;
@@ -361,6 +1111,12 @@ export interface CloudflareScopeHostOptions {
361
1111
  * provider can be stood up in memory for tests and dev.
362
1112
  */
363
1113
  fetch?: FetchLike;
1114
+ /**
1115
+ * How long a streamed connector response body may go without a byte before it errors (#2518).
1116
+ * Defaults to `STREAM_IDLE_TIMEOUT_MS`. A buffered read stays under the connector's `timeoutMs`.
1117
+ * Here so a test can use a short window.
1118
+ */
1119
+ streamIdleTimeoutMs?: number;
364
1120
  /**
365
1121
  * #1691: where each connector call's data point goes, beside the health line
366
1122
  * `recordConnectionUse` settles — on the platform, the control plane's Analytics
@@ -557,6 +1313,12 @@ export declare const SWITCH_HOLD_EXTRA_WAITS = 3;
557
1313
  * clock, against the releasing host's clock: minutes, not seconds.
558
1314
  */
559
1315
  export declare const SWITCH_HOLD_PENDING_MAX_MS: number;
1316
+ /**
1317
+ * Ask the scope's write gate before a streamed body is read (#2516). A scope DO from before the
1318
+ * verb exists cannot answer it, and a missing answer is never a pass: the call fails closed with a
1319
+ * retryable `unavailable` that says to redeploy, rather than skipping the gate and reading the body.
1320
+ */
1321
+ export declare function askAttachmentWriteGate(stub: Pick<ScopeStubRpc, 'attachmentCheckWriteReply'>, entity: EntityRef, principal: PrincipalId, tenantId: TenantId, scopeId: ScopeId, connectionId?: string): Promise<void>;
560
1322
  /** #1819: one rewind's claim on one held module, as the hold object stores it. */
561
1323
  export interface SwitchHoldClaim {
562
1324
  claimId: string;
@@ -584,6 +1346,8 @@ export declare class CloudflareScopeHost implements ScopeHost {
584
1346
  private readonly moduleIds;
585
1347
  /** #2430: what a registered module declares `holder: 'system'` — `assignRole`'s refusal reads it. */
586
1348
  private readonly reservedKeys;
1349
+ /** #2447: every key a registered or listed module declares, marked or not — a role definition reads it. */
1350
+ private readonly declaredKeys;
587
1351
  /** #2430: the declared permission surface `registerModule` holds each module to, when given. */
588
1352
  private readonly permissions;
589
1353
  /**
@@ -608,6 +1372,7 @@ export declare class CloudflareScopeHost implements ScopeHost {
608
1372
  /** Executor id → {eventType, handler} (K-22 §4.2). Coordinator-side, not in the DO. */
609
1373
  private readonly secretBox;
610
1374
  private readonly fetchImpl;
1375
+ private readonly streamIdleMs;
611
1376
  private readonly connectorCalls;
612
1377
  /** The live D1 client for per-tenant stores (#301); undefined ⇒ refuse loudly. */
613
1378
  private readonly tenantStores?;
@@ -782,8 +1547,20 @@ export declare class CloudflareScopeHost implements ScopeHost {
782
1547
  * gate (the caller is the control plane, which did the K-3 check + audit on its side).
783
1548
  */
784
1549
  introspectScopeTables(scopeId: ScopeId): Promise<ScopeTable[]>;
1550
+ /**
1551
+ * The Data view's table page (#2563): refused `conflict` with the shared sentence for a scope the
1552
+ * deployment does not hold, as the reads above are, never answered as an empty store. The carry
1553
+ * and its cleanup read a wiped copy's tombstone, which is not held by design: they use
1554
+ * `readTombstoneTable`.
1555
+ */
785
1556
  introspectScopeTable(scopeId: ScopeId, input: ReadScopeTableInput): Promise<ScopeTablePage>;
786
- /** The SQL console's CP-less path (#219) — same trust line as the pair above. */
1557
+ /**
1558
+ * A table of a store this deployment may no longer HOLD, for the carry and its cleanup, which ask
1559
+ * a wiped copy whether it carries the `carried_away` tombstone (#2563). Not guarded: a wiped copy
1560
+ * answers nothing else. Not for a screen or a console; those use `introspectScopeTable`.
1561
+ */
1562
+ readTombstoneTable(scopeId: ScopeId, input: ReadScopeTableInput): Promise<ScopeTablePage>;
1563
+ /** The SQL console's CP-less path (#219): guarded as the table page is (#2563). */
787
1564
  introspectScopeQuery(scopeId: ScopeId, input: QueryScopeInput): Promise<ScopeQueryResult>;
788
1565
  /**
789
1566
  * The K-35 denial log's CP-less path (#867) — same trust line as the reads above.
@@ -1033,8 +1810,13 @@ export declare class CloudflareScopeHost implements ScopeHost {
1033
1810
  entityHistoryLocal(scopeId: ScopeId, input: EntityHistoryInput): Promise<Page<HistoryEntry>>;
1034
1811
  /** When this host's own scope applied each migration (#1236) — the vertical-host read. */
1035
1812
  appliedMigrationsLocal(scopeId: ScopeId): Promise<AppliedMigration[]>;
1036
- /** This host's own scope's database size in bytes (#1524), the vertical-host read. */
1813
+ /**
1814
+ * This host's own scope's database size in bytes (#1524), the vertical-host read. A scope this
1815
+ * deployment does not hold is refused (#2541), not read as 0.
1816
+ */
1037
1817
  databaseSizeLocal(scopeId: ScopeId): Promise<number>;
1818
+ /** This host's own scope's attachment bytes (#2501), the vertical-host read; refused as the size is. */
1819
+ attachmentSizeLocal(scopeId: ScopeId): Promise<number>;
1038
1820
  /**
1039
1821
  * Each scope store's size in this deployment (#2207). The control plane already
1040
1822
  * audited the ask; this end does not write the access log again.
@@ -1180,6 +1962,12 @@ export declare class CloudflareScopeHost implements ScopeHost {
1180
1962
  */
1181
1963
  private openAttachmentBytes;
1182
1964
  importScope(actor: PlatformActorId, input: ProvisionScopeInput, dump: ScopeDumpInput): Promise<void>;
1965
+ /**
1966
+ * `importScope`, with its role judgement (#2555) when `judge`, made before the scope is created so a
1967
+ * refused import leaves nothing behind. A snapshot passes false: it is the platform's copy of a live
1968
+ * scope of this host, made at once, so its seats are the ones that scope holds now (K-55).
1969
+ */
1970
+ private importScopeFrom;
1183
1971
  restoreScope(actor: PlatformActorId, tenantId: TenantId, scopeId: ScopeId, dump: ScopeDumpInput, opts?: {
1184
1972
  sourceScopeId?: ScopeId;
1185
1973
  }): Promise<StoreErasureReplay | void>;
@@ -1537,7 +2325,9 @@ export declare class CloudflareScopeHost implements ScopeHost {
1537
2325
  * grant — see the kernel interface doc.
1538
2326
  */
1539
2327
  checkFreshness(moduleId: ModuleId, tenantId: TenantId, scopeId: ScopeId): Promise<FreshnessReport>;
1540
- runDueSchedules(moduleId: ModuleId, tenantId: TenantId, scopeId: ScopeId): Promise<ScheduleRunReport>;
2328
+ runDueSchedules(moduleId: ModuleId, tenantId: TenantId, scopeId: ScopeId,
2329
+ /** #2517: what the sweep tick's earlier calls did to this scope (`ScopeHost.runDueSchedules`). */
2330
+ tick?: ScheduleTick): Promise<ScheduleRunReport>;
1541
2331
  /** The stub body, shared by the principal, connection, system and impersonation doors. */
1542
2332
  private buildStub;
1543
2333
  close(): Promise<void>;
@@ -1908,6 +2698,78 @@ export declare class CloudflareScopeHost implements ScopeHost {
1908
2698
  /** #2430: delivered connection grants not seated because they name a reserved key; absent when none. */
1909
2699
  skippedConnectionGrants?: SkippedConnectionGrant[];
1910
2700
  }>;
2701
+ /**
2702
+ * #2447: what a tenant's role definition is judged with, beyond the stored roles the ControlPlaneDO
2703
+ * reads in its own unit:
2704
+ * - the reservations of this host's modules, joined with the scope class's. Every scope on this
2705
+ * binding is one class, so one of the tenant's scopes answers for all, and the fan-out after
2706
+ * the write projects into exactly those scopes. A tenant with no scope yet is judged by this
2707
+ * host's modules alone (a stated residual: its first scope's projection is not judged again);
2708
+ * - for a `holder: 'system'` definition of a key with no system-held definition stored (none at
2709
+ * all included: a seat can outlive its role's row), whether anyone but a system principal holds a
2710
+ * live scope-level seat in it in one of those scopes (the ControlPlaneDO reads the tenant-level
2711
+ * seats). No scope is asked for seats otherwise.
2712
+ *
2713
+ * Every scope that is not reaped is read, a scope archived straight from `provisioning` included:
2714
+ * the platform's door seats into a provisioning scope, and unarchive brings the seat back. A DO
2715
+ * cannot be asked anything without waking it, and waking one that never ran would create storage it
2716
+ * never had (#1524), so such a scope is read only when its directory row records a migration
2717
+ * (`schema_version` past `'0'`). That is not a mark of instantiation: it counts the module
2718
+ * migrations applied, and is written only when a pass applied one, so a scope of a class with no
2719
+ * module migrations reads `'0'` though it holds a seat. One that records none is not read, and the
2720
+ * definition fails closed where that scope could decide it: `unreadable` names it, and the caller
2721
+ * refuses a definition its seats, or its class's reservations when no other scope gives them, would
2722
+ * decide. So while such a scope is the tenant's only one, every definition is refused until it is
2723
+ * unarchived or reaped. No product path reaches this today: the dashboard is the one Cloudflare
2724
+ * caller of `defineRole`, and its class has migrations.
2725
+ *
2726
+ * A stated residual: this half is read before the ControlPlaneDO's unit, not in it, so a scope-level
2727
+ * seat made in between is not seen. The tenant-level seats and the stored roles are read in the unit.
2728
+ */
2729
+ private definitionContext;
2730
+ /**
2731
+ * #2447: why `roles` may not be defined on `scopeId`, judged as a set against this host's
2732
+ * reservations joined with the scope class's, or null. A restore asks it before it imports a dump,
2733
+ * which replaces whatever the scope stores now, so what the set is judged against is the DUMP's
2734
+ * roles and seats for `dump.tenantId` (`dumpRoleState`), when the caller has the dump in hand. A
2735
+ * staged restore has not (its pages are already in the scope), so it is judged as a set alone
2736
+ * here, and `projectRolesLocal` judges again after the import, against what the dump brought.
2737
+ */
2738
+ roleSetRefusalLocal(scopeId: ScopeId, roles: RoleDefinition[], dump?: {
2739
+ tenantId: TenantId;
2740
+ tables: readonly DumpTable[];
2741
+ }): Promise<string | null>;
2742
+ /**
2743
+ * #2555: refuse loading `dump` into a scope of `tenantId` (`dumpRoleRefusal`): the tenant's roles as
2744
+ * the directory stores them now, against the dump's role rows and seats, with this host's
2745
+ * reservations joined with the scope class's. The class is read at `scopeId` when the scope exists
2746
+ * (a restore), else at a live scope of the tenant (an import, whose scope is not created yet: asking
2747
+ * its object would create storage for a scope a refusal leaves unmade).
2748
+ *
2749
+ * Two stated residuals. The directory is read here and the dump is loaded in the scope's own call,
2750
+ * so a role defined between the two is not judged against the dump's seats. And an import for a
2751
+ * tenant with no live scope reads no class, so it is judged with this host's reservations alone: the
2752
+ * seat rule holds without them, and a key only the class reserves is not counted.
2753
+ */
2754
+ private refuseDumpRoles;
2755
+ /**
2756
+ * #2446: the roles `scopeId` stores for `tenantId` and the role keys anyone but a system principal
2757
+ * holds a live seat in there: what `refuseReservedRoleSet` judges a reconcile's role set against
2758
+ * (`roleStateHere`), read for a promote to judge the incoming version's set with
2759
+ * (`releaseRoleSetRefusal`) before it moves the scope. Read-only, and names no principal.
2760
+ */
2761
+ roleStateLocal(tenantId: TenantId, scopeId: ScopeId): Promise<RoleState>;
2762
+ /**
2763
+ * #2447: refuse a role set a CP-less provision or restore defines on `scopeId` for `tenantId`
2764
+ * (`reservedRoleSetRefusal`), against this host's reservations joined with the scope class's,
2765
+ * and the roles the scope stores for the tenant now with their seats there. Answers the reserved
2766
+ * keys it judged with, for the seats the same call makes.
2767
+ *
2768
+ * A stated residual: the scope state is read in one RPC (`roleStateHere`) and written in another
2769
+ * (`applyProjectionReply`), so a seat made between the two is not seen. The window is one call
2770
+ * wide, and a reconcile runs again every pass.
2771
+ */
2772
+ private refuseReservedRoleSet;
1911
2773
  /**
1912
2774
  * #2430: the delivered connection grants a provision or reconcile may seat, and those it skips. A
1913
2775
  * connection acts for the external provider holding its token, so a grant naming a key reserved
@@ -1968,7 +2830,15 @@ export declare class CloudflareScopeHost implements ScopeHost {
1968
2830
  * storage. Idempotent (writeTuple is INSERT OR REPLACE). `roleKey` must be one the scope
1969
2831
  * already projected (via `provisionScopeLocal`), or the local checker expands it to nothing.
1970
2832
  */
1971
- assignScopeRole(scopeId: ScopeId, principal: PrincipalId, roleKey: string): Promise<void>;
2833
+ assignScopeRole(scopeId: ScopeId, principal: PrincipalId, roleKey: string,
2834
+ /** #2447: `person` when the caller knows it seats a person (the owner hand-over); the platform's door otherwise. */
2835
+ door?: 'person' | 'platform'): Promise<void>;
2836
+ /**
2837
+ * #2447: why `roleKey` may not be seated at scope level through `door`, or null — the question
2838
+ * `assignScopeRole` asks before it writes, for a caller (the owner hand-over) that must ask it
2839
+ * before anything else moves.
2840
+ */
2841
+ scopeRoleRefusal(scopeId: ScopeId, roleKey: string, door: 'person' | 'platform'): Promise<string | null>;
1972
2842
  /**
1973
2843
  * Take a scope-level role back — the counterpart `assignScopeRole` went without (#1161).
1974
2844
  * A tombstone, exactly as `HostAdmin.unassignRole` writes for a scope-level assignment:
@@ -2068,6 +2938,13 @@ export declare class CloudflareScopeHost implements ScopeHost {
2068
2938
  * so a caller falls back rather than failing a dispatch over a missing file.
2069
2939
  */
2070
2940
  connectorAttachmentOpenLocal(connectionId: ConnectionId, tenantId: TenantId, scopeId: ScopeId, attachmentId: string, eventId?: string): Promise<OpenedAttachment | null>;
2941
+ /**
2942
+ * `connectorAttachmentOpenLocal`, with the bytes as a stream read from R2 as the platform reads
2943
+ * the response (#2514). The same gates, in the same order, before the bucket is touched.
2944
+ */
2945
+ connectorAttachmentOpenStreamLocal(connectionId: ConnectionId, tenantId: TenantId, scopeId: ScopeId, attachmentId: string, eventId?: string): Promise<OpenedAttachmentStream | null>;
2946
+ /** The attachment surface the platform's reads as a connection go through, behind their gates. */
2947
+ private connectorReadSurface;
2071
2948
  /**
2072
2949
  * The delivery half of `grantToConnection` for a scope served HERE (#574): write
2073
2950
  * the scope-local `connection:<id>` grant tuple the permission checker reads.
@@ -2215,4 +3092,5 @@ export declare class CloudflareScopeHost implements ScopeHost {
2215
3092
  */
2216
3093
  versionConnectionLandings(verticalSlug: string, versionId: string): Promise<ManifestConnectionLandings>;
2217
3094
  }
3095
+ export {};
2218
3096
  //# sourceMappingURL=host.d.ts.map