@substrat-run/kernel 0.148.0 → 0.149.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/attachment-sweep.d.ts +208 -0
  2. package/dist/attachment-sweep.d.ts.map +1 -0
  3. package/dist/attachment-sweep.js +318 -0
  4. package/dist/attachment-sweep.js.map +1 -0
  5. package/dist/index.d.ts +15 -9
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +11 -7
  8. package/dist/index.js.map +1 -1
  9. package/dist/invocation-line.d.ts +85 -0
  10. package/dist/invocation-line.d.ts.map +1 -1
  11. package/dist/invocation-line.js +43 -0
  12. package/dist/invocation-line.js.map +1 -1
  13. package/dist/migration-digest.d.ts +56 -0
  14. package/dist/migration-digest.d.ts.map +1 -1
  15. package/dist/migration-digest.js +92 -0
  16. package/dist/migration-digest.js.map +1 -1
  17. package/dist/migration-fence.d.ts +14 -0
  18. package/dist/migration-fence.d.ts.map +1 -0
  19. package/dist/migration-fence.js +515 -0
  20. package/dist/migration-fence.js.map +1 -0
  21. package/dist/migration-replay.d.ts +52 -0
  22. package/dist/migration-replay.d.ts.map +1 -0
  23. package/dist/migration-replay.js +140 -0
  24. package/dist/migration-replay.js.map +1 -0
  25. package/dist/module-migrations.d.ts +3 -3
  26. package/dist/module-migrations.js +3 -3
  27. package/dist/peer.d.ts +32 -2
  28. package/dist/peer.d.ts.map +1 -1
  29. package/dist/peer.js +99 -3
  30. package/dist/peer.js.map +1 -1
  31. package/dist/permission-eval.d.ts +8 -0
  32. package/dist/permission-eval.d.ts.map +1 -1
  33. package/dist/permission-eval.js +14 -0
  34. package/dist/permission-eval.js.map +1 -1
  35. package/dist/platform-sweep.d.ts +94 -1
  36. package/dist/platform-sweep.d.ts.map +1 -1
  37. package/dist/platform-sweep.js +130 -0
  38. package/dist/platform-sweep.js.map +1 -1
  39. package/dist/promote-review.d.ts +125 -0
  40. package/dist/promote-review.d.ts.map +1 -0
  41. package/dist/promote-review.js +181 -0
  42. package/dist/promote-review.js.map +1 -0
  43. package/dist/scope-host.d.ts +204 -42
  44. package/dist/scope-host.d.ts.map +1 -1
  45. package/dist/scope-host.js.map +1 -1
  46. package/dist/scope-store.d.ts +5 -5
  47. package/dist/scope-store.d.ts.map +1 -1
  48. package/dist/spine-restore.d.ts +2 -0
  49. package/dist/spine-restore.d.ts.map +1 -1
  50. package/dist/spine-restore.js +5 -0
  51. package/dist/spine-restore.js.map +1 -1
  52. package/dist/store-driver-releases.d.ts +306 -0
  53. package/dist/store-driver-releases.d.ts.map +1 -0
  54. package/dist/store-driver-releases.js +581 -0
  55. package/dist/store-driver-releases.js.map +1 -0
  56. package/dist/table-ownership.d.ts +5 -2
  57. package/dist/table-ownership.d.ts.map +1 -1
  58. package/dist/table-ownership.js +19 -10
  59. package/dist/table-ownership.js.map +1 -1
  60. package/dist/testing.d.ts +2 -0
  61. package/dist/testing.d.ts.map +1 -1
  62. package/dist/testing.js +2 -0
  63. package/dist/testing.js.map +1 -1
  64. package/dist/upload-link.d.ts +60 -7
  65. package/dist/upload-link.d.ts.map +1 -1
  66. package/dist/upload-link.js +189 -25
  67. package/dist/upload-link.js.map +1 -1
  68. package/dist/version-migrations.d.ts +63 -1
  69. package/dist/version-migrations.d.ts.map +1 -1
  70. package/dist/version-migrations.js +174 -8
  71. package/dist/version-migrations.js.map +1 -1
  72. package/dist/vertical-scope-stores.d.ts +9 -9
  73. package/dist/vertical-scope-stores.d.ts.map +1 -1
  74. package/dist/vertical-scope-stores.js +29 -29
  75. package/dist/vertical-scope-stores.js.map +1 -1
  76. package/package.json +2 -2
  77. package/dist/store-kind-releases.d.ts +0 -137
  78. package/dist/store-kind-releases.d.ts.map +0 -1
  79. package/dist/store-kind-releases.js +0 -252
  80. package/dist/store-kind-releases.js.map +0 -1
