@substrat-run/adapter-cloudflare 0.140.0 → 0.142.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/index.js CHANGED
@@ -27,6 +27,6 @@ export { createD1TenantStores, d1TenantRelationalStore, tenantStoreDatabaseName,
27
27
  export { blobStoreBucketName, createR2BlobStores, r2TenantBlobStore, } from './r2.js';
28
28
  export { createRouteResolver } from './route-resolver.js';
29
29
  export { cloudflareClientContext, cloudflareGeo } from './client-context.js';
30
- // #1978: moving here from the kernel — the hosted connector-call recorder.
30
+ // #1978: moved here from the kernel — the hosted connector-call recorder.
31
31
  export { analyticsEngineConnectorCallRecorder, CONNECTOR_CALL_DATA_POINT_LAYOUT, connectorCallDataPoint, } from './connector-calls.js';
32
32
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAWhD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAC1F,6FAA6F;AAC7F,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,6BAA6B,GAK9B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAQjF,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAG1D,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE7E,2EAA2E;AAC3E,OAAO,EACL,oCAAoC,EACpC,gCAAgC,EAChC,sBAAsB,GACvB,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAWhD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAC1F,6FAA6F;AAC7F,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,6BAA6B,GAK9B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAQjF,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAG1D,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE7E,0EAA0E;AAC1E,OAAO,EACL,oCAAoC,EACpC,gCAAgC,EAChC,sBAAsB,GACvB,MAAM,sBAAsB,CAAC"}
@@ -74,5 +74,5 @@ export type CapabilityAttachmentReply<T> = DoReply<T>;
74
74
  export declare function switchSqlOver(sql: SqlStorage): SwitchSql;
75
75
  /** The splitter both adapters run migrations through — the kernel's, re-exported for this package's callers. */
76
76
  export { splitSqlStatements };
77
- export declare function defineScopeDO(modules: ModuleRegistration[], bareOps: Record<string, OperationHandler<never, unknown>>): new (ctx: DurableObjectState, env: ScopeDoEnv) => DurableObject;
77
+ export declare function defineScopeDO(modules: ModuleRegistration[], bareOps: Record<string, OperationHandler<never, unknown>>): new (ctx: DurableObjectState, env: ScopeDoEnv) => DurableObject<ScopeDoEnv>;
78
78
  //# sourceMappingURL=scope-do.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scope-do.d.ts","sourceRoot":"","sources":["../src/scope-do.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AA2DnD,OAAO,EA6DL,KAAK,SAAS,EAsBd,KAAK,kBAAkB,EAEvB,KAAK,gBAAgB,EAqGtB,MAAM,sBAAsB,CAAC;AA6C9B,OAAO,EAAW,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAkBL,kBAAkB,EAgEnB,MAAM,sBAAsB,CAAC;AAyB9B;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,UAAU;IACzB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAoFD,oFAAoF;AACpF,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;AAkjB5E;;;GAGG;AACH,MAAM,MAAM,yBAAyB,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;AAmBtD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG,SAAS,CAOxD;AAED,gHAAgH;AAChH,OAAO,EAAE,kBAAkB,EAAE,CAAC;AA2B9B,wBAAgB,aAAa,CAC3B,OAAO,EAAE,kBAAkB,EAAE,EAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,GACxD,KAAK,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,UAAU,KAAK,aAAa,CA2yNjE"}
1
+ {"version":3,"file":"scope-do.d.ts","sourceRoot":"","sources":["../src/scope-do.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AA4DnD,OAAO,EA6DL,KAAK,SAAS,EAsBd,KAAK,kBAAkB,EAEvB,KAAK,gBAAgB,EAqGtB,MAAM,sBAAsB,CAAC;AAiD9B,OAAO,EAAW,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAkBL,kBAAkB,EAqEnB,MAAM,sBAAsB,CAAC;AA0B9B;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,UAAU;IACzB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAoFD,oFAAoF;AACpF,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;AAqjB5E;;;GAGG;AACH,MAAM,MAAM,yBAAyB,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;AAmBtD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG,SAAS,CAOxD;AAED,gHAAgH;AAChH,OAAO,EAAE,kBAAkB,EAAE,CAAC;AA2B9B,wBAAgB,aAAa,CAC3B,OAAO,EAAE,kBAAkB,EAAE,EAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,GACxD,KAAK,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,UAAU,KAAK,aAAa,CAAC,UAAU,CAAC,CAo5N7E"}
package/dist/scope-do.js CHANGED
@@ -1,13 +1,14 @@
1
1
  import { REWIND_REFUSED } from './rewind-refusal.js';
2
2
  import { SYSTEM_DOOR_MOVED } from './system-door.js';
3
3
  import { DurableObject } from 'cloudflare:workers';
4
- import { ATTACHMENT_ADDED, ATTACHMENT_REMOVED, attachmentRecord, domainEvent, domainEventInput, eventId, instant, objectRef, entityObjectRef, assertModuleEmittableType, assertKernelAuthoredType, toWireFailure, grantRefFromProof, principalId, scopeId as scopeIdOf, tenantId as tenantIdOf, platformRequestInput, platformRequestId, MAX_PENDING_PLATFORM_REQUESTS, MAX_PENDING_SWEEP_RUNS, SWEEP_RUNS_KIND, SCOPE_TABLE_PAGE_MAX, SCOPE_QUERY_ROW_MAX, listLimitOf, pageOf, countedPageOf, requestFingerprint, substratError, errorCodeOf, assertReplayableDump, REDRAIN_BATCH, } from '@substrat-run/contracts';
4
+ import { ATTACHMENT_ADDED, ATTACHMENT_REMOVED, attachmentRecord, domainEvent, domainEventInput, eventId, instant, objectRef, entityObjectRef, assertModuleEmittableType, assertKernelAuthoredType, toWireFailure, grantRefFromProof, principalId, principalBecomeCapabilityInput, scopeId as scopeIdOf, tenantId as tenantIdOf, platformRequestInput, platformRequestId, MAX_PENDING_PLATFORM_REQUESTS, MAX_PENDING_SWEEP_RUNS, SWEEP_RUNS_KIND, SCOPE_TABLE_PAGE_MAX, SCOPE_QUERY_ROW_MAX, listLimitOf, pageOf, countedPageOf, requestFingerprint, substratError, errorCodeOf, assertReplayableDump, REDRAIN_BATCH, } from '@substrat-run/contracts';
5
5
  import { ulid, LIVE_CLOSE, LIVE_SOCKETS_PER_PRINCIPAL, DO_SQL_LIMITS, unknownRoleError, createUlid, assertAllowed, ConnectionSealingKeyUnavailableError, noSealingKeyMessage, sealTo, assertReadOnlyQuery, entitlementDenial, platformRequestHistoryQuery, platformRequestOf, PLATFORM_REQUEST_COLUMNS, PLATFORM_REQUEST_REDACTION_SQL, platformRequestRedactionParams, platformRequestRedactionQuery, intentPayloadCarriesSubject, redactSubjectJobRuns, eraseSubjectFromModules, moduleErasurePlan, redactSubjectScopeText, assertRowLimit, assertRowOffset, JOB_RUN_PATCH_SQL, JOB_RUN_CLAIM_SQL, JOB_RUN_RENEW_SQL, JOB_RUN_BEGIN_SQL, JOB_RUN_MISS_SQL, JOB_RUN_MISS_SETTLE_SQL, admissionMissOutcome, JOB_LEASE_EXPIRED_NOTE, JOB_STEP_RECORD_SQL, DELIVERY_ERROR_REDACTION_SQL, REDACTED_DELIVERY_NOTE, seatScopeTuple, delegatedRevokeSql, writeExplicitTupleIn, grantEntityShapeIn, topUpEntityGrantShapes, applyScopeRoleChange, changeScopeRole, revokeScopeRoles, scopeRoleHolders, effectiveRoleGrantQuery, switchRecordedOff, switchSystemSchedules, peerGrantsStatus, systemGrantsStatus, systemScheduleState, systemSwitchedOff, denialListQuery, refusalListQuery, mapRefusalRow, denialSummaryQuery, denialTotalsQuery, DENIAL_WINDOW_QUERY, mapDenialRow, mapDenialSummaryBuckets, PermissionDenied, assertImpersonationWrites, assertModuleEnqueueableKind, impersonationStampOf, createAtomic, NotSearchable, isSearchIndexTable, searchIndexPlans, NotListable, listIndexPlans, moduleMigrations, MIGRATION_DIGEST_FENCE_DDL, MIGRATION_DIGEST_MARK_LEGACY, assertJournalDumpCoherent, assertMigrationSql, migrationDivergence, migrationFailedError, migrationSteps, planMigrations, listQuery, cursorOf, searchLimit, searchMatchExpression, searchQuery, ATTACHMENT_TEXT_DDL, attachmentRecordOfRow, enqueueAttachmentText, reconcileAttachmentText, queueAttachmentTextBackfill, startAttachmentTextBackfill, recordAttachmentText, searchAttachments, IDEMPOTENCY_DDL, REFUSALS_TABLE_DDL, REFUSALS_INDEX, refusalInsert, refusalOf, markGuardRefusal, REFUSALS_REBUILD, refusalsAdmitGuards, assertIdempotencyKey, assertPermissionKey, idempotencyLookupQuery, idempotencyPruneStatement, idempotencyRecordStatement, idempotencyOptedOutMessage, replayFor, entityVersionQuery, entityVersionOf, assertIfMatch, OUTBOX_ENTITY_INDEX, SCHEDULE_STATE_DDL, SCHEDULE_STATE_REBUILD, scheduleStateHasKind, VERTICAL_EVENTS_DDL, EXPORT_HOPS_SQL, emptyImportResult, IMPORT_CURSORS_SQL, IMPORT_CURSOR_OF_SQL, OUTBOX_MARK_SQL, exportedSinceQuery, emittedSinceQuery, emittedReportOf, EMITTED_REPORT_CAP, IMPORT_CURSOR_ADVANCE_SQL, IMPORT_RECORD_SQL, moveImportCursor, CrossVerticalRegistry, exportReadPlan, exportReadQuery, planExportBatch, withheldNote, JOB_RUN_DDL, JOB_RUN_DUE_AT, jobRunListLimit, SYSTEM_DOOR_WAIT, ancestorsWithin, } from '@substrat-run/kernel';
6
6
  import { isUpgradeRequest, readSubscription, LIVE_DECIDE_ATTEMPTS, LIVE_FANOUT_LIMIT, LIVE_MODE_HEADER, LIVE_PRINCIPAL_HEADER, LIVE_SCOPE_HEADER, LIVE_SUBSCRIBE_PATH, LIVE_TENANT_HEADER, LIVE_WITHIN_HEADER, LIVE_EXPIRES_HEADER, decodeLiveWithin, liveInstant, } from './live-reads.js';
7
7
  import { replyOf } from './do-reply.js';
8
8
  import { OperationQueue } from './serialization.js';
9
9
  import { assertNoWorkerdStorage, doScopedSql, doBuiltColumnsOf, doRedactionSql, doSpineSql } from './sql.js';
10
- import { actorOf, admitPeer, collectPeers, switchPeer, peerSubjectRef, subjectGrantState, assertNoSecret, CAPABILITY_DDL, COPY_ORIGIN_DDL, ENTITY_STATE_MOVES_DDL, SWITCH_FENCES_DDL, TABLE_OWNERS_DDL, assertMigrationLeavesLedgerAlone, recordOwnershipSteps, runMigrationStatements, splitSqlStatements, executableSqlStatements, blankSqlComments, CAPABILITY_EXCHANGE_OPERATION, capabilityAttachmentWriteRefused, createCapabilityVerbs, createEntityEdgeVerbs, createEntityStateVerbs, createTrashedReads, CONTINUATION_POSITION_CAP, visibleContinuation, searchStateWhere, uncheckedView, addStatePlans, assertNoCallerPurge, isUnreachableParent, PURGE_BATCH, heldPurgePass, lifecycleRefusal, purgeHeldBy, purgeDueOf, purgeOnlyKeysOf, runPurgePass, refuseTrashedTarget, registerTrashTargets, withheldKeysFor, statefulTablesOf, afterMigration, afterRuntimeDdl, derivesAnything, repairDerivedObjects, StateColumnLost, exchangeCapability, guardSecrets, mintBecomeCapability, redactSecrets, redactSecretText, moduleLog, asyncInvocationId, asyncLinePass, resolveCapabilitySession, revokeCapabilityAsPlatform, readCapabilityPage, domainEventOf, facetEvents, readDeadLetters, readLifecycleFlow, readOperationSeries, readHistory, readInvocation, readUndrainedOutbox, walkEventCause, walkEventEffects, } from '@substrat-run/kernel';
10
+ import { actorOf, admitPeer, collectPeers, switchPeer, peerSubjectRef, subjectGrantState, assertNoSecret, CAPABILITY_DDL, COPY_ORIGIN_DDL, ENTITY_STATE_MOVES_DDL, SWITCH_FENCES_DDL, TABLE_OWNERS_DDL, assertMigrationLeavesLedgerAlone, recordOwnershipSteps, runMigrationStatements, splitSqlStatements, executableSqlStatements, blankSqlComments, CAPABILITY_EXCHANGE_OPERATION, CAPABILITY_BECOME_MINT_OPERATION, becomeMintCheck, mintBecomeCapabilityAsPrincipal, revokeBecomeCapabilityAsPrincipal, readBecomeLinkStates, capabilityAttachmentWriteRefused, createCapabilityVerbs, createEntityEdgeVerbs, createEntityStateVerbs, createTrashedReads, CONTINUATION_POSITION_CAP, visibleContinuation, searchStateWhere, uncheckedView, addStatePlans, assertNoCallerPurge, isUnreachableParent, PURGE_BATCH, heldPurgePass, lifecycleRefusal, purgeHeldBy, purgeDueOf, purgeOnlyKeysOf, runPurgePass, refuseTrashedTarget, registerTrashTargets, withheldKeysFor, statefulTablesOf, afterMigration, afterRuntimeDdl, derivesAnything, repairDerivedObjects, StateColumnLost, exchangeCapability, guardSecrets, mintBecomeCapability, redactSecrets, redactSecretText, moduleLog, asyncInvocationId, asyncLinePass, resolveCapabilitySession, revokeCapabilityAsPlatform, readCapabilityPage, domainEventOf, facetEvents, readDeadLetters, readLifecycleFlow, readOperationSeries, readHistory, readInvocation, readUndrainedOutbox, walkEventCause, walkEventEffects, } from '@substrat-run/kernel';
11
+ import { COPY_RESTORE_FENCE_LAPSED, copyRestoreFenceLapsed } from '@substrat-run/kernel';
11
12
  import { createDoTupleChecker, createLocalControlPlaneReader, scopeTupleReader } from './checker.js';
12
13
  import { settlePlatformRequestIn } from '@substrat-run/kernel';
13
14
  import { CARRIED_AWAY_KEY, COPY_MARK_CLEARED_KEY, KEPT_COPY_REFUSAL, KEPT_DIVERGENT_KEY, LOAD_STAMP_KEY, STORE_LOCAL_META_KEYS, WRITE_REVISION_KEY, carriedAwayDump, isCopyMarkInsert, isWriteStatement, assertSpineTablesBuilt, capabilitiesForLoad, clearCopyMarker, dumpRowsInsert, isSpineTable, markCopyOrigin, repointScopeGrants, settleCopiedWork, emittedHere, IS_COPY_SQL, isCopyLoad, isLifecycleWrite, readLifecycle, settleLifecycleAfterLoad, writeLifecycle, spineColumnAdditions } from '@substrat-run/kernel';
@@ -39,6 +40,9 @@ const foreignTenant = (held, tenantId) => held !== null
39
40
  : `refused: this scope holds role rows for another tenant and none for ${tenantId}`;
40
41
  /** #2016: the load refusals `tenantReceiptRefusal` raised, told apart by identity, not by text. */
41
42
  const tenantRefusals = new WeakSet();
43
+ /** #1722: a load refused because its copy move's lease ran out (`CopyRestoreFence`), told apart
44
+ * from a store that moved, since the host must not read it as an applied retry. */
45
+ const fenceRefusals = new WeakSet();
42
46
  /**
43
47
  * The scope spine, as this adapter builds it — one of two hand-written copies (#969).
44
48
  *
@@ -2982,7 +2986,7 @@ export function defineScopeDO(modules, bareOps) {
2982
2986
  return this.queue.enqueue(async () => {
2983
2987
  let capability;
2984
2988
  try {
2985
- capability = resolveCapabilitySession(doSpineSql(this.sql), sessionHash, instant.parse(new Date().toISOString()), operation);
2989
+ capability = resolveCapabilitySession(doSpineSql(this.sql), sessionHash, instant.parse(new Date().toISOString()), operation, true);
2986
2990
  }
2987
2991
  catch (err) {
2988
2992
  return { failure: toWireFailure(err) };
@@ -3500,6 +3504,9 @@ export function defineScopeDO(modules, bareOps) {
3500
3504
  // pseudo-operation name — the actor is the capability, as on every event it
3501
3505
  // goes on to cause.
3502
3506
  emit: (capability, event) => kernelEmit(this.operationContext(principalId.parse(ulid()), tenantId, scopeId, undefined, undefined, undefined, undefined, undefined, CAPABILITY_EXCHANGE_OPERATION, capability, [], now), event),
3507
+ holdings: this.checker.holdings
3508
+ ? (principal) => this.checker.holdings({ kind: 'principal', id: principal }, { tenantId, scopeId })
3509
+ : undefined,
3503
3510
  }, secret, mode);
3504
3511
  });
3505
3512
  if (outcome)
@@ -3507,6 +3514,49 @@ export function defineScopeDO(modules, bareOps) {
3507
3514
  return outcome;
3508
3515
  });
3509
3516
  }
3517
+ /**
3518
+ * A principal's `become` mint (#1686) — a member invite's link. The kernel's
3519
+ * `becomeMintCheck` and the write in ONE queued body and one storage transaction, so
3520
+ * nothing the target or the caller holds moves between the check and the mint; a refusal
3521
+ * writes nothing. On the spine as `capability.become-minted`, the caller its actor.
3522
+ */
3523
+ async mintBecomeCapabilityBoundedFor(tenantId, scopeId, caller, input) {
3524
+ await this.ensureMigrations();
3525
+ const minter = principalId.parse(caller);
3526
+ const parsed = principalBecomeCapabilityInput.parse(input);
3527
+ return await this.queue.enqueue(async () => {
3528
+ const checked = await becomeMintCheck({ sql: doSpineSql(this.sql), checker: this.checker }, minter, parsed.principal, { tenantId, scopeId });
3529
+ if (!checked.ok)
3530
+ return checked;
3531
+ const liveSince = this.liveHighWaterMark();
3532
+ const now = instant.parse(new Date().toISOString());
3533
+ let minted;
3534
+ await this.revision.transaction(async () => {
3535
+ minted = await mintBecomeCapabilityAsPrincipal({
3536
+ sql: doSpineSql(this.sql),
3537
+ now,
3538
+ emit: (event) => kernelEmit(this.operationContext(minter, tenantId, scopeId, undefined, undefined, undefined, undefined, undefined, CAPABILITY_BECOME_MINT_OPERATION, undefined, [], now), event),
3539
+ }, minter, parsed, checked.targetDigest);
3540
+ });
3541
+ await this.settleCommitted(tenantId, scopeId, liveSince, null);
3542
+ return { ok: true, minted: minted };
3543
+ });
3544
+ }
3545
+ /** Where each named `become` link stands (#1686) — a pending-invite list's read. */
3546
+ async becomeLinkStatesFor(tenantId, scopeId, ids) {
3547
+ await this.ensureMigrations();
3548
+ return this.queue.enqueue(() => readBecomeLinkStates(doSpineSql(this.sql), ids, instant.parse(new Date().toISOString()), this.checker.holdings
3549
+ ? (principal) => this.checker.holdings({ kind: 'principal', id: principal }, { tenantId, scopeId })
3550
+ : undefined));
3551
+ }
3552
+ /**
3553
+ * Revoke a `become` capability a principal minted (#1686) — withdrawing a member invite's
3554
+ * link, with `by` recorded as the revoker. False for any other capability.
3555
+ */
3556
+ async revokeBecomeCapabilityFor(tenantId, scopeId, id, by) {
3557
+ await this.ensureMigrations();
3558
+ return await this.queue.enqueue(() => revokeBecomeCapabilityAsPrincipal({ sql: doSpineSql(this.sql), checker: this.checker, node: { tenantId, scopeId }, now: instant.parse(new Date().toISOString()) }, id, principalId.parse(by)));
3559
+ }
3510
3560
  /**
3511
3561
  * The platform's mint (#1672, `HostAdmin.mintCapability`) — a `become` capability,
3512
3562
  * serialized on the queue with every other spine write. One INSERT, so it needs no
@@ -4632,6 +4682,12 @@ export function defineScopeDO(modules, bareOps) {
4632
4682
  // #2066: the rows already there get the legacy mark below, never a digest.
4633
4683
  'ALTER TABLE _substrat_migrations ADD COLUMN sql_digest TEXT',
4634
4684
  'ALTER TABLE _substrat_tuples ADD COLUMN revoked_at TEXT',
4685
+ // #2126: NULL keeps every existing capability's attachment behavior unchanged.
4686
+ 'ALTER TABLE _substrat_capabilities ADD COLUMN attachments TEXT',
4687
+ // #1686: NULL on every row already there — none is a principal-minted become.
4688
+ 'ALTER TABLE _substrat_capabilities ADD COLUMN target_digest TEXT',
4689
+ // #1686: why the kernel revoked a link itself; NULL for every revoke already there.
4690
+ 'ALTER TABLE _substrat_capabilities ADD COLUMN revoked_reason TEXT',
4635
4691
  // #1632: legacy runs retain an unknown subject; no content-based backfill.
4636
4692
  'ALTER TABLE _substrat_job_runs ADD COLUMN subject_id TEXT',
4637
4693
  // #2034: the lease. NULL = nobody holds the run, which is right for every row already there.
@@ -4762,7 +4818,7 @@ export function defineScopeDO(modules, bareOps) {
4762
4818
  this.sql.exec(stmt);
4763
4819
  });
4764
4820
  }
4765
- async importDump(tables, destScopeId, { switchOff, sourceScopeId, exact, loadStamp, expect, resolveKept, markCopy, provisionedFor, } = {}) {
4821
+ async importDump(tables, destScopeId, { switchOff, sourceScopeId, exact, loadStamp, expect, resolveKept, markCopy, provisionedFor, fence, } = {}) {
4766
4822
  // The WHOLE drop-then-replay runs under deferred foreign keys, in one transaction.
4767
4823
  //
4768
4824
  // Two distinct FK hazards, and both need the deferral — the second is why it cannot
@@ -4798,6 +4854,13 @@ export function defineScopeDO(modules, bareOps) {
4798
4854
  throw substratError('validation_failed', 'restore refused: `exact` needs the scope the dump came from');
4799
4855
  }
4800
4856
  const switched = await this.revision.transaction(async () => {
4857
+ // #1722: first, before any read: a store a reap has since emptied must refuse the late load
4858
+ // too, and the check is against this store's clock, inside the load's own transaction.
4859
+ if (fence && copyRestoreFenceLapsed(fence, Date.now())) {
4860
+ const lapsed = substratError('precondition_failed', COPY_RESTORE_FENCE_LAPSED);
4861
+ fenceRefusals.add(lapsed);
4862
+ throw lapsed;
4863
+ }
4801
4864
  const before = this.loadMarker();
4802
4865
  // #1713: the lifecycle the platform delivered here, read before the drops take it.
4803
4866
  const lifecycleBefore = readLifecycle(this.switchSql());
@@ -5002,6 +5065,8 @@ export function defineScopeDO(modules, bareOps) {
5002
5065
  return { refused: false, switchedOff: await this.importDump(tables, destScopeId, opts) };
5003
5066
  }
5004
5067
  catch (e) {
5068
+ if (e instanceof Error && fenceRefusals.has(e))
5069
+ return { refused: 'lapsed' };
5005
5070
  const code = errorCodeOf(e);
5006
5071
  if (code === 'precondition_failed')
5007
5072
  return { refused: 'changed' };