@substrat-run/kernel 0.136.0 → 0.138.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capability.d.ts +2 -0
- package/dist/capability.d.ts.map +1 -1
- package/dist/capability.js +2 -0
- package/dist/capability.js.map +1 -1
- package/dist/delivery-refusal.d.ts +21 -0
- package/dist/delivery-refusal.d.ts.map +1 -0
- package/dist/delivery-refusal.js +21 -0
- package/dist/delivery-refusal.js.map +1 -0
- package/dist/index.d.ts +23 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -12
- package/dist/index.js.map +1 -1
- package/dist/invocation-log.d.ts +25 -1
- package/dist/invocation-log.d.ts.map +1 -1
- package/dist/invocation-log.js +45 -6
- package/dist/invocation-log.js.map +1 -1
- package/dist/job-run.d.ts +370 -47
- package/dist/job-run.d.ts.map +1 -1
- package/dist/job-run.js +472 -81
- package/dist/job-run.js.map +1 -1
- package/dist/lifecycle-flow.d.ts.map +1 -1
- package/dist/lifecycle-flow.js +2 -1
- package/dist/lifecycle-flow.js.map +1 -1
- package/dist/membership-executor.d.ts +97 -0
- package/dist/membership-executor.d.ts.map +1 -0
- package/dist/membership-executor.js +246 -0
- package/dist/membership-executor.js.map +1 -0
- package/dist/membership-fence.d.ts +57 -0
- package/dist/membership-fence.d.ts.map +1 -0
- package/dist/membership-fence.js +90 -0
- package/dist/membership-fence.js.map +1 -0
- package/dist/operation-series.d.ts +58 -0
- package/dist/operation-series.d.ts.map +1 -0
- package/dist/operation-series.js +63 -0
- package/dist/operation-series.js.map +1 -0
- package/dist/peer.d.ts +13 -8
- package/dist/peer.d.ts.map +1 -1
- package/dist/peer.js +13 -14
- package/dist/peer.js.map +1 -1
- package/dist/permission-eval.d.ts +48 -1
- package/dist/permission-eval.d.ts.map +1 -1
- package/dist/permission-eval.js +137 -18
- package/dist/permission-eval.js.map +1 -1
- package/dist/read-only-sql.d.ts +2 -26
- package/dist/read-only-sql.d.ts.map +1 -1
- package/dist/read-only-sql.js +13 -9
- package/dist/read-only-sql.js.map +1 -1
- package/dist/refusal-query.d.ts +4 -1
- package/dist/refusal-query.d.ts.map +1 -1
- package/dist/refusal-query.js +6 -4
- package/dist/refusal-query.js.map +1 -1
- package/dist/refusals.d.ts +80 -8
- package/dist/refusals.d.ts.map +1 -1
- package/dist/refusals.js +141 -22
- package/dist/refusals.js.map +1 -1
- package/dist/scope-copy.d.ts +18 -0
- package/dist/scope-copy.d.ts.map +1 -1
- package/dist/scope-copy.js +18 -0
- package/dist/scope-copy.js.map +1 -1
- package/dist/scope-host.d.ts +266 -15
- package/dist/scope-host.d.ts.map +1 -1
- package/dist/scope-host.js +41 -0
- package/dist/scope-host.js.map +1 -1
- package/dist/spine-restore.d.ts +7 -2
- package/dist/spine-restore.d.ts.map +1 -1
- package/dist/spine-restore.js +13 -5
- package/dist/spine-restore.js.map +1 -1
- package/dist/system-switch-record.d.ts +290 -74
- package/dist/system-switch-record.d.ts.map +1 -1
- package/dist/system-switch-record.js +353 -101
- package/dist/system-switch-record.js.map +1 -1
- package/dist/system-switch.d.ts +146 -20
- package/dist/system-switch.d.ts.map +1 -1
- package/dist/system-switch.js +135 -27
- package/dist/system-switch.js.map +1 -1
- package/dist/timeline.d.ts +23 -0
- package/dist/timeline.d.ts.map +1 -1
- package/dist/timeline.js +30 -0
- package/dist/timeline.js.map +1 -1
- package/dist/ulid.d.ts +5 -0
- package/dist/ulid.d.ts.map +1 -1
- package/dist/ulid.js +9 -0
- package/dist/ulid.js.map +1 -1
- package/dist/vertical-events.d.ts +7 -1
- package/dist/vertical-events.d.ts.map +1 -1
- package/dist/vertical-events.js +14 -0
- package/dist/vertical-events.js.map +1 -1
- package/package.json +4 -4
package/dist/scope-host.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ModuleLog } from './module-log.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { DeliveryRefusal } from './delivery-refusal.js';
|
|
3
|
+
import type { OnBehalfOf, ExportReadInput, ExportedBatch, ImportBatch, ImportedEvent, ImportResult, ImportState, ImportCursorMove, ImportCursorMoved, AdminAction, BecomeCapabilityInput, CapabilityExchange, CapabilityFilter, CapabilityId, CapabilityPage, MintedCapability, ListPage, Connection, ConnectionFilter, ConnectionId, ConnectionGrant, ConnectionGrantRecord, ConnectionSecret, CreateConnectionInput, OpenConnection, ProjectedConnectionGrant, ProjectedConnectionKey, AccessLogEntry, DelegatedReadRecord, OwnerTransferAudit, CopyMarkAudit, BindHostnameInput, AdminLogEntry, OpsFailureEntry, PlatformRequestDrainTotalsEntry, SweepRunEntry, SweepRunKind, SweepRunOutcome, ModelUsageEntry, ModelUsageSummary, CapabilityGrant, CreateTenantInput, Decision, Instant, DomainEvent, DomainEventInput, PlatformRequestInput, PlatformRequestId, PlatformRequest, PlatformRequestFilter, PlatformRequestStatus, PlatformRequestFailure, EntitlementGrant, EntitlementGrantInput, EntitlementView, MeterReading, EntityRef, IdentityLink, IdentityPool, Jurisdiction, ModuleId, ModuleManifest, ScheduleSpec, SystemGrant, PeerGrantsStatusEntry, SystemGrantsStatusEntry, SystemSwitchRecord, SystemSwitch, SystemSwitchResult, PeerCoverage, PeerSwitch, PeerSwitchResult, VerticalCaller, VerticalResolution, CreateOrgInput, Node, Org, OrgId, OrgMembership, PermissionKey, PlatformActorId, ChannelName, ExportBreak, ChannelHistoryEntry, DnsRecord, HostnameBinding, HostnameStatus, PromotionAcknowledgement, BindAcknowledgement, PublishVersionInput, RegisterVerticalInput, VerticalServingState, RouteTarget, DirectoryDump, IdentityMembership, PrincipalId, ResolvedIdentity, RoleAssignment, RoleDefinition, QueryScopeInput, ReadScopeTableInput, Scope, ScopeDump, SubjectShredReceipt, ScopeQueryResult, ScopeId, ScopeStatus, ScopeTable, DenialFilter, DenialSummary, RefusalFilter, RefusalRecord, PermissionDenial, Coverage, BeginImpersonationInput, ImpersonationFilter, ImpersonationSession, ImpersonationSessionId, ScopeTablePage, StorageShape, Tenant, TenantId, TenantRole, TenantStoreHandle, AttachmentRecord, BlobStoreHandle, Visibility, Vertical, VerticalChannel, VerticalVersion, TenantStatus, Page, CountedPage, FreshnessSpec, EntityHistoryInput, EventFacetInput, EventCauseInput, EventEffectsInput, EffectsTree, InvocationEventsInput, InvocationEvents, DeadLettersInput, DeadLetter, LifecycleFlowInput, LifecycleFlowResult, OperationSeriesInput, OperationSeriesResult, CauseChain, EventFacetResult, HistoryEntry, ErrorCode, PlatformRequestFailureOrigin, IssueEntry, IssueStatus, IssueStatusInput, DeclaredMigration, CheckSubject } from '@substrat-run/contracts';
|
|
3
4
|
import type { ConnectionUseOutcome } from './connector-calls.js';
|
|
4
5
|
import type { CapabilityVerbs } from './capability.js';
|
|
5
6
|
import type { ModelUsageFilter, ModelUsageInput, ModelUsageWindow } from './model-usage.js';
|
|
@@ -593,6 +594,18 @@ export interface InvokeOptions {
|
|
|
593
594
|
* uncapped count, so a reader can tell a short list from a truncated one.
|
|
594
595
|
*/
|
|
595
596
|
readonly onEmitted?: (report: EmittedReport) => void;
|
|
597
|
+
/**
|
|
598
|
+
* Called after the operation COMMITS and its executors ran inline (#1184), with what each
|
|
599
|
+
* delivery of this call's events did. The inline path is the common case of K-22 §4.2,
|
|
600
|
+
* so the request holder is the one who can tell a person their accept was refused rather
|
|
601
|
+
* than report success for an effect that never happened.
|
|
602
|
+
*
|
|
603
|
+
* Every delivery the call's post-commit tail attempted, which can include an earlier
|
|
604
|
+
* call's retry that came due: a caller picks out its own by `eventType` and `entity`.
|
|
605
|
+
* Never called for a rolled-back operation, a read-only session or an
|
|
606
|
+
* idempotent replay. Absent from a host that predates it — read that as "not reported".
|
|
607
|
+
*/
|
|
608
|
+
readonly onExecutorOutcomes?: (outcomes: readonly ExecutorOutcome[]) => void;
|
|
596
609
|
}
|
|
597
610
|
/** How many of an invocation's own events `onEmitted` names (#1746). `total` is uncapped. */
|
|
598
611
|
export declare const EMITTED_REPORT_CAP = 20;
|
|
@@ -723,6 +736,48 @@ export type ConsumerHandler = (ctx: OperationContext, event: DomainEvent) => voi
|
|
|
723
736
|
* code, and the version you declared is the only one you will be handed.
|
|
724
737
|
*/
|
|
725
738
|
export type ImportHandler = (ctx: OperationContext, event: ImportedEvent) => void | Promise<void>;
|
|
739
|
+
/**
|
|
740
|
+
* One membership change for `HostAdmin.applyMembership` (#1184): a TENANT-level role assigned
|
|
741
|
+
* or taken away, and nothing else. No org is joined or left — what an org confers lives partly
|
|
742
|
+
* in each scope's own store, where no directory unit can bound it, so authorizing an org
|
|
743
|
+
* membership is its own capability.
|
|
744
|
+
*/
|
|
745
|
+
export type MembershipChange = {
|
|
746
|
+
tenantId: TenantId;
|
|
747
|
+
principal: PrincipalId;
|
|
748
|
+
/** The tenant-level role assigned or taken away. */
|
|
749
|
+
roleKey: string;
|
|
750
|
+
/**
|
|
751
|
+
* Whose authority bounds the write (§5.1) — the inviter of an add, the remover of a removal:
|
|
752
|
+
* the unit applies nothing unless this principal covers every permission `roleKey` carries,
|
|
753
|
+
* read inside the unit (`tenantCoverage`).
|
|
754
|
+
*/
|
|
755
|
+
boundedBy: PrincipalId;
|
|
756
|
+
} & ({
|
|
757
|
+
op: 'add';
|
|
758
|
+
/** Apply nothing when `principal`'s removal fence stands at or after this instant. */
|
|
759
|
+
unlessRemovedSince: Instant;
|
|
760
|
+
} | {
|
|
761
|
+
op: 'remove';
|
|
762
|
+
});
|
|
763
|
+
/**
|
|
764
|
+
* What the unit did: applied, or why not — fenced by a removal (an add only), or out of the
|
|
765
|
+
* bound. A removal that applied says whether it took anything (`changed`); one that took
|
|
766
|
+
* nothing still raised the fence.
|
|
767
|
+
*/
|
|
768
|
+
export type MembershipChangeResult = {
|
|
769
|
+
applied: true;
|
|
770
|
+
changed?: boolean;
|
|
771
|
+
} | {
|
|
772
|
+
applied: false;
|
|
773
|
+
removedAt: string;
|
|
774
|
+
} | {
|
|
775
|
+
applied: false;
|
|
776
|
+
missing: PermissionKey[];
|
|
777
|
+
} | {
|
|
778
|
+
applied: false;
|
|
779
|
+
unknownRole: string;
|
|
780
|
+
};
|
|
726
781
|
/**
|
|
727
782
|
* An **executor**: out-of-band host code that effects, outside a scope, what a module
|
|
728
783
|
* asked for inside one (K-22 §4.2; D-18's triage rule — effects on the outside world
|
|
@@ -742,8 +797,53 @@ export type ImportHandler = (ctx: OperationContext, event: ImportedEvent) => voi
|
|
|
742
797
|
* It receives `HostAdmin`, not `ctx`: it acts with platform authority, which is
|
|
743
798
|
* precisely what module code must never hold. Admin writes it makes are stamped with
|
|
744
799
|
* the causing event's id (`causedBy`), so the split trail joins.
|
|
800
|
+
*
|
|
801
|
+
* A handler that decides an event must never be effected RETURNS `refuseDelivery(reason)`
|
|
802
|
+
* (#1184). The delivery is journaled terminal with the reason, never retried. A return
|
|
803
|
+
* value rather than a thrown error, so module code cannot produce one: the most an
|
|
804
|
+
* operation can do is throw, and a throw is retried like any other failure.
|
|
745
805
|
*/
|
|
746
|
-
export type ExecutorHandler = (admin: HostAdmin, event: DomainEvent) => void | Promise<void>;
|
|
806
|
+
export type ExecutorHandler = (admin: HostAdmin, event: DomainEvent, scope: ExecutorScope) => void | DeliveryRefusal | Promise<void | DeliveryRefusal>;
|
|
807
|
+
/**
|
|
808
|
+
* The scope an executor's event came from, as the executor may read it (#1184).
|
|
809
|
+
*
|
|
810
|
+
* Here rather than through `HostAdmin` or the host, and the pure adapter is why: its
|
|
811
|
+
* handler runs INSIDE the scope's actor task, and every host read of a scope re-enqueues
|
|
812
|
+
* on that actor, so `host.canAssign` from a handler waits on the task holding it and never
|
|
813
|
+
* returns. The adapter builds this to suit where the handler runs, as it builds a
|
|
814
|
+
* connector's `openAttachment`.
|
|
815
|
+
*
|
|
816
|
+
* Reads only, and checks no permission: the caller is host code that already holds
|
|
817
|
+
* platform authority. Both answer about the event's own (tenant, scope), never another.
|
|
818
|
+
*/
|
|
819
|
+
export interface ExecutorScope {
|
|
820
|
+
/** One entity's history in this scope — `readHistory`'s answer, oldest first. */
|
|
821
|
+
history(entity: EntityRef, page?: ListPage): Promise<Page<HistoryEntry>>;
|
|
822
|
+
/**
|
|
823
|
+
* May `principal` confer `roleKey` at this scope's node, or at the tenant node
|
|
824
|
+
* (`level: 'tenant'`)? The K-21 set comparison `ctx.canAssign` answers, narrowing-aware,
|
|
825
|
+
* over the tenant's projected role. Throws `unknownRoleError` for a role this tenant does
|
|
826
|
+
* not define.
|
|
827
|
+
*/
|
|
828
|
+
covers(principal: PrincipalId, roleKey: string, level: 'scope' | 'tenant'): Promise<Coverage>;
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* What one executor delivery did inside the call that emitted its event (#1184) — what
|
|
832
|
+
* `InvokeOptions.onExecutorOutcomes` reports. `refused` is terminal by the handler's own
|
|
833
|
+
* decision; `dead-lettered` is terminal by exhaustion; `retrying` will be tried again.
|
|
834
|
+
*/
|
|
835
|
+
export interface ExecutorOutcome {
|
|
836
|
+
readonly executorId: string;
|
|
837
|
+
readonly eventId: string;
|
|
838
|
+
readonly eventType: string;
|
|
839
|
+
/** The event's entity, as `<entityType>:<entityId>` — how a caller finds its own event. */
|
|
840
|
+
readonly entity: string;
|
|
841
|
+
readonly outcome: 'delivered' | 'retrying' | 'dead-lettered' | 'refused' | 'inert' | 'routed';
|
|
842
|
+
/** The refusal reason, or the failure's message. Absent on a delivery. */
|
|
843
|
+
readonly error?: string;
|
|
844
|
+
}
|
|
845
|
+
/** One delivery's `ExecutorOutcome` — the one builder both adapters report through. */
|
|
846
|
+
export declare function executorOutcomeOf(executorId: string, event: DomainEvent, outcome: ExecutorOutcome['outcome'], error?: unknown): ExecutorOutcome;
|
|
747
847
|
/**
|
|
748
848
|
* How hard the host tries before it gives up on one delivery (#100).
|
|
749
849
|
*
|
|
@@ -846,7 +946,8 @@ export interface ScheduleRunReport {
|
|
|
846
946
|
* The module is switched off on this scope (#1666) — every `skipped` above is that,
|
|
847
947
|
* not a cadence window. Optional and absent otherwise, so a stored or pre-widening
|
|
848
948
|
* report stays valid. A module a PITR rewind holds off until its switch is back in the
|
|
849
|
-
* scope (#1819, Cloudflare only) reports the same.
|
|
949
|
+
* scope (#1819, Cloudflare only) reports the same. The system door refuses that module's
|
|
950
|
+
* other calls too, a job run's among them (#1834).
|
|
850
951
|
*/
|
|
851
952
|
switchedOff?: true;
|
|
852
953
|
/**
|
|
@@ -1432,6 +1533,10 @@ export interface HostAdmin {
|
|
|
1432
1533
|
* role that was never assigned (or already revoked) is a silent no-op. Takes a
|
|
1433
1534
|
* `PlatformActorId` like every admin mutation — the caller's own authority to do
|
|
1434
1535
|
* this is decided above the kernel (e.g. the dashboard's manage-members check).
|
|
1536
|
+
*
|
|
1537
|
+
* At the TENANT node it also raises the principal's removal fence (#1184), in the same
|
|
1538
|
+
* unit as the revoke and whether or not anything was held, so a membership-executor add
|
|
1539
|
+
* still on its way for them is refused. The no-op stays unaudited.
|
|
1435
1540
|
*/
|
|
1436
1541
|
unassignRole(actor: PlatformActorId, assignment: RoleAssignment): Promise<void>;
|
|
1437
1542
|
grant(actor: PlatformActorId, grant: CapabilityGrant): Promise<void>;
|
|
@@ -1471,8 +1576,10 @@ export interface HostAdmin {
|
|
|
1471
1576
|
* with `conflict`: restore is the lever; a grant is not. A grant that went through would
|
|
1472
1577
|
* hand the module's system authority back to a job run or a `getSystemScope` invoke
|
|
1473
1578
|
* while its schedules stayed off. Only `restoreToSystem` turns the switch back on.
|
|
1474
|
-
*
|
|
1475
|
-
*
|
|
1579
|
+
* A TENANT-level grant (`node.scopeId` null) is refused the same way while the directory
|
|
1580
|
+
* records the module off on any scope of the tenant (#1743). One that already existed when
|
|
1581
|
+
* a scope was switched off gives the module nothing there: the evaluator denies a
|
|
1582
|
+
* switched-off subject on its scope whatever it holds (#1823).
|
|
1476
1583
|
*/
|
|
1477
1584
|
grantToSystem(actor: PlatformActorId, grant: SystemGrant): Promise<void>;
|
|
1478
1585
|
/**
|
|
@@ -1489,7 +1596,8 @@ export interface HostAdmin {
|
|
|
1489
1596
|
* `restoreToSystem`;
|
|
1490
1597
|
* - anything acting with the module's system authority is denied by its own
|
|
1491
1598
|
* `ctx.check` — a resumable job run (#1577) fails its step, a `getSystemScope` invoke
|
|
1492
|
-
* is refused
|
|
1599
|
+
* is refused — including authority from a TENANT-level grant, which OFF cannot
|
|
1600
|
+
* tombstone: the evaluator reads the marker before any grant (#1823);
|
|
1493
1601
|
* - and nothing can hand that authority back: `grantToSystem` refuses, and a reconcile
|
|
1494
1602
|
* seats no `system:` grant for the module, not even one a newer version declares.
|
|
1495
1603
|
* `restoreToSystem` is the only way back.
|
|
@@ -1502,8 +1610,14 @@ export interface HostAdmin {
|
|
|
1502
1610
|
* atomic: this order fails toward "an intent with no outcome", never toward "a switch
|
|
1503
1611
|
* that moved with no row".
|
|
1504
1612
|
*
|
|
1505
|
-
* Throws `not_found` when the
|
|
1506
|
-
*
|
|
1613
|
+
* Throws `not_found` when the module has no authority reaching the scope at all — no
|
|
1614
|
+
* `system:<module>` grant on the scope and no live tenant-level one (#1823) — so a typo in
|
|
1615
|
+
* an emergency does not answer "done". A module whose only authority there is a
|
|
1616
|
+
* tenant-level grant IS switched: OFF writes the marker and tombstones nothing.
|
|
1617
|
+
*
|
|
1618
|
+
* The directory's record of the switch is written BEFORE the scope moves and taken back if
|
|
1619
|
+
* the move throws or holds nothing (#1823), so #1743's tenant-level refusal has no window to
|
|
1620
|
+
* miss. On a host that delegates to the deployment
|
|
1507
1621
|
* serving the scope, the write happens THERE and the audit rows HERE.
|
|
1508
1622
|
*/
|
|
1509
1623
|
revokeFromSystem(actor: PlatformActorId, input: SystemSwitch): Promise<SystemSwitchResult>;
|
|
@@ -1545,6 +1659,30 @@ export interface HostAdmin {
|
|
|
1545
1659
|
* `systemGrantsStatus` reports both side by side (`schedules` and `recorded`).
|
|
1546
1660
|
*/
|
|
1547
1661
|
listSystemSwitches(actor: PlatformActorId, filter?: SystemSwitchRecordFilter): Promise<SystemSwitchRecord[]>;
|
|
1662
|
+
/**
|
|
1663
|
+
* Which of these modules the tenant holds a live TENANT-level `system:` grant for (#1823).
|
|
1664
|
+
* A module whose only authority on a scope is such a grant has nothing in the scope's
|
|
1665
|
+
* storage, so a switch carried into a deployment (a hosted provision, reconcile or restore)
|
|
1666
|
+
* names these as `tenantHeld`, and the deployment's in-unit OFF holds them too. Order follows
|
|
1667
|
+
* `moduleIds`. Access-logged (K-24), as the fleet read is.
|
|
1668
|
+
*/
|
|
1669
|
+
tenantHeldSystemModules(actor: PlatformActorId, tenantId: TenantId, moduleIds: readonly ModuleId[]): Promise<ModuleId[]>;
|
|
1670
|
+
/**
|
|
1671
|
+
* The peer half of a carry (#2029): the peers the directory records OFF on this one scope
|
|
1672
|
+
* (`revokeFromPeer` writes the record, as `revokeFromSystem` writes the module's), and of those
|
|
1673
|
+
* the ones the tenant holds a live tenant-level `vertical:` grant for (#2030). Carried into a
|
|
1674
|
+
* deployment's provision, reconcile or restore beside the modules, so its in-unit OFF puts them
|
|
1675
|
+
* back off too. Empty lists when none is. Access-logged (K-24), as the module reads are.
|
|
1676
|
+
*/
|
|
1677
|
+
peerSwitchCarry(actor: PlatformActorId, node: {
|
|
1678
|
+
tenantId: TenantId;
|
|
1679
|
+
scopeId: ScopeId;
|
|
1680
|
+
}): Promise<{
|
|
1681
|
+
switchedOffPeers: string[];
|
|
1682
|
+
tenantHeldPeers: string[];
|
|
1683
|
+
/** #2045: each recorded-off peer's fence (the record's operation id), by slug. */
|
|
1684
|
+
fences: Record<string, string>;
|
|
1685
|
+
}>;
|
|
1548
1686
|
/**
|
|
1549
1687
|
* Put the directory's OFF positions back into one scope (#1674), for a scope whose storage
|
|
1550
1688
|
* lost them: a wipe then re-provision, or a restore of a dump taken before the switch was
|
|
@@ -1572,6 +1710,10 @@ export interface HostAdmin {
|
|
|
1572
1710
|
* deployment applying it. That move is undone (switched back on) and audited as
|
|
1573
1711
|
* `staleCarry`, BEFORE the record is read again for the OFF pass. Only a module recorded
|
|
1574
1712
|
* `on` is ever switched on here.
|
|
1713
|
+
*
|
|
1714
|
+
* Every kind of switch (#2029): after the modules, every peer the record holds `off` on the
|
|
1715
|
+
* scope is switched off again the same way (through `revokeFromPeer`'s switch), audited as
|
|
1716
|
+
* `reassertPeerSwitch`, and answered as an entry naming `vertical` rather than `moduleId`.
|
|
1575
1717
|
*/
|
|
1576
1718
|
reassertSystemSwitches(actor: PlatformActorId, node: {
|
|
1577
1719
|
tenantId: TenantId;
|
|
@@ -1635,6 +1777,21 @@ export interface HostAdmin {
|
|
|
1635
1777
|
revokeCapability(actor: PlatformActorId, tenantId: TenantId, scopeId: ScopeId, capabilityId: CapabilityId): Promise<void>;
|
|
1636
1778
|
grantToOrg(actor: PlatformActorId, orgId: OrgId, permission: PermissionKey, node: Node, entity?: EntityRef): Promise<void>;
|
|
1637
1779
|
addMember(actor: PlatformActorId, tenantId: TenantId, principal: PrincipalId, orgId: OrgId): Promise<void>;
|
|
1780
|
+
/**
|
|
1781
|
+
* One membership change, applied as ONE directory unit (#1184): a single SQLite transaction or
|
|
1782
|
+
* a single synchronous ControlPlaneDO method, with no await inside it. In that unit it
|
|
1783
|
+
* re-evaluates the bound — `boundedBy` must still cover every permission `roleKey` carries,
|
|
1784
|
+
* against the directory as it stands (`tenantCoverage`) — and only then writes. An ADD also
|
|
1785
|
+
* reads `principal`'s removal fence first, then assigns the TENANT-level role and writes its
|
|
1786
|
+
* audit row. A REMOVE unassigns it and raises the fence, as a tenant-level `unassignRole`
|
|
1787
|
+
* does. A removal, a grant, a role redefinition or a demotion lands wholly before the unit
|
|
1788
|
+
* (and governs it) or wholly after it, never between its check and its write.
|
|
1789
|
+
*
|
|
1790
|
+
* The fence is `_substrat_membership_fences`: every tenant-level `unassignRole` and every
|
|
1791
|
+
* `removeMember` raises it for the person, in the same unit as its revoke — a no-op included,
|
|
1792
|
+
* since a removal of someone whose add is still on its way must still win.
|
|
1793
|
+
*/
|
|
1794
|
+
applyMembership(actor: PlatformActorId, change: MembershipChange): Promise<MembershipChangeResult>;
|
|
1638
1795
|
/**
|
|
1639
1796
|
* Revoke a membership (K-21). **Tombstones, never deletes**: the tuple keeps its
|
|
1640
1797
|
* row, gains a `revokedAt`, and the permission walk skips it. Deletion would
|
|
@@ -1646,6 +1803,9 @@ export interface HostAdmin {
|
|
|
1646
1803
|
* no-op, and a no-op is not audited. Re-adding via `addMember` clears the
|
|
1647
1804
|
* tombstone (they are a member again); the add/revoke history lives in the admin
|
|
1648
1805
|
* log, which is append-only.
|
|
1806
|
+
*
|
|
1807
|
+
* Raises the principal's removal fence (#1184) in the same unit as the revoke, a no-op
|
|
1808
|
+
* included, so a membership-executor add still on its way for them is refused.
|
|
1649
1809
|
*/
|
|
1650
1810
|
removeMember(actor: PlatformActorId, tenantId: TenantId, principal: PrincipalId, orgId: OrgId): Promise<void>;
|
|
1651
1811
|
/**
|
|
@@ -2341,6 +2501,13 @@ export interface HostAdmin {
|
|
|
2341
2501
|
* Reads only the lifecycle field of unclassified payloads; logged like every read here.
|
|
2342
2502
|
*/
|
|
2343
2503
|
lifecycleFlow(actor: PlatformActorId, tenantId: TenantId, scopeId: ScopeId, input: LifecycleFlowInput): Promise<LifecycleFlowResult>;
|
|
2504
|
+
/**
|
|
2505
|
+
* Calls per `(entityType, operation)` per time bucket over the scope's outbox (#1750) —
|
|
2506
|
+
* `readOperationSeries`, hoisted: the business volumes Pulse draws. The caller passes
|
|
2507
|
+
* the pairs (from the running version's declared lifecycles); the scope holds no model.
|
|
2508
|
+
* Reads no payload; logged like every read here.
|
|
2509
|
+
*/
|
|
2510
|
+
operationSeries(actor: PlatformActorId, tenantId: TenantId, scopeId: ScopeId, input: OperationSeriesInput): Promise<OperationSeriesResult>;
|
|
2344
2511
|
/**
|
|
2345
2512
|
* One read-only SQL statement against the scope's database — the console the two
|
|
2346
2513
|
* table-shaped reads deliberately weren't (#219). User SQL DOES reach the DB here,
|
|
@@ -3871,6 +4038,12 @@ export interface ScopeHost {
|
|
|
3871
4038
|
* one enforcement path, `ctx.check` stays the single gate, no bypass. Events it
|
|
3872
4039
|
* emits are stamped `{ system: moduleId }`. `ctx.principal` carries the module id
|
|
3873
4040
|
* so the type holds, but it is **not a person**.
|
|
4041
|
+
*
|
|
4042
|
+
* **It is the one door for that authority** (#1834). A schedule's fire and a job run's
|
|
4043
|
+
* `pass.scope()` both act through it, and so does `getSystemAttachments`. On the hosted adapter it
|
|
4044
|
+
* also refuses (`forbidden`) a module a PITR rewind holds off (#1819), at every call it makes,
|
|
4045
|
+
* not only when it is opened: each call is pinned to the scope instance the door checked, and
|
|
4046
|
+
* a call landing on a restarted one (a rewound scope always is) is checked again first.
|
|
3874
4047
|
*/
|
|
3875
4048
|
getSystemScope(moduleId: ModuleId, tenantId: TenantId, scopeId: ScopeId): Promise<ScopeStub>;
|
|
3876
4049
|
/**
|
|
@@ -4116,12 +4289,19 @@ export interface ScopeHost {
|
|
|
4116
4289
|
* and this driver's contract in `job-run.ts`. `retry` is the DEFAULT policy for
|
|
4117
4290
|
* the job's steps; a step may pass its own.
|
|
4118
4291
|
*
|
|
4292
|
+
* `options.leaseMs` (#2034) is how long one pass holds its run without reaching a
|
|
4293
|
+
* step boundary, which renews it — default `JOB_LEASE_MS`. Past it, the run is due
|
|
4294
|
+
* again and the next drive takes it over, counting the silent pass as a failed
|
|
4295
|
+
* attempt. A job whose pass may go longer than that between two steps says so here.
|
|
4296
|
+
*
|
|
4119
4297
|
* The fourth driver, and a SIBLING of the three that already exist rather than a
|
|
4120
4298
|
* widening of any of them. An executor retries one delivery whole; a schedule
|
|
4121
4299
|
* fires one operation that must finish; the platform sweep does a pass of
|
|
4122
4300
|
* maintenance. None of them can stop halfway through an hour and carry on.
|
|
4123
4301
|
*/
|
|
4124
|
-
registerJob(moduleId: ModuleId, name: string, handler: JobHandler, retry?: ExecutorRetryPolicy
|
|
4302
|
+
registerJob(moduleId: ModuleId, name: string, handler: JobHandler, retry?: ExecutorRetryPolicy, options?: {
|
|
4303
|
+
leaseMs?: number;
|
|
4304
|
+
}): void;
|
|
4125
4305
|
/**
|
|
4126
4306
|
* Start a run, or JOIN the one already in flight for the same
|
|
4127
4307
|
* `(module, job, instance)` — the coalescing half of the driver.
|
|
@@ -4163,12 +4343,10 @@ export interface ScopeHost {
|
|
|
4163
4343
|
* grant gate here would STALL it silently rather than refuse it. The authority it
|
|
4164
4344
|
* exercises is checked where it is used, inside the operations its steps invoke.
|
|
4165
4345
|
*
|
|
4166
|
-
* **
|
|
4167
|
-
*
|
|
4168
|
-
*
|
|
4169
|
-
*
|
|
4170
|
-
* scope DO's alarm fires for its own), so the bound holds by construction; the full
|
|
4171
|
-
* argument, and what an overlap would actually cost, is in `job-run.ts`.
|
|
4346
|
+
* **Overlapping calls are safe** (#2034): each run is CLAIMED before its pass, with a
|
|
4347
|
+
* lease the pass renews at every step boundary, so two calls on one scope never run
|
|
4348
|
+
* one run's handler together. A run whose pass died is taken over once its lease
|
|
4349
|
+
* expires. The full argument is in `job-run.ts`.
|
|
4172
4350
|
*/
|
|
4173
4351
|
runDueJobs(tenantId: TenantId, scopeId: ScopeId, options?: {
|
|
4174
4352
|
maxPasses?: number;
|
|
@@ -4317,6 +4495,11 @@ export interface ScopeHost {
|
|
|
4317
4495
|
* changed at 14:02 is information about that row, so "the body was empty" is not a
|
|
4318
4496
|
* defence: the filter runs whether or not there is a payload to withhold.
|
|
4319
4497
|
*
|
|
4498
|
+
* A subscription may be narrowed `within` one entity (#1853), which only removes frames;
|
|
4499
|
+
* the one exception to the per-principal check is a `within` the vertical built with
|
|
4500
|
+
* `vouchedWithin`, whose subscriber is sent bare `LiveNudge` frames and nothing that
|
|
4501
|
+
* names an entity.
|
|
4502
|
+
*
|
|
4320
4503
|
* **Generic over the runtime's request and response, because the kernel names
|
|
4321
4504
|
* neither.** This package has one dependency and no DOM or workers lib
|
|
4322
4505
|
* (`docs/architecture/dependency-policy.md`), which is why `FetchLike` above describes
|
|
@@ -4351,8 +4534,58 @@ export interface LiveReadSurface<Req extends LiveUpgradeRequest = LiveUpgradeReq
|
|
|
4351
4534
|
principal: PrincipalId;
|
|
4352
4535
|
/** The upgrade request as it arrived, carried whole so the host reads its own headers. */
|
|
4353
4536
|
request: Req;
|
|
4537
|
+
/**
|
|
4538
|
+
* Narrow the feed to one entity and what hangs beneath it (#1853).
|
|
4539
|
+
*
|
|
4540
|
+
* A frame is then delivered only if its entity IS this one or reaches it upward
|
|
4541
|
+
* through live declared `parent` edges (what `ctx.link` / `ctx.relink` write) — the
|
|
4542
|
+
* walk `ctx.check` makes, rooted here instead of at a grant.
|
|
4543
|
+
*
|
|
4544
|
+
* - **An `EntityRef` narrows and nothing else.** The principal's own per-frame
|
|
4545
|
+
* `liveTargets` check still runs; `within` is ANDed with it, so it can only take
|
|
4546
|
+
* frames away. A screen watching one conversation passes it to stop hearing the
|
|
4547
|
+
* rest of the desk.
|
|
4548
|
+
* - **A `vouchedWithin(…)` value replaces the principal's check.** For a subscriber
|
|
4549
|
+
* confined by something other than a grant — a widget visitor holding a session
|
|
4550
|
+
* token the vertical has just redeemed. The vertical asserts access to the root,
|
|
4551
|
+
* the same trust it already extends in naming `principal`, and the scope's walk is
|
|
4552
|
+
* the whole filter. Such a subscriber receives `LiveNudge` frames only, which name
|
|
4553
|
+
* no event type and no entity.
|
|
4554
|
+
*/
|
|
4555
|
+
within?: EntityRef | VouchedWithin;
|
|
4354
4556
|
}): Promise<Res>;
|
|
4355
4557
|
}
|
|
4558
|
+
/** The brand only `vouchedWithin` can apply — a literal cannot type-check as one. */
|
|
4559
|
+
declare const vouchedBrand: unique symbol;
|
|
4560
|
+
/**
|
|
4561
|
+
* A `within` root the VERTICAL vouches the subscriber may watch, in place of the
|
|
4562
|
+
* principal's own grants (#1853). Built only by `vouchedWithin`.
|
|
4563
|
+
*/
|
|
4564
|
+
export interface VouchedWithin {
|
|
4565
|
+
readonly [vouchedBrand]: true;
|
|
4566
|
+
readonly entity: EntityRef;
|
|
4567
|
+
/** Why the vertical vouches — what it checked. Required, and kept on the subscription. */
|
|
4568
|
+
readonly because: string;
|
|
4569
|
+
}
|
|
4570
|
+
/**
|
|
4571
|
+
* Vouch that the subscriber may watch `entity` and everything beneath it, though the
|
|
4572
|
+
* principal it subscribes as holds no read on it.
|
|
4573
|
+
*
|
|
4574
|
+
* The one way to reach the replacing mode, so every use is greppable and none is an
|
|
4575
|
+
* accident: an object literal is refused by the type checker, and anything not built
|
|
4576
|
+
* here is refused by the host at run time (`isVouchedWithin`). Call it only after the
|
|
4577
|
+
* vertical itself has proven access — for ticket0's widget, by redeeming the session
|
|
4578
|
+
* token for the session it names.
|
|
4579
|
+
*
|
|
4580
|
+
* What it costs the subscriber is detail: a vouched feed carries `LiveNudge` frames, so
|
|
4581
|
+
* the scope never tells it which entity changed or how. Root it at an entity whose
|
|
4582
|
+
* subtree holds only what the subscriber may see — the walk is the whole filter.
|
|
4583
|
+
*/
|
|
4584
|
+
export declare function vouchedWithin(entity: EntityRef, opts: {
|
|
4585
|
+
because: string;
|
|
4586
|
+
}): VouchedWithin;
|
|
4587
|
+
/** Was this value built by `vouchedWithin`? A host asks before it drops the principal's check. */
|
|
4588
|
+
export declare function isVouchedWithin(value: unknown): value is VouchedWithin;
|
|
4356
4589
|
/**
|
|
4357
4590
|
* Is this request asking to be upgraded to a WebSocket?
|
|
4358
4591
|
*
|
|
@@ -4412,6 +4645,24 @@ export interface LiveChange {
|
|
|
4412
4645
|
/** When the event was emitted (ISO 8601), i.e. the emitting operation's instant. */
|
|
4413
4646
|
readonly at: string;
|
|
4414
4647
|
}
|
|
4648
|
+
/**
|
|
4649
|
+
* What a vouched subscriber is told (#1853): something beneath its root changed, and
|
|
4650
|
+
* nothing else.
|
|
4651
|
+
*
|
|
4652
|
+
* No event type and no entity, deliberately. A vouched subscriber holds no read on the
|
|
4653
|
+
* entities its frames are about — the vertical vouched for the ROOT, and the scope
|
|
4654
|
+
* cannot know which rows under it the vertical's own read would show. Naming the type
|
|
4655
|
+
* or the id would tell it what it never asked to read. The client re-reads, as it does
|
|
4656
|
+
* on a `LiveChange`.
|
|
4657
|
+
*/
|
|
4658
|
+
export interface LiveNudge {
|
|
4659
|
+
readonly kind: 'nudge';
|
|
4660
|
+
/** The event id, as on `LiveChange` — for ordering and de-duplication only. */
|
|
4661
|
+
readonly id: string;
|
|
4662
|
+
readonly at: string;
|
|
4663
|
+
}
|
|
4664
|
+
/** Any frame a live read sends. */
|
|
4665
|
+
export type LiveFrame = LiveChange | LiveNudge;
|
|
4415
4666
|
/**
|
|
4416
4667
|
* Refuse a cutoff in the future — at the HostAdmin boundary, not only at the HTTP door.
|
|
4417
4668
|
*
|