@@ -1,3 +1,4 @@
1
+ import type { OrphanSweepReport, OrphanSweepRequest } from './attachment-sweep.js';
1
2
  import type { ScopeStoreStubOptions, StoreErasureReplay, StoreStub } from './scope-store.js';
2
3
  import type { ModuleLog } from './module-log.js';
3
4
  import type { BoundOperations } from './module-operations.js';
@@ -5,9 +6,9 @@ import type { DeliveryRefusal } from './delivery-refusal.js';
5
6
  import type { ScopeRoleHolder } from './scope-role-admin.js';
6
7
  import type { ScopeCopyBackfillResult, SubjectErasureFence } from './scope-copy-ledger.js';
7
8
  import type { VerticalScopeStore } from './vertical-scope-stores.js';
8
- import type { StoreKindHoldMode, StoreKindReleaseRecord, StoreKindRolloutAction, StoreKindRolloutRecord, StoreKindRolloutState, StoreKindState } from './store-kind-releases.js';
9
+ import type { StoreDriverHoldMode, StoreDriverRebuildFailure, StoreDriverRebuildRecord, StoreDriverReleaseRecord, StoreDriverRolloutAction, StoreDriverRolloutRecord, StoreDriverRolloutState, StoreDriverRolloutTarget, StoreDriverState } from './store-driver-releases.js';
9
10
  import type { SubjectRedactionCounts } from './subject-redaction.js';
10
- import type { OnBehalfOf, ExportReadInput, ExportedBatch, ImportBatch, ImportedEvent, ImportResult, ImportState, ImportCursorMove, ImportCursorMoved, AdminAction, BecomeCapabilityInput, CapabilityExchange, CapabilityFilter, CapabilityId, CapabilityPage, MintedCapability, ListPage, ConnectLink, ConnectLinkConsume, ConnectLinkFilter, ConnectLinkKey, ConsumeConnectLinkInput, MintConnectLinkInput, Connection, ConnectionFilter, ConnectionId, ConnectionGrant, ConnectionGrantRecord, ConnectionSecret, CreateConnectionInput, OpenConnection, ProjectedConnectionGrant, ProjectedConnectionKey, AccessLogEntry, DelegatedReadRecord, OwnerTransferAudit, MemberChangeAudit, PeerBindingAudit, ConnectionBindingAudit, CopyMarkAudit, BindHostnameInput, AdminLogEntry, OpsFailureEntry, PlatformRequestDrainTotalsEntry, SweepRunEntry, SweepRunKind, SweepRunOutcome, ModelUsageEntry, ModelUsageSummary, CapabilityGrant, CreateTenantInput, Decision, Instant, DomainEvent, DomainEventInput, PlatformRequestInput, PlatformRequestId, PlatformRequestEntry, PlatformRequestFilter, PlatformRequestStatus, PlatformRequestFailure, PlatformOutcomeEvent, EntitlementGrant, EntitlementGrantInput, EntitlementView, MeterReading, ScopeStorageSample, EntityRef, IdentityLink, IdentityPool, Jurisdiction, ModuleId, ModuleManifest, ScheduleSpec, SystemGrant, PeerGrantsStatusEntry, SystemGrantsStatusEntry, SystemSwitchRecord, SystemSwitch, SystemSwitchResult, PeerCoverage, PeerSwitch, PeerSwitchResult, VerticalCaller, VerticalResolution, PeerBinding, PeerInstanceResolution, 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, ScopeDumpInput, SubjectShredReceipt, ScopeQueryResult, ScopeId, ScopeStatus, ScopeTable, DenialFilter, DenialSummary, RefusalFilter, RefusalRecord, PermissionDenial, Coverage, BeginImpersonationInput, ImpersonationFilter, ImpersonationSession, ImpersonationSessionId, ScopeTablePage, StorageShape, Tenant, TenantId, TenantRole, TenantStoreHandle, AttachmentRecord, UploadLinkView, BlobStoreHandle, Visibility, Vertical, VerticalChannel, VerticalVersion, TenantStatus, Page, CountedPage, FreshnessSpec, EntityHistoryInput, EventFacetInput, EventCauseInput, EventEffectsInput, EffectsTree, InvocationEventsInput, InvocationEvents, DeadLettersInput, DeadLetter, LifecycleFlowInput, LifecycleFlowResult, OperationSeriesInput, OperationSeriesResult, CauseChain, EventFacetResult, HistoryEntry, ErrorCode, PlatformRequestFailureOrigin, IssueEntry, IssueStatus, IssueStatusInput, FindingEntry, FindingFilter, FindingRuleEntry, FindingRuleInput, FindingStatusInput, DeclaredMigration, CheckSubject, EntityGrantShape } from '@substrat-run/contracts';
11
+ import type { OnBehalfOf, ExportReadInput, ExportedBatch, ImportBatch, ImportedEvent, ImportResult, ImportState, ImportCursorMove, ImportCursorMoved, AdminAction, BecomeCapabilityInput, CapabilityExchange, CapabilityFilter, CapabilityId, CapabilityPage, MintedCapability, ListPage, ConnectLink, ConnectLinkConsume, ConnectLinkFilter, ConnectLinkKey, ConsumeConnectLinkInput, MintConnectLinkInput, Connection, ConnectionFilter, ConnectionId, ConnectionGrant, ConnectionGrantRecord, ConnectionSecret, CreateConnectionInput, OpenConnection, ProjectedConnectionGrant, ProjectedConnectionKey, AccessLogEntry, DelegatedReadRecord, OwnerTransferAudit, MemberChangeAudit, PeerBindingAudit, ConnectionBindingAudit, CopyMarkAudit, OrphanSweepAudit, BindHostnameInput, AdminLogEntry, OpsFailureEntry, PlatformRequestDrainTotalsEntry, SweepRunEntry, SweepRunKind, SweepRunOutcome, ModelUsageEntry, ModelUsageSummary, CapabilityGrant, CreateTenantInput, Decision, Instant, DomainEvent, DomainEventInput, PlatformRequestInput, PlatformRequestId, PlatformRequestEntry, PlatformRequestFilter, PlatformRequestStatus, PlatformRequestFailure, PlatformOutcomeEvent, EntitlementGrant, EntitlementGrantInput, EntitlementView, MeterReading, ScopeStorageSample, EntityRef, IdentityLink, IdentityPool, Jurisdiction, ModuleId, ModuleManifest, ScheduleSpec, SystemGrant, PeerGrantsStatusEntry, SystemGrantsStatusEntry, SystemSwitchRecord, SystemSwitch, SystemSwitchResult, PeerCoverage, PeerSwitch, PeerSwitchResult, VerticalCaller, VerticalResolution, PeerBinding, PeerInstanceResolution, 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, ScopeDumpInput, SubjectShredReceipt, ScopeQueryResult, ScopeId, ScopeStatus, ScopeTable, DenialFilter, DenialSummary, RefusalFilter, RefusalRecord, PermissionDenial, Coverage, BeginImpersonationInput, ImpersonationFilter, ImpersonationSession, ImpersonationSessionId, ScopeTablePage, StorageShape, Tenant, TenantId, TenantRole, TenantStoreHandle, AttachmentRecord, UploadLinkView, BlobStoreHandle, Visibility, Vertical, VerticalChannel, VerticalVersion, TenantStatus, Page, CountedPage, FreshnessSpec, EntityHistoryInput, EventFacetInput, EventCauseInput, EventEffectsInput, EffectsTree, InvocationEventsInput, InvocationEvents, DeadLettersInput, DeadLetter, LifecycleFlowInput, LifecycleFlowResult, OperationSeriesInput, OperationSeriesResult, CauseChain, EventFacetResult, HistoryEntry, ErrorCode, PlatformRequestFailureOrigin, IssueEntry, IssueStatus, IssueStatusInput, FindingEntry, FindingFilter, FindingRuleEntry, FindingRuleInput, FindingStatusInput, DeclaredMigration, CheckSubject, EntityGrantShape } from '@substrat-run/contracts';
11
12
  import type { ConnectionUseOutcome } from './connector-calls.js';
12
13
  import type { CapabilityVerbs } from './capability.js';
13
14
  import type { AttachmentVerbs } from './upload-link.js';
@@ -811,6 +812,23 @@ export interface SqlMigration {
811
812
  /** Ordered, unique per module, e.g. '0001-init'. Journaled per (module, version). */
812
813
  version: string;
813
814
  sql: string;
815
+ /**
816
+ * Data this migration MOVES, verified (#115): `{ source: target }`, both `table` or both
817
+ * `table.column`. The kernel refuses a migration that loses rows or values a scope holds
818
+ * (`migration-fence.ts`); a move excuses the source's loss only when the target gained at least
819
+ * what the source held, and a table moved whole only when each of its columns that held values
820
+ * arrives under the same name or is declared by name. Reviewed in the migration diff beside the SQL, and not part of the
821
+ * SQL's digest. The planner never writes it: a generated migration loses nothing.
822
+ */
823
+ moves?: Readonly<Record<string, string>>;
824
+ /**
825
+ * Data this migration deliberately LOSES (#115): `{ target: why }`, where target is `table`
826
+ * (its rows, or the table itself) or `table.column` (its values, or their storage class — a
827
+ * deliberate retype, a widening included, is declared here). The
828
+ * reason is required and is what a reviewer reads. Anything lost and not declared here or in
829
+ * `moves` refuses the migration in a scope that holds it.
830
+ */
831
+ discards?: Readonly<Record<string, string>>;
814
832
  }
815
833
  /**
816
834
  * The deployed migration frontier — what "up to date" means for THIS host build
@@ -2071,7 +2089,7 @@ export interface HostAdmin {
2071
2089
  * rather than where the typing was.
2072
2090
  *
2073
2091
  * Refuses (`conflict`) a version that declares a scope-store binding the vertical already
2074
- * declared as another kind (#2286): see `verticalScopeStores`.
2092
+ * declared as another driver (#2286): see `verticalScopeStores`.
2075
2093
  */
2076
2094
  publishVersion(actor: PlatformActorId, input: PublishVersionInput): Promise<void>;
2077
2095
  /** Ordered by id (ULID = publish order), `asc` unless `page.order` says otherwise. */
@@ -2217,6 +2235,16 @@ export interface HostAdmin {
2217
2235
  * (`bindExportBreaksOf`), asked of one install. Judged on what the scope RUNS before and
2218
2236
  * after, so re-pointing a scope that runs its vertical's serving script, a fork, a preview,
2219
2237
  * or a first bind is never refused. `bindingImpact` is the listing.
2238
+ *
2239
+ * **#2347: a rebind across lineages moves the route and the pointer in ONE write.**
2240
+ * `opts.servingRef` routes the scope onto that serving script in the same write as the bind
2241
+ * (judged for what the scope runs there, as `bindingImpact` with `servingRef` is), and
2242
+ * `opts.expectedServing` makes that write conditional on the target lineage's serving script
2243
+ * still running the version the rebind was judged against. Two writes would leave a window
2244
+ * where a promote on the target lands between them: the route on the new script, the pointer
2245
+ * on the old lineage. When `expectedServing` is given, losing either compare-and-set (it, or
2246
+ * `expectedVersionId`) is refused as `rebind_review_stale`. Both options are additive: a
2247
+ * caller that sends neither binds exactly as before.
2220
2248
  */
2221
2249
  bindScopeVersion(actor: PlatformActorId, tenantId: TenantId, scopeId: ScopeId, versionId: string, opts?: {
2222
2250
  snapshot?: boolean;
@@ -2225,6 +2253,14 @@ export interface HostAdmin {
2225
2253
  expectedErasureEpoch?: number;
2226
2254
  /** #1722: the copy move this bind routes the scope onto, confirmed in the same write. */
2227
2255
  confirmMove?: ScopeCopyMoveConfirmation;
2256
+ /** #2347: the serving script the scope routes to from this same write. */
2257
+ servingRef?: string;
2258
+ /** #2347: what the target lineage's serving script must still run for the write to land. */
2259
+ expectedServing?: {
2260
+ vertical: string;
2261
+ ref: string;
2262
+ versionId: string;
2263
+ };
2228
2264
  }): Promise<void>;
2229
2265
  /**
2230
2266
  * Which apps in the scope's tenant binding `versionId` would break (#1756): the answer
@@ -2284,11 +2320,11 @@ export interface HostAdmin {
2284
2320
  */
2285
2321
  versionManifest(actor: PlatformActorId, verticalSlug: string, versionId: string): Promise<string | null>;
2286
2322
  /**
2287
- * Every scope-store binding any version of the vertical has declared, with its kind (#2286,
2323
+ * Every scope-store binding any version of the vertical has declared, with its driver (#2286,
2288
2324
  * RFC scope-stores L17): what export, wipe, erasure and the meter reach, so an object under a
2289
2325
  * binding a later version dropped is still reached. Read by its keys, with no per-version work
2290
- * once the vertical is indexed. A binding keeps its kind for the life of the vertical:
2291
- * `publishVersion` refuses a version that re-kinds one, or declares one twice (`conflict`).
2326
+ * once the vertical is indexed. A binding keeps its driver for the life of the vertical:
2327
+ * `publishVersion` refuses a version that moves one to another driver, or declares one twice (`conflict`).
2292
2328
  * Refuses (`unavailable`) when a version's manifest cannot be read, since that version may
2293
2329
  * declare a store nobody can name. Empty for an unknown vertical.
2294
2330
  */
@@ -2299,7 +2335,8 @@ export interface HostAdmin {
2299
2335
  * that carries none that can be shown (pushed before #1677, or over the push's caps), which
2300
2336
  * a reader must show as "not available", never as "no migrations". `[]` is a version whose
2301
2337
  * modules ship none. Refuses a version of another vertical, like `versionManifest`.
2302
- * Access-logged: an owner read for the promote review, never a bulk or background path.
2338
+ * Access-logged. Read by the promote review, and by every serving upload, which injects the
2339
+ * digest of the served version's migrations (#2349) — one read per upload, never a scan.
2303
2340
  */
2304
2341
  versionMigrations(actor: PlatformActorId, verticalSlug: string, versionId: string): Promise<DeclaredMigration[] | null>;
2305
2342
  /**
@@ -2619,29 +2656,29 @@ export interface HostAdmin {
2619
2656
  vertical?: string;
2620
2657
  }): Promise<BlobStoreRecord[]>;
2621
2658
  /**
2622
- * Record that a store kind's release bytes are stored, keyed by their sha256 (D-63, #2300).
2659
+ * Record that a store driver's release bytes are stored, keyed by their sha256 (D-63, #2300).
2623
2660
  * Insert-only: the same sha256 again answers the row as it stands, and from another source
2624
- * commit is refused (`conflict`). Audited `uploadStoreKindRelease` when it writes.
2661
+ * commit is refused (`conflict`). Audited `uploadStoreDriverRelease` when it writes.
2625
2662
  */
2626
- recordStoreKindRelease(actor: PlatformActorId, input: {
2627
- kind: string;
2663
+ recordStoreDriverRelease(actor: PlatformActorId, input: {
2664
+ driver: string;
2628
2665
  sha256: string;
2629
2666
  sourceCommit: string;
2630
- }): Promise<StoreKindReleaseRecord>;
2667
+ }): Promise<StoreDriverReleaseRecord>;
2631
2668
  /** One stored release, or undefined. */
2632
- getStoreKindRelease(actor: PlatformActorId, kind: string, sha256: string): Promise<StoreKindReleaseRecord | undefined>;
2669
+ getStoreDriverRelease(actor: PlatformActorId, driver: string, sha256: string): Promise<StoreDriverReleaseRecord | undefined>;
2633
2670
  /**
2634
- * A kind's current good release, every rollout of it, oldest first, and where a rollback goes.
2635
- * A `rolling` row whose holder holds the kind no longer reads as `diverged`.
2671
+ * A driver's current good release, every rollout of it, oldest first, and where a rollback goes.
2672
+ * A `rolling` row whose holder holds the driver no longer reads as `diverged`.
2636
2673
  */
2637
- storeKindState(actor: PlatformActorId, kind: string): Promise<StoreKindState>;
2674
+ storeDriverState(actor: PlatformActorId, driver: string): Promise<StoreDriverState>;
2638
2675
  /**
2639
- * Every vertical any version of which declared a store of `kind`, with those bindings: the
2676
+ * Every vertical any version of which declared a store of `driver`, with those bindings: the
2640
2677
  * scripts a rollout reaches, and in `unreadable` every vertical that cannot say (a manifest that
2641
- * does not parse, or versions from before the record that re-kinded a binding): the caller that
2678
+ * does not parse, or versions from before the record that moved a binding to another driver): the caller that
2642
2679
  * must reach every script refuses while it is not empty.
2643
2680
  */
2644
- storeKindDeclarations(actor: PlatformActorId, kind: string): Promise<{
2681
+ storeDriverDeclarations(actor: PlatformActorId, driver: string): Promise<{
2645
2682
  verticals: {
2646
2683
  vertical: string;
2647
2684
  stores: VerticalScopeStore[];
@@ -2649,42 +2686,123 @@ export interface HostAdmin {
2649
2686
  unreadable: string[];
2650
2687
  }>;
2651
2688
  /**
2652
- * Take or renew a lease on a kind (#2300): `exclusive` for a rollout or rollback, `shared` for
2689
+ * What the next request of a rollout of `sha256` (or a rollback to it) walks (#2312): the first
2690
+ * `limit` verticals, in (vertical, binding) order, holding a declaration of the driver the attempt
2691
+ * it would continue has not walked yet; `pending` counts them all and `walked` the verticals that
2692
+ * attempt walked. A request that continues no attempt (the rows before it are not `paused` at the
2693
+ * same release and action) walks every declaration. `unreadable` as `storeDriverDeclarations`.
2694
+ */
2695
+ storeDriverRolloutPending(actor: PlatformActorId, input: {
2696
+ driver: string;
2697
+ sha256: string;
2698
+ action: StoreDriverRolloutAction;
2699
+ limit: number;
2700
+ }): Promise<{
2701
+ verticals: StoreDriverRolloutTarget[];
2702
+ pending: number;
2703
+ walked: number;
2704
+ unreadable: string[];
2705
+ }>;
2706
+ /**
2707
+ * What the attempt of the rollout at `seq` walked in its earlier requests, in (vertical, binding)
2708
+ * order, at most `limit` (#2312): what a failure part way returns besides its own request's.
2709
+ */
2710
+ storeDriverRolloutWalked(actor: PlatformActorId, input: {
2711
+ driver: string;
2712
+ seq: number;
2713
+ limit: number;
2714
+ }): Promise<{
2715
+ verticals: StoreDriverRolloutTarget[];
2716
+ walked: number;
2717
+ }>;
2718
+ /**
2719
+ * Take or renew a lease on a driver (#2300): `exclusive` for a rollout or rollback, `shared` for
2653
2720
  * a push or serve rebuilding a script. False when a live hold of another holder excludes it.
2654
2721
  * `renew` extends only a hold the holder still holds (a lapsed one answers false, never a fresh
2655
2722
  * hold), and `rolloutSeq` also needs that rollout to be `rolling` still.
2656
2723
  */
2657
- holdStoreKind(actor: PlatformActorId, input: {
2658
- kind: string;
2724
+ holdStoreDriver(actor: PlatformActorId, input: {
2725
+ driver: string;
2659
2726
  holder: string;
2660
- mode: StoreKindHoldMode;
2727
+ mode: StoreDriverHoldMode;
2661
2728
  ttlMs: number;
2662
2729
  renew?: boolean;
2663
2730
  rolloutSeq?: number;
2664
2731
  }): Promise<boolean>;
2665
- releaseStoreKindHold(actor: PlatformActorId, input: {
2666
- kind: string;
2732
+ releaseStoreDriverHold(actor: PlatformActorId, input: {
2733
+ driver: string;
2667
2734
  holder: string;
2668
2735
  }): Promise<void>;
2669
2736
  /**
2670
- * Begin a rollout (or a rollback) of a stored release, as the kind's exclusive holder; refused
2671
- * (`conflict`) otherwise. A rollback must name `storeKindState`'s `rollbackTarget`. Audited
2672
- * under `rollOutStoreKind` / `rollBackStoreKind`.
2737
+ * Begin a rollout (or a rollback) of a stored release, as the driver's exclusive holder; refused
2738
+ * (`conflict`) otherwise. A rollback must name `storeDriverState`'s `rollbackTarget`. Audited
2739
+ * under `rollOutStoreDriver` / `rollBackStoreDriver`.
2673
2740
  */
2674
- beginStoreKindRollout(actor: PlatformActorId, input: {
2675
- kind: string;
2741
+ beginStoreDriverRollout(actor: PlatformActorId, input: {
2742
+ driver: string;
2676
2743
  sha256: string;
2677
- action: StoreKindRolloutAction;
2744
+ action: StoreDriverRolloutAction;
2678
2745
  holder: string;
2679
- }): Promise<StoreKindRolloutRecord>;
2680
- /** End a rollout as its exclusive holder; a `good` end makes its release current. Audited as its begin. */
2681
- finishStoreKindRollout(actor: PlatformActorId, input: {
2682
- kind: string;
2746
+ }): Promise<StoreDriverRolloutRecord>;
2747
+ /**
2748
+ * End a rollout as its exclusive holder; a `good` end makes its release current. Audited as its
2749
+ * begin. A `paused` end records `walked`, what its request walked, in the attempt's ledger, in
2750
+ * the same transaction (#2312); any other end drops the ledger, and `walked` with it is refused.
2751
+ */
2752
+ finishStoreDriverRollout(actor: PlatformActorId, input: {
2753
+ driver: string;
2683
2754
  seq: number;
2684
2755
  holder: string;
2685
- state: Exclude<StoreKindRolloutState, 'rolling'>;
2756
+ state: Exclude<StoreDriverRolloutState, 'rolling'>;
2686
2757
  detail: string | null;
2687
- }): Promise<StoreKindRolloutRecord>;
2758
+ walked?: readonly StoreDriverRolloutTarget[];
2759
+ }): Promise<StoreDriverRolloutRecord>;
2760
+ /** The platform's rebuild of every script of a driver (#2309), or undefined before its first. */
2761
+ storeDriverRebuild(actor: PlatformActorId, driver: string): Promise<StoreDriverRebuildRecord | undefined>;
2762
+ /**
2763
+ * Take the lease on a driver's fleet rebuild towards `fingerprint` (#2309): the rebuild as it
2764
+ * stands, started over when it was towards another fingerprint (or, with `restart`, when it is
2765
+ * done; with `attestedBy`, always, carrying that attestation), or null while another holder's
2766
+ * lease is live.
2767
+ */
2768
+ claimStoreDriverRebuild(actor: PlatformActorId, input: {
2769
+ driver: string;
2770
+ fingerprint: string;
2771
+ holder: string;
2772
+ ttlMs: number;
2773
+ restart?: boolean;
2774
+ attestedBy?: string;
2775
+ }): Promise<StoreDriverRebuildRecord | null>;
2776
+ /**
2777
+ * Record the lease holder's progress and renew its lease (or drop it with `release`); refused
2778
+ * (`conflict`) once another pass claimed the row or it was restarted at another fingerprint.
2779
+ */
2780
+ advanceStoreDriverRebuild(actor: PlatformActorId, input: {
2781
+ driver: string;
2782
+ fingerprint: string;
2783
+ holder: string;
2784
+ cursor: string | null;
2785
+ failures: readonly StoreDriverRebuildFailure[];
2786
+ done: boolean;
2787
+ /** With `done`: the walk proved every script current. */
2788
+ verified?: boolean;
2789
+ ttlMs: number;
2790
+ release?: boolean;
2791
+ }): Promise<StoreDriverRebuildRecord>;
2792
+ /**
2793
+ * Audit a staff-run pass of a driver's fleet rebuild (#2309) as `rebuildStoreDriver`: the driver,
2794
+ * how the pass ended and what it walked, and `deployFinished` when the person attested that no
2795
+ * control-plane deploy was rolling out. The scheduled pass writes no admin row; its failures are
2796
+ * ops-failure rows.
2797
+ */
2798
+ recordStoreDriverRebuildRun(actor: PlatformActorId, entry: {
2799
+ driver: string;
2800
+ outcome: string;
2801
+ walked: readonly string[];
2802
+ uploaded: readonly string[];
2803
+ error?: string;
2804
+ deployFinished?: true;
2805
+ }): Promise<void>;
2688
2806
  /**
2689
2807
  * One scope's directory record. Cross-checks the (tenantId, scopeId) pair and
2690
2808
  * returns undefined on a mismatch rather than another tenant's scope (K-3) —
@@ -3663,6 +3781,14 @@ export interface HostAdmin {
3663
3781
  * cannot be written.
3664
3782
  */
3665
3783
  recordCopyMark(actor: PlatformActorId, entry: CopyMarkAudit): Promise<void>;
3784
+ /**
3785
+ * Record one pass of the platform's orphan-attachment sweep over one scope (#2314) on the admin
3786
+ * log, as `sweepOrphanAttachments`. The pass runs in the vertical's deployment, so the platform's
3787
+ * sweep writes this after it, when the pass found or deleted an orphan. A dry-run row is also
3788
+ * what licenses a later destructive pass on the scope (`OrphanAttachmentSweepOptions`).
3789
+ * `recordCopyMark`'s shape. Throws when the row cannot be written.
3790
+ */
3791
+ recordOrphanSweep(actor: PlatformActorId, entry: OrphanSweepAudit): Promise<void>;
3666
3792
  /**
3667
3793
  * Record one phase of a dashboard member change (#1150) — an invite, a role move or a removal
3668
3794
  * on an installed vertical's scope — as `manageScopeMember`. The change runs in the vertical's
@@ -3971,7 +4097,19 @@ export interface UploadLinkRedeemInput {
3971
4097
  filename: string;
3972
4098
  contentType: string;
3973
4099
  body: Uint8Array;
4100
+ /**
4101
+ * #2315: the redeemer's own id for this redemption (the route reads `Idempotency-Key`). The
4102
+ * spend records its hash; a later redemption of the SAME link with the same key, the same
4103
+ * bytes and inside the link's lifetime plus `UPLOAD_LINK_REPLAY_GRACE_SECONDS` gets the stored
4104
+ * record back (`replayed: true`) and writes nothing. Without it, a spent link is 410 forever.
4105
+ */
4106
+ idempotencyKey?: string;
3974
4107
  }
4108
+ /** What `ScopeHost.redeemUploadLink` answers: the record, and whether it was stored by an earlier request. */
4109
+ export type RedeemedUploadLink = AttachmentRecord & {
4110
+ /** Present only on a replay (#2315): the file was stored by the request that spent the link, and nothing was written now. */
4111
+ replayed?: true;
4112
+ };
3975
4113
  /** An opened attachment: the metadata fact plus the bytes it witnesses. */
3976
4114
  export interface OpenedAttachment {
3977
4115
  record: AttachmentRecord;
@@ -4504,13 +4642,15 @@ export interface ScopeHost {
4504
4642
  /**
4505
4643
  * Look an upload link up WITHOUT spending it, so the route can refuse an oversize body or a
4506
4644
  * wrong type before it reads a byte. `not_found` for a secret that names nothing (and for
4507
- * one that cannot be ours); `conflict` with reason `upload_link_gone` for one that is used,
4508
- * expired or revoked. The mint is `ctx.attachments.mintUploadUrl`.
4645
+ * one that cannot be ours); `not_found` with reason `upload_link_wrong_target` when `target`
4646
+ * is given and the link was minted for another, judged before its state, so a wrong path is
4647
+ * the same 404 for a pending link and a spent one; `conflict` with reason `upload_link_gone`
4648
+ * for one that is used, expired or revoked. The mint is `ctx.attachments.mintUploadUrl`.
4509
4649
  *
4510
4650
  * Optional, like `getCapabilityAttachments`: a host that has not wired the link table says so
4511
4651
  * by leaving it out, and `mountUploadLinkRedeem` refuses to mount over one.
4512
4652
  */
4513
- inspectUploadLink?(tenantId: TenantId, scopeId: ScopeId, secret: string): Promise<UploadLinkView>;
4653
+ inspectUploadLink?(tenantId: TenantId, scopeId: ScopeId, secret: string, target?: EntityRef, idempotencyKey?: string): Promise<UploadLinkView>;
4514
4654
  /**
4515
4655
  * Redeem an upload link: write the bytes as ONE attachment, AS the principal who minted the
4516
4656
  * link — the target's `writePermission` is checked as them now, they are the author, and
@@ -4520,8 +4660,30 @@ export interface ScopeHost {
4520
4660
  * `upload_link_too_large` / `upload_link_type_refused`) and a link used by another request
4521
4661
  * first is `conflict` / `upload_link_gone`. The bytes are written to the blob store before
4522
4662
  * the row, as an upload's are, and removed again if the row is refused.
4523
- */
4524
- redeemUploadLink?(tenantId: TenantId, scopeId: ScopeId, secret: string, input: UploadLinkRedeemInput): Promise<AttachmentRecord>;
4663
+ *
4664
+ * #2315: with `input.idempotencyKey`, a link spent by a redemption that carried the same key
4665
+ * is a REPLAY — `inspectUploadLink` with that key answers the view instead of 410, and this
4666
+ * answers the stored record with `replayed: true` once the body matches it by size and
4667
+ * SHA-256 (`conflict` / `upload_link_replay_mismatch` when it does not). A replay puts no
4668
+ * bytes, writes no row and emits nothing. A retry that races the first request and loses the
4669
+ * spend has its own bytes removed and is answered as a replay too.
4670
+ */
4671
+ redeemUploadLink?(tenantId: TenantId, scopeId: ScopeId, secret: string, input: UploadLinkRedeemInput): Promise<RedeemedUploadLink>;
4672
+ /**
4673
+ * One orphan-sweep pass over a scope's attachment bytes (#2314): delete the objects under the
4674
+ * scope's attachment prefix that no `_substrat_attachments` row names, once they are older than
4675
+ * the grace period and the scope is clear of its restore fence. The scope tombstones each id
4676
+ * before its object is deleted, and the attachment insert refuses a tombstoned id, so a row
4677
+ * that commits never loses its bytes (`sweepOrphanAttachments`, attachment-sweep.ts).
4678
+ *
4679
+ * Platform-only, and judged by THIS host's copy of the scope: only an active primary of
4680
+ * `tenantId` that this host holds is swept, and anything else refuses (`conflict`), as does a
4681
+ * scope with an unsettled copy in another script. On a host whose scopes live in several
4682
+ * deployments, the caller asks only the deployment serving the scope. The age floor and the
4683
+ * fence are judged against the host's own clock, never the caller's.
4684
+ * Optional: a host that has not wired the sweep leaves it out.
4685
+ */
4686
+ sweepOrphanAttachments?(tenantId: TenantId, scopeId: ScopeId, request?: OrphanSweepRequest): Promise<OrphanSweepReport>;
4525
4687
  /**
4526
4688
  * Provision a NEW scope and load a `ScopeDump` into it — the write side of
4527
4689
  * `exportScope` and the fork primitive (docs/architecture/preview-and-snapshots.md §3):