@substrat-run/adapter-sqlite 0.162.0 → 0.164.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.d.ts +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +214 -137
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -2,8 +2,8 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { closeSync, existsSync, fstatSync, mkdirSync, openSync, readdirSync, readFileSync, readSync, realpathSync, rmSync, writeFileSync, writeSync } from 'node:fs';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
4
|
import Database from 'better-sqlite3';
|
|
5
|
-
import { accessLogEntry, adminLogEntry, delegatedReadParams, operationSeriesCount, opsFailureEntry, opsFailureFingerprint, issueEntry, FRESHNESS_HEARTBEAT_MINUTES, sweepRunEntry, modelUsageEntry, ATTACHMENT_ADDED, ATTACHMENT_REMOVED, createTenantInput, domainEvent, domainEventInput, exportReadInput, importBatch, importCursorMove, exportsOfManifestJson, assertNoBindingShapeBreak, importsOfManifestJson, connectionLandingsOfManifestJson, entitlementGrant, entitlementGrantInput, eventId, meterReading, platformRequestInput, platformRequestId, identityLink, identityPool, instant, isSubstratError, connection, capabilityGrant, connectionGrant, connectionGrantRecord, connectionSecret, systemGrant, systemSwitch, systemSwitchRecord, peerSwitch, verticalCaller, verticalSlug as verticalSlugOf, subjectRef, requestFingerprint, createConnectionInput, connectLinkFilter, connectLinkKey, consumeConnectLinkInput, mintConnectLinkInput, projectedConnectionGrant, projectedConnectionKey, moduleManifest, createOrgInput, promotionAcknowledgement, bindAcknowledgement, bindHostnameInput, channelHistoryEntry, hostnameBinding, publishVersionInput, AUTO_ADMISSION_NOTE, routeTarget, ROUTE_SCOPE_HOSTNAMES_MAX, registerVerticalInput, vertical as verticalSchema, verticalChannel, verticalVersion, objectRef, entityObjectRef, assertModuleEmittableType, assertKernelAuthoredType, grantRefFromProof, org as orgSchema, orgMembership, principalId, resolvedIdentity, identityMembership, roleDefinition, scope as scopeSchema, tenant as tenantSchema, tenantRole, principalBecomeCapabilityInput, subjectShredReceipt, SCOPE_TABLE_PAGE_DEFAULT, SCOPE_TABLE_PAGE_MAX, SCOPE_QUERY_ROW_MAX, verticalServingState, deploymentWireAuth, callsOfManifestJson, outboundOfManifestJson, listLimitOf, pageOf, countedPageOf, SCOPE_GATE_REASONS, substratError, assertReplayableDump, delegatedReadRecord, ownerTransferAudit, memberChangeAudit, peerBindingAudit, connectionBindingAudit, copyMarkAudit, orphanSweepAudit, redrainEventsInput, REDRAIN_BATCH, errorCodeOf, } from '@substrat-run/contracts';
|
|
6
|
-
import { actorOf, asPrincipal, assertAllowed, assertNoSecret, assertPermissionKey, CAPABILITY_DDL, UPLOAD_LINK_DDL, createAttachmentVerbs, inspectUploadLink, redeemUploadLinkInTransaction, redeemOrReplayUploadLink, COPY_ORIGIN_DDL, ENTITY_STATE_MOVES_DDL, CAPABILITY_EXCHANGE_OPERATION, CAPABILITY_BECOME_MINT_OPERATION, becomeMintCheck, mintBecomeCapabilityAsPrincipal, revokeBecomeCapabilityAsPrincipal, readBecomeLinkStates, ENTITY_UNBIND_OPERATION, allowedFor, unbindEntityHolderBounded, capabilityAttachmentWriteRefused, capabilityTokenHash, createCapabilityVerbs, createEntityEdgeVerbs, createEntityStateVerbs, createTrashedReads, CONTINUATION_POSITION_CAP, visibleContinuation, searchStateWhere, uncheckedView, addStatePlans, addUpdatedStampPlans, watchUpdatedStamps, entityStateMigrations, assertNoCallerPurge, assertPlatformRequestRoom, isUnreachableParent, heldPurgePass, purgeDueOf, purgeHeldBy, lifecycleRefusal, scopeNotHeldRefusal, purgeOnlyKeysOf, purgeReportOf, purgeStillDue, refuseTrashedTarget, assertBoundOperations, checkHeldOperationsOf, countChecks, noteUncheckedCompletion, registerTrashTargets, runPurgePass, withheldKeysFor, statefulTablesOf, afterMigration, afterRuntimeDdl, derivesAnything, repairDerivedObjects, StateColumnLost, exchangeCapability as exchangeCapabilitySecret, guardSecrets, mintBecomeCapability, plausibleSessionToken, readCapabilityPage, redactSecrets, redactSecretText, moduleLog, consoleLogSink, asyncInvocationId, asyncLinePass, consoleInvocationLineSink, resolveCapabilitySession, revokeCapabilityAsPlatform, assertReadOnlyQuery, attachmentBlobKey, attachmentSha256, isSizedByteStream, knownLengthStream, openAttachmentByteStream, putAttachmentBytes, entitlementDenial, requiredEntitlementFor, foldMeterReading, guardSpine, guardSqlLimits, DO_SQL_LIMITS, tooManyResultColumns, tooManyTableColumns, parseValidationRecords, readableScopes, resolveScopeRecord, ulid, ISSUE_RETENTION_DAYS, telemetryRetentionStatements, FINDINGS_DDL, ATTACHMENT_BYTES_SQL, SCOPE_STORAGE_DDL, forgetScopeStorage, listScopeAttachmentStorageRows, retireScopeStorage, listScopeStorageAttemptRows, listScopeStorageRows, listScopeStoreStorageRows, pruneScopeStorageRows, recordScopeStorageRows, REQUEST_ROLLUP_DDL, listRequestDayRows, listRequestRollupDayRows, pruneRequestDayRows, recordRequestDayRows, METER_DAYS_DDL, VERTICAL_OWNERS_BACKFILL, VERTICAL_OWNERS_DDL, closeVerticalOwnerRow, listVerticalOwnerRows, openVerticalOwnerRow, verticalOwnerNow, listMeterDayRows, listMeterDayTakeRows, pruneMeterDayRows, recordMeterDayRows, CONNECT_LINKS_DDL, consumeConnectLinkRow, insertConnectLink, listConnectLinks as listConnectLinkRows, readConnectLink, restoreConnectLinkRow, revokeConnectLinkRow, createFindingRule, findingOfOpsFailure, findingOfSweepRun, pruneFindings, listFindingRules, listFindings, observeFinding, revokeFindingRule, setFindingStatus, assertRowLimit, assertRowOffset, OPS_FAILURE_RETENTION_DAYS, SWEEP_RUN_RETENTION_DAYS, SWEEP_RUNS_INTENT_INDEX, sweepRunsIntentHasKind, MODEL_USAGE_RETENTION_DAYS, foldModelUsage, backoffAt, platformRequestHistoryQuery, platformRequestOf, PLATFORM_REQUEST_COLUMNS, PLATFORM_REQUEST_REDACTION_SQL, platformRequestRedactionParams, platformRequestRedactionQuery, intentPayloadCarriesSubject, redactSubjectJobRuns, eraseSubjectFromModules, moduleRowsErased, moduleErasurePlan, redactSubjectScopeText, redactSubjectDirectoryText, ISSUE_EXEMPLAR_OWNER_BACKFILL_SQL, issueExemplarOwner, 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_STEP_RECORD_SQL, JOB_LEASE_EXPIRED_NOTE, assertLeaseMs, DELIVERY_ERROR_REDACTION_SQL, REDACTED_DELIVERY_NOTE, seatScopeTuple, applyScopeRoleChange, changeScopeRole, revokeScopeRoles, scopeRoleHolders, assertModuleDeclared, noteDeclaredKeys, noteReservedKeys, dumpRoleRefusal, reservedGrantRefusal, reservedRoleDefinitionRefusal, reservedRoleRefusal, reservedShapeRefusal, SCOPE_SEATED_ROLES_SQL, SEAT_HOLDING_SCOPE_STATUSES, TENANT_SEATED_ROLES_SQL, admitPeer, boundToPeerDeclarations, peerCallerAt, collectPeers, peerSeats, resolveVerticalInstanceFrom, resolvePeerInstanceFrom, peerVerticalSpellings, samePeerVertical, peerGrantsStatus, systemGrantsStatus, systemScheduleState, systemSwitchedOff, systemSwitchedOffMessage, tenantSystemSwitchedOffMessage, PEER_SWITCHES_DDL, PEER_BINDINGS_DDL, SWITCH_OWED_DDL, SWITCH_FENCES_DDL, TABLE_OWNERS_DDL, assertMigrationLeavesLedgerAlone, recordOwnershipSteps, runMigrationStatements, executableSqlStatements, blankSqlComments, recordWriteSuperseded, switchFencesOf, switchSupersededMessage, SWITCH_KINDS, SYSTEM_SWITCHES_DDL, forgetSwitchesOf, listSystemSwitchRecords, moveSwitch, reassertActionOf, reassertEntry, reassertOffRow, recordSwitchedOff, recordSwitchedOn, restoreSwitchRecord, scopesSwitchedOffFor, switchActionOf, switchAuditSubject, recordAuditOutcome, auditedCapabilityRevoke, auditWarningOf, switchNotFoundMessage, switchRecordsOf, switchedOffOf, switchesBackfillSqlOf, switchesTableExists, tenantHeldOf, tenantHoldsGrant, inUnitMovesToAudit, staleCarryRevertRow, staleCarryReverts, withRecorded, VERSION_MIGRATIONS_DDL, VERTICAL_SCOPE_STORES_DDL, forgetVerticalScopeStores, DEPLOYMENT_WIRE_AUTH_DDL, assertWireAuthFilter, deploymentWireAuthOf, forgetVerticalWireAuth, wireAuthAccessTarget, writeDeploymentWireAuth, indexVersionScopeStores, driverDeclarationsOf, verticalScopeStoresOf, STORE_DRIVER_RELEASES_DDL, legacyStoreDriverConflict, legacyStoreDriverRenames, beginStoreDriverRolloutIn, finishStoreDriverRolloutIn, holdStoreDriverIn, advanceStoreDriverRebuildIn, claimStoreDriverRebuildIn, storeDriverRebuildIn, recordStoreDriverReleaseIn, releaseStoreDriverHoldIn, storeDriverReleaseIn, storeDriverStateIn, storeDriverRolloutPendingIn, storeDriverRolloutWalkedIn, splitManifestMigrations, splitVersionMigrationsBatch, versionMigrationsOf, deriveVersionMigrationDigest, publishedMigrationDigest, migrationChangeRefusal, writeVersionMigrations, denialListQuery, refusalListQuery, mapRefusalRow, denialSummaryQuery, denialTotalsQuery, DENIAL_WINDOW_QUERY, mapDenialRow, mapDenialSummaryBuckets, IMPERSONATION_COLUMNS, IMPERSONATION_DDL, ImpersonationRefused, assertImpersonationWrites, assertModuleEnqueueableKind, assertSessionUsable, impersonationByIdQuery, impersonationListQuery, impersonationRowValues, impersonationStampOf, mapImpersonationRow, newImpersonationSession, resolveRetryPolicy, executorOutcomeOf, isDeliveryRefusal, refusalJournalText, membershipFencesBackfillSql, MEMBERSHIP_FENCES_DDL, membershipFencesTableExists, MEMBERSHIP_FENCE_SINCE_SQL, RAISE_MEMBERSHIP_FENCE_SQL, memberAddedAudit, orgChangeBound, tenantCoverage, isSecretBoxConfigured, unconfiguredSecretBox, createSubjectKeys, generateSealingKeyPair, noSealingKeyMessage, openSealed, sealTo, ConnectionSealingKeyUnavailableError, PermissionDenied, createAtomic, NotSearchable, NotListable, listIndexMigrations, listIndexPlans, listQuery, cursorOf, moduleMigrations, MIGRATION_DIGEST_FENCE_DDL, MIGRATION_DIGEST_MARK_LEGACY, assertJournalDumpCoherent, assertMigrationSql, migrationDivergence, migrationFailedError, migrationSetDivergence, migrationSteps, planMigrations, isSearchIndexTable, searchIndexMigrations, searchIndexPlans, searchLimit, searchMatchExpression, searchQuery, ATTACHMENT_TEXT_DDL, ATTACHMENT_CONDEMNED_DDL, ATTACHMENT_SWEEP_LICENCE_DDL, ATTACHMENT_SWEEP_LICENCE_TABLE, clearOrphanSweepLicence, condemnLicensedOrphanAttachments, consumeOrphanSweepLicence, licensedOrphanIds, recordOrphanSweepLicence, refuseUnlicensedOrphanSweep, fenceAttachmentSweep, refuseCondemnedAttachment, refuseCondemnedInDump, rowlessAttachmentIds, sweepOrphanAttachments, missingAttachmentStore, SETTLED_COPY_STATES, unsettledCopyRefusal, assertAttachmentExtractors, resolveAttachmentTextBounds, assertJobRegistrable, attachmentRecordOfRow, attachmentTextJob, enqueueAttachmentText, ATTACHMENT_TEXT_BACKFILL_JOB, ATTACHMENT_TEXT_JOB, kernelJobFor, queueAttachmentTextBackfill, startAttachmentTextBackfill, attachmentTextBackfillJob, reconcileAttachmentText, recordAttachmentText, searchAttachments, 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, emittedSinceQuery, emittedReportOf, EMITTED_REPORT_CAP, exportedSinceQuery, IMPORT_CURSOR_ADVANCE_SQL, IMPORT_RECORD_SQL, moveImportCursor, importCursorSourceOf, exportBreaksOf, exportBreakRefusal, assertPromoteReviewCurrent, promoteRaceRefusal, servingRaceRefusal, rebindRaceRefusal, SERVES_NOW_CONDITION, SERVES_NOW_PREDICATE, servesNowParams, bindExportBreaksOf, bindExportBreakRefusal, scopeMoveOf, runningVersionOf, isPrimaryScope, PROVISIONED_VERSIONS_SQL, installsReconciledOf, strandJudgmentsOf, provisionedVersionsParams, boundShapesColumn, boundShapesOfColumn, installReconciled, CrossVerticalRegistry, exportReadPlan, exportReadQuery, planExportBatch, withheldNote, JOB_RUN_DDL, JOB_RUN_DUE_AT, jobRunListLimit, jobRunOf, runDueJobRuns, startJobRun, IDEMPOTENCY_DDL, REFUSALS_TABLE_DDL, REFUSALS_INDEX, refusalInsert, refusalOf, markGuardRefusal, REFUSALS_REBUILD, refusalsAdmitGuards, assertIdempotencyKey, idempotencyLookupQuery, idempotencyPruneStatement, idempotencyRecordStatement, idempotencyOptedOutMessage, replayFor, globalFetch, fetchLikeOf, deadlineFetch, createUlid, facetEvents, readHistory, walkEventCause, walkEventEffects, assertRedrainWindow, readInvocation, readDeadLetters, readLifecycleFlow, readOperationSeries, domainEventOf, readUndrainedOutbox, undrainedEventsOf, connectorCallRecord, noopConnectorCallRecorder, recordConnectorCall, settleConnectionUse, unknownRoleError, callReservedStoreOperation, callScopeStore, observeStoreCall, replayStoreErasures, scopeStoreLifecycleOf, scopeStoreRunnersOf, } from '@substrat-run/kernel';
|
|
5
|
+
import { accessLogEntry, adminLogEntry, delegatedReadParams, operationSeriesCount, opsFailureEntry, opsFailureFingerprint, issueEntry, FRESHNESS_HEARTBEAT_MINUTES, sweepRunEntry, modelUsageEntry, ATTACHMENT_ADDED, ATTACHMENT_REMOVED, createTenantInput, domainEvent, domainEventInput, exportReadInput, importBatch, importCursorMove, exportsOfManifestJson, assertNoBindingShapeBreak, importsOfManifestJson, connectionLandingsOfManifestJson, connectorsOfManifestJson, entitlementGrant, entitlementGrantInput, eventId, meterReading, platformRequestInput, platformRequestId, identityLink, identityPool, instant, isSubstratError, connection, capabilityGrant, connectionGrant, connectionGrantRecord, connectionSecret, systemGrant, systemSwitch, systemSwitchRecord, peerSwitch, verticalCaller, verticalSlug as verticalSlugOf, subjectRef, requestFingerprint, createConnectionInput, connectLinkFilter, connectLinkKey, consumeConnectLinkInput, mintConnectLinkInput, projectedConnectionGrant, projectedConnectionKey, moduleManifest, createOrgInput, promotionAcknowledgement, bindAcknowledgement, bindHostnameInput, channelHistoryEntry, hostnameBinding, publishVersionInput, AUTO_ADMISSION_NOTE, routeTarget, ROUTE_SCOPE_HOSTNAMES_MAX, registerVerticalInput, vertical as verticalSchema, verticalChannel, verticalVersion, objectRef, entityObjectRef, assertModuleEmittableType, assertKernelAuthoredType, grantRefFromProof, org as orgSchema, orgMembership, principalId, resolvedIdentity, identityMembership, roleDefinition, scope as scopeSchema, tenant as tenantSchema, tenantRole, principalBecomeCapabilityInput, subjectShredReceipt, SCOPE_TABLE_PAGE_DEFAULT, SCOPE_TABLE_PAGE_MAX, SCOPE_QUERY_ROW_MAX, verticalServingState, deploymentWireAuth, callsOfManifestJson, outboundOfManifestJson, listLimitOf, pageOf, countedPageOf, SCOPE_GATE_REASONS, substratError, assertReplayableDump, delegatedReadRecord, ownerTransferAudit, memberChangeAudit, peerBindingAudit, connectionBindingAudit, copyMarkAudit, orphanSweepAudit, redrainEventsInput, REDRAIN_BATCH, errorCodeOf, } from '@substrat-run/contracts';
|
|
6
|
+
import { actorOf, asPrincipal, assertAllowed, assertNoSecret, assertPermissionKey, CAPABILITY_DDL, UPLOAD_LINK_DDL, createAttachmentVerbs, inspectUploadLink, redeemUploadLinkInTransaction, redeemOrReplayUploadLink, COPY_ORIGIN_DDL, ENTITY_STATE_MOVES_DDL, CAPABILITY_EXCHANGE_OPERATION, CAPABILITY_BECOME_MINT_OPERATION, becomeMintCheck, mintBecomeCapabilityAsPrincipal, revokeBecomeCapabilityAsPrincipal, readBecomeLinkStates, ENTITY_UNBIND_OPERATION, allowedFor, unbindEntityHolderBounded, capabilityAttachmentWriteRefused, capabilityTokenHash, createCapabilityVerbs, createEntityEdgeVerbs, createEntityStateVerbs, createTrashedReads, CONTINUATION_POSITION_CAP, visibleContinuation, searchStateWhere, uncheckedView, addStatePlans, addUpdatedStampPlans, watchUpdatedStamps, entityStateMigrations, assertNoCallerPurge, assertPlatformRequestRoom, isUnreachableParent, heldPurgePass, purgeDueOf, purgeHeldBy, lifecycleRefusal, scopeNotHeldRefusal, purgeOnlyKeysOf, purgeReportOf, purgeStillDue, refuseTrashedTarget, assertBoundOperations, checkHeldOperationsOf, countChecks, noteUncheckedCompletion, registerTrashTargets, runPurgePass, withheldKeysFor, statefulTablesOf, afterMigration, afterRuntimeDdl, derivesAnything, repairDerivedObjects, StateColumnLost, exchangeCapability as exchangeCapabilitySecret, guardSecrets, mintBecomeCapability, plausibleSessionToken, readCapabilityPage, redactSecrets, redactSecretText, moduleLog, consoleLogSink, asyncInvocationId, asyncLinePass, consoleInvocationLineSink, resolveCapabilitySession, revokeCapabilityAsPlatform, assertReadOnlyQuery, attachmentBlobKey, attachmentSha256, isSizedByteStream, knownLengthStream, openAttachmentByteStream, putAttachmentBytes, entitlementDenial, requiredEntitlementFor, foldMeterReading, guardSpine, guardSqlLimits, DO_SQL_LIMITS, tooManyResultColumns, tooManyTableColumns, parseValidationRecords, readableScopes, resolveScopeRecord, ulid, ISSUE_RETENTION_DAYS, telemetryRetentionStatements, FINDINGS_DDL, ATTACHMENT_BYTES_SQL, SCOPE_STORAGE_DDL, forgetScopeStorage, listScopeAttachmentStorageRows, retireScopeStorage, listScopeStorageAttemptRows, listScopeStorageRows, listScopeStoreStorageRows, pruneScopeStorageRows, recordScopeStorageRows, REQUEST_ROLLUP_DDL, listRequestDayRows, listRequestRollupDayRows, pruneRequestDayRows, recordRequestDayRows, METER_DAYS_DDL, VERTICAL_OWNERS_BACKFILL, VERTICAL_OWNERS_DDL, closeVerticalOwnerRow, listVerticalOwnerRows, openVerticalOwnerRow, verticalOwnerNow, listMeterDayRows, listMeterDayTakeRows, pruneMeterDayRows, recordMeterDayRows, CONNECT_LINKS_DDL, consumeConnectLinkRow, insertConnectLink, listConnectLinks as listConnectLinkRows, readConnectLink, restoreConnectLinkRow, revokeConnectLinkRow, createFindingRule, findingOfOpsFailure, findingOfSweepRun, pruneFindings, listFindingRules, listFindings, observeFinding, revokeFindingRule, setFindingStatus, assertRowLimit, assertRowOffset, OPS_FAILURE_RETENTION_DAYS, SWEEP_RUN_RETENTION_DAYS, SWEEP_RUNS_INTENT_INDEX, sweepRunsIntentHasKind, MODEL_USAGE_RETENTION_DAYS, foldModelUsage, backoffAt, platformRequestHistoryQuery, platformRequestOf, PLATFORM_REQUEST_COLUMNS, PLATFORM_REQUEST_REDACTION_SQL, platformRequestRedactionParams, platformRequestRedactionQuery, intentPayloadCarriesSubject, redactSubjectJobRuns, eraseSubjectFromModules, moduleRowsErased, moduleErasurePlan, redactSubjectScopeText, redactSubjectDirectoryText, ISSUE_EXEMPLAR_OWNER_BACKFILL_SQL, issueExemplarOwner, 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_STEP_RECORD_SQL, JOB_LEASE_EXPIRED_NOTE, assertLeaseMs, DELIVERY_ERROR_REDACTION_SQL, REDACTED_DELIVERY_NOTE, seatScopeTuple, applyScopeRoleChange, changeScopeRole, revokeScopeRoles, scopeRoleHolders, assertModuleDeclared, noteDeclaredKeys, noteReservedKeys, dumpRoleRefusal, reservedGrantRefusal, reservedRoleDefinitionRefusal, reservedRoleRefusal, reservedShapeRefusal, SCOPE_SEATED_ROLES_SQL, SEAT_HOLDING_SCOPE_STATUSES, TENANT_SEATED_ROLES_SQL, admitPeer, boundToPeerDeclarations, peerCallerAt, collectPeers, peerSeats, resolveVerticalInstanceFrom, resolvePeerInstanceFrom, peerVerticalSpellings, samePeerVertical, peerGrantsStatus, systemGrantsStatus, systemScheduleState, systemSwitchedOff, systemSwitchedOffMessage, tenantSystemSwitchedOffMessage, PEER_SWITCHES_DDL, PEER_BINDINGS_DDL, SWITCH_OWED_DDL, SWITCH_FENCES_DDL, TABLE_OWNERS_DDL, assertMigrationLeavesLedgerAlone, recordOwnershipSteps, runMigrationStatements, executableSqlStatements, blankSqlComments, recordWriteSuperseded, switchFencesOf, switchSupersededMessage, SWITCH_KINDS, SYSTEM_SWITCHES_DDL, forgetSwitchesOf, listSystemSwitchRecords, moveSwitch, reassertActionOf, reassertEntry, reassertOffRow, recordSwitchedOff, recordSwitchedOn, restoreSwitchRecord, scopesSwitchedOffFor, switchActionOf, switchAuditSubject, recordAuditOutcome, auditedCapabilityRevoke, auditWarningOf, switchNotFoundMessage, switchRecordsOf, switchedOffOf, switchesBackfillSqlOf, switchesTableExists, tenantHeldOf, tenantHoldsGrant, inUnitMovesToAudit, staleCarryRevertRow, staleCarryReverts, withRecorded, VERSION_MIGRATIONS_DDL, VERTICAL_SCOPE_STORES_DDL, forgetVerticalScopeStores, DEPLOYMENT_WIRE_AUTH_DDL, assertWireAuthFilter, deploymentWireAuthOf, forgetVerticalWireAuth, wireAuthAccessTarget, writeDeploymentWireAuth, indexVersionScopeStores, driverDeclarationsOf, verticalScopeStoresOf, STORE_DRIVER_RELEASES_DDL, legacyStoreDriverConflict, legacyStoreDriverRenames, beginStoreDriverRolloutIn, finishStoreDriverRolloutIn, holdStoreDriverIn, advanceStoreDriverRebuildIn, claimStoreDriverRebuildIn, storeDriverRebuildIn, recordStoreDriverReleaseIn, releaseStoreDriverHoldIn, storeDriverReleaseIn, storeDriverStateIn, storeDriverRolloutPendingIn, storeDriverRolloutWalkedIn, splitManifestMigrations, splitVersionMigrationsBatch, versionMigrationsOf, deriveVersionMigrationDigest, publishedMigrationDigest, migrationChangeRefusal, writeVersionMigrations, denialListQuery, refusalListQuery, mapRefusalRow, denialSummaryQuery, denialTotalsQuery, DENIAL_WINDOW_QUERY, mapDenialRow, mapDenialSummaryBuckets, IMPERSONATION_COLUMNS, IMPERSONATION_DDL, ImpersonationRefused, assertImpersonationWrites, assertModuleEnqueueableKind, assertSessionUsable, impersonationByIdQuery, impersonationListQuery, impersonationRowValues, impersonationStampOf, mapImpersonationRow, newImpersonationSession, resolveRetryPolicy, executorOutcomeOf, isDeliveryRefusal, refusalJournalText, membershipFencesBackfillSql, MEMBERSHIP_FENCES_DDL, membershipFencesTableExists, MEMBERSHIP_FENCE_SINCE_SQL, RAISE_MEMBERSHIP_FENCE_SQL, memberAddedAudit, orgChangeBound, tenantCoverage, isSecretBoxConfigured, unconfiguredSecretBox, createSubjectKeys, generateSealingKeyPair, noSealingKeyMessage, openSealed, sealTo, ConnectionSealingKeyUnavailableError, PermissionDenied, createAtomic, NotSearchable, NotListable, listIndexMigrations, listIndexPlans, listQuery, cursorOf, moduleMigrations, MIGRATION_DIGEST_FENCE_DDL, MIGRATION_DIGEST_MARK_LEGACY, assertJournalDumpCoherent, assertMigrationSql, migrationDivergence, migrationFailedError, migrationSetDivergence, migrationSteps, planMigrations, isSearchIndexTable, searchIndexMigrations, searchIndexPlans, searchLimit, searchMatchExpression, searchQuery, ATTACHMENT_TEXT_DDL, ATTACHMENT_CONDEMNED_DDL, ATTACHMENT_SWEEP_LICENCE_DDL, ATTACHMENT_SWEEP_LICENCE_TABLE, clearOrphanSweepLicence, condemnLicensedOrphanAttachments, consumeOrphanSweepLicence, licensedOrphanIds, recordOrphanSweepLicence, refuseUnlicensedOrphanSweep, fenceAttachmentSweep, refuseCondemnedAttachment, refuseCondemnedInDump, rowlessAttachmentIds, sweepOrphanAttachments, missingAttachmentStore, SETTLED_COPY_STATES, unsettledCopyRefusal, assertAttachmentExtractors, resolveAttachmentTextBounds, assertJobRegistrable, attachmentRecordOfRow, attachmentTextJob, enqueueAttachmentText, ATTACHMENT_TEXT_BACKFILL_JOB, ATTACHMENT_TEXT_JOB, kernelJobFor, queueAttachmentTextBackfill, startAttachmentTextBackfill, attachmentTextBackfillJob, reconcileAttachmentText, recordAttachmentText, searchAttachments, 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, emittedSinceQuery, emittedReportOf, EMITTED_REPORT_CAP, exportedSinceQuery, IMPORT_CURSOR_ADVANCE_SQL, IMPORT_RECORD_SQL, moveImportCursor, importCursorSourceOf, exportBreaksOf, exportBreakRefusal, assertPromoteReviewCurrent, promoteRaceRefusal, servingRaceRefusal, autoAdmittedProdRefusal, listedPromoteRefusal, rebindRaceRefusal, SERVES_NOW_CONDITION, SERVES_NOW_PREDICATE, servesNowParams, migratesFrom, servedFromRow, servesAsFrom, bindExportBreaksOf, bindExportBreakRefusal, scopeMoveOf, runningVersionOf, isPrimaryScope, PROVISIONED_VERSIONS_SQL, installsReconciledOf, strandJudgmentsOf, provisionedVersionsParams, boundShapesColumn, boundShapesOfColumn, installReconciled, CrossVerticalRegistry, exportReadPlan, exportReadQuery, planExportBatch, withheldNote, WIRE_NONCE_DDL, WIRE_NONCE_FULL, claimWireNonceIn, isWireNonceTable, JOB_RUN_DDL, JOB_RUN_DUE_AT, jobRunListLimit, jobRunOf, runDueJobRuns, startJobRun, IDEMPOTENCY_DDL, REFUSALS_TABLE_DDL, REFUSALS_INDEX, refusalInsert, refusalOf, markGuardRefusal, REFUSALS_REBUILD, refusalsAdmitGuards, assertIdempotencyKey, idempotencyLookupQuery, idempotencyPruneStatement, idempotencyRecordStatement, idempotencyOptedOutMessage, replayFor, globalFetch, fetchLikeOf, deadlineFetch, createUlid, facetEvents, readHistory, walkEventCause, walkEventEffects, assertRedrainWindow, readInvocation, readDeadLetters, readLifecycleFlow, readOperationSeries, domainEventOf, readUndrainedOutbox, undrainedEventsOf, connectorCallRecord, noopConnectorCallRecorder, recordConnectorCall, settleConnectionUse, unknownRoleError, callReservedStoreOperation, callScopeStore, observeStoreCall, replayStoreErasures, scopeStoreLifecycleOf, scopeStoreRunnersOf, } from '@substrat-run/kernel';
|
|
7
7
|
import { attributedView, delegatedRevokeSql, writeExplicitTupleIn, grantEntityShapeIn, shapeTopUpBatch, topUpEntityGrantShapes, settlePlatformRequestIn, } from '@substrat-run/kernel';
|
|
8
8
|
import { ADMIN_LOG_INDEXES_SQL, SETTLE_INTENT_SQL, SETTLE_OUTCOME_SQL, settleOutcomeParamsOf, readAuditedOperations, unknownOutcomeOf, } from '@substrat-run/kernel';
|
|
9
9
|
import { INERT_SCOPE_REASON, isPrimaryScopeRow, normalizeUploadMediaType } from '@substrat-run/kernel';
|
|
@@ -343,6 +343,10 @@ const KERNEL_DDL = `
|
|
|
343
343
|
-- only) and its watermark per producer, plus the producer-side (type, id) outbox index the
|
|
344
344
|
-- export read seeks on. Shared with the DO adapter from @substrat-run/kernel.
|
|
345
345
|
${VERTICAL_EVENTS_DDL}
|
|
346
|
+
-- RFC wire-signing §4: the nonces of signed calls this scope accepted, and their count.
|
|
347
|
+
-- Store-local, never dumped. Shared with the DO adapter from @substrat-run/kernel, where the
|
|
348
|
+
-- column comments are (its time is integer seconds, the envelope's own unit).
|
|
349
|
+
${WIRE_NONCE_DDL}
|
|
346
350
|
CREATE TABLE IF NOT EXISTS _substrat_migrations (
|
|
347
351
|
module_id TEXT NOT NULL,
|
|
348
352
|
version TEXT NOT NULL,
|
|
@@ -2889,7 +2893,9 @@ export class SqliteScopeHost {
|
|
|
2889
2893
|
// A dump written before indexes were excluded may still carry them; skip those
|
|
2890
2894
|
// too rather than failing a restore over data that is about to be recomputed.
|
|
2891
2895
|
// Matched without case, as SQLite resolves a table name (#1883 review).
|
|
2892
|
-
|
|
2896
|
+
// The wire nonce store is this store's own (RFC wire-signing §4): a dump that carries it is not
|
|
2897
|
+
// replayed, so a load starts it empty, as it starts a vertical's search index.
|
|
2898
|
+
const replayable = dumped.filter((t) => !isSearchIndexTable(t.name.toLowerCase()) && !isWireNonceTable(t.name));
|
|
2893
2899
|
// A Durable Object's dump carries spine tables this adapter keeps in its directory, or does
|
|
2894
2900
|
// not keep at all; they are skipped by name, and any other unknown spine table is refused.
|
|
2895
2901
|
const loadable = replayable.filter((t) => !DO_SCOPE_ONLY_SPINE_TABLES.has(t.name.toLowerCase()));
|
|
@@ -3743,6 +3749,13 @@ export class SqliteScopeHost {
|
|
|
3743
3749
|
throw substratError('not_found', `unknown version ${versionId} for vertical '${slug}'`);
|
|
3744
3750
|
return connectionLandingsOfManifestJson(manifest);
|
|
3745
3751
|
}
|
|
3752
|
+
/** What one version of `slug` declares about vertical-defined connectors (vertical-connectors.md §6.4); `not_found` for a version it does not know. */
|
|
3753
|
+
async versionConnectors(slug, versionId) {
|
|
3754
|
+
const manifest = this.manifestJsonOf(versionId, slug);
|
|
3755
|
+
if (manifest === undefined)
|
|
3756
|
+
throw substratError('not_found', `unknown version ${versionId} for vertical '${slug}'`);
|
|
3757
|
+
return connectorsOfManifestJson(manifest);
|
|
3758
|
+
}
|
|
3746
3759
|
/** #1705 PR 3: the promote gate's question, over two stored versions (`exportBreaksOf`). */
|
|
3747
3760
|
exportBreaksBetween(actor, producer, outgoingId, incomingId) {
|
|
3748
3761
|
return exportBreaksOf({
|
|
@@ -4225,6 +4238,20 @@ export class SqliteScopeHost {
|
|
|
4225
4238
|
async redrainCountLocal(scopeId, drainedBefore) {
|
|
4226
4239
|
return redrainCountOf(this.heldReadDb(scopeId), drainedBefore);
|
|
4227
4240
|
}
|
|
4241
|
+
/**
|
|
4242
|
+
* RFC wire-signing §4: spend a signed call's nonce in this scope's own store, the hosted
|
|
4243
|
+
* object's `claimWireNonce` here: `true` once, `false` for a nonce already spent or a claim after
|
|
4244
|
+
* `until`, and a throw when the store is at its cap (after its purge committed). Judged against
|
|
4245
|
+
* this host's clock. No provisioned check, as on the hosted object: the call that provisions a
|
|
4246
|
+
* scope spends its nonce there too.
|
|
4247
|
+
*/
|
|
4248
|
+
async claimWireNonceLocal(tenantId, scopeId, nonce, until) {
|
|
4249
|
+
const rt = this.runtime(tenantId, scopeId);
|
|
4250
|
+
const answer = await rt.actor.enqueue(() => rt.db.transaction(() => claimWireNonceIn(switchSqlOf(rt.db), nonce, until, Math.floor(Date.parse(this.clock()) / 1000)))());
|
|
4251
|
+
if (answer === 'full')
|
|
4252
|
+
throw substratError('unavailable', WIRE_NONCE_FULL);
|
|
4253
|
+
return answer;
|
|
4254
|
+
}
|
|
4228
4255
|
/**
|
|
4229
4256
|
* The read connection of a scope this host holds, for a read that names only the scope id (#2541),
|
|
4230
4257
|
* with the directory read ONCE. Refused `conflict` before anything is opened: no row, a reaped one
|
|
@@ -7142,98 +7169,116 @@ export class SqliteScopeHost {
|
|
|
7142
7169
|
// -- the hostname map (K-26) -------------------------------------------
|
|
7143
7170
|
bindHostname: async (actor, input) => {
|
|
7144
7171
|
const parsed = bindHostnameInput.parse(input);
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
.
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7172
|
+
// #2526: the scope, the holder it judges, the demotion, the bind and the row are one
|
|
7173
|
+
// directory unit, so a name bound elsewhere meanwhile is refused rather than moved.
|
|
7174
|
+
let vertical = null;
|
|
7175
|
+
this.directoryWriteAndAudit(() => {
|
|
7176
|
+
const scope = this.directory
|
|
7177
|
+
.prepare('SELECT tenant_id, vertical FROM scopes WHERE scope_id = ?')
|
|
7178
|
+
.get(parsed.scopeId);
|
|
7179
|
+
if (!scope || scope.tenant_id !== parsed.tenantId) {
|
|
7180
|
+
throw substratError('not_found', `unknown scope ${parsed.scopeId} in tenant ${parsed.tenantId}`);
|
|
7181
|
+
}
|
|
7182
|
+
vertical = scope.vertical;
|
|
7183
|
+
const existing = this.directory
|
|
7184
|
+
.prepare(`SELECT h.scope_id AS scope_id, s.status AS scope_status
|
|
7185
|
+
FROM hostnames h LEFT JOIN scopes s ON s.scope_id = h.scope_id
|
|
7186
|
+
WHERE h.hostname = ?`)
|
|
7187
|
+
.get(parsed.hostname);
|
|
7188
|
+
const holderReleased = existing?.scope_status === 'archived' || existing?.scope_status === 'reaped';
|
|
7189
|
+
if (existing && existing.scope_id !== parsed.scopeId && !holderReleased) {
|
|
7190
|
+
// A hostname is globally unique and routes to exactly one place. Silently
|
|
7191
|
+
// rebinding it would move another tenant's traffic. Exception: the holder is
|
|
7192
|
+
// ARCHIVED or REAPED (a deleted app, storage since wiped) — it released the
|
|
7193
|
+
// name, so the rebind reclaims it.
|
|
7194
|
+
throw substratError('conflict', `hostname '${parsed.hostname}' is already bound to another scope`);
|
|
7195
|
+
}
|
|
7196
|
+
// Exactly one canonical per (scope, surface): "which one do certs and
|
|
7197
|
+
// redirects use" has to have one answer, so a new canonical demotes the old.
|
|
7198
|
+
if (parsed.canonical) {
|
|
7199
|
+
this.directory
|
|
7200
|
+
.prepare('UPDATE hostnames SET canonical = 0 WHERE scope_id = ? AND surface = ?')
|
|
7201
|
+
.run(parsed.scopeId, parsed.surface);
|
|
7202
|
+
}
|
|
7167
7203
|
this.directory
|
|
7168
|
-
.prepare(
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
VALUES (?, ?, ?, ?, ?, ?, 'pending', NULL, ?, ?, NULL, NULL)`)
|
|
7179
|
-
.run(parsed.hostname, parsed.tenantId, parsed.scopeId, scope.vertical, parsed.surface, parsed.region, parsed.canonical ? 1 : 0, new Date().toISOString());
|
|
7180
|
-
this.recordAdmin(actor, 'bindHostname', { tenantId: parsed.tenantId, scopeId: parsed.scopeId, vertical: scope.vertical }, null, parsed);
|
|
7204
|
+
.prepare(
|
|
7205
|
+
// INSERT OR REPLACE resets issuance columns — a (re)bind starts a fresh
|
|
7206
|
+
// lifecycle, so any prior CF hostname id / DNS records must not linger.
|
|
7207
|
+
`INSERT OR REPLACE INTO hostnames
|
|
7208
|
+
(hostname, tenant_id, scope_id, vertical_slug, surface, region,
|
|
7209
|
+
status, status_note, canonical, created_at, custom_hostname_id, validation_records)
|
|
7210
|
+
VALUES (?, ?, ?, ?, ?, ?, 'pending', NULL, ?, ?, NULL, NULL)`)
|
|
7211
|
+
.run(parsed.hostname, parsed.tenantId, parsed.scopeId, scope.vertical, parsed.surface, parsed.region, parsed.canonical ? 1 : 0, new Date().toISOString());
|
|
7212
|
+
return true;
|
|
7213
|
+
}, () => this.recordAdmin(actor, 'bindHostname', { tenantId: parsed.tenantId, scopeId: parsed.scopeId, vertical }, null, parsed));
|
|
7181
7214
|
},
|
|
7182
7215
|
setHostnameStatus: async (actor, raw, status, note) => {
|
|
7183
7216
|
const hostname = raw.toLowerCase(); // DNS is case-insensitive; the map is normalized
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
.
|
|
7194
|
-
|
|
7217
|
+
// #2526: the row's read (its `before`, and the no-op judgement), the write and the
|
|
7218
|
+
// admin row are one directory unit.
|
|
7219
|
+
let row;
|
|
7220
|
+
this.directoryWriteAndAudit(() => {
|
|
7221
|
+
row = this.directory
|
|
7222
|
+
.prepare('SELECT tenant_id, scope_id, status FROM hostnames WHERE hostname = ?')
|
|
7223
|
+
.get(hostname);
|
|
7224
|
+
if (!row)
|
|
7225
|
+
throw substratError('not_found', `unknown hostname '${hostname}'`);
|
|
7226
|
+
if (row.status === status)
|
|
7227
|
+
return false; // idempotent, and a no-op is not audited
|
|
7228
|
+
this.directory
|
|
7229
|
+
.prepare('UPDATE hostnames SET status = ?, status_note = ? WHERE hostname = ?')
|
|
7230
|
+
.run(status, note ?? null, hostname);
|
|
7231
|
+
return true;
|
|
7232
|
+
}, () => this.recordAdmin(actor, 'setHostnameStatus', { tenantId: row.tenant_id, scopeId: row.scope_id }, { status: row.status }, { status, note: note ?? null }));
|
|
7195
7233
|
},
|
|
7196
7234
|
setHostnameIssuance: async (actor, raw, fields) => {
|
|
7197
7235
|
const hostname = raw.toLowerCase(); // DNS is case-insensitive; the map is normalized
|
|
7198
|
-
const row = this.directory
|
|
7199
|
-
.prepare('SELECT tenant_id, scope_id, status, custom_hostname_id, validation_records FROM hostnames WHERE hostname = ?')
|
|
7200
|
-
.get(hostname);
|
|
7201
|
-
if (!row)
|
|
7202
|
-
throw substratError('not_found', `unknown hostname '${hostname}'`);
|
|
7203
7236
|
const recordsJson = fields.validationRecords.length
|
|
7204
7237
|
? JSON.stringify(fields.validationRecords)
|
|
7205
7238
|
: null;
|
|
7206
|
-
|
|
7207
|
-
//
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7239
|
+
// #2526: the row's read (its `before`, and the no-op judgement), the write and the
|
|
7240
|
+
// admin row are one directory unit.
|
|
7241
|
+
let row;
|
|
7242
|
+
this.directoryWriteAndAudit(() => {
|
|
7243
|
+
row = this.directory
|
|
7244
|
+
.prepare('SELECT tenant_id, scope_id, status, custom_hostname_id, validation_records FROM hostnames WHERE hostname = ?')
|
|
7245
|
+
.get(hostname);
|
|
7246
|
+
if (!row)
|
|
7247
|
+
throw substratError('not_found', `unknown hostname '${hostname}'`);
|
|
7248
|
+
const idUnchanged = fields.customHostnameId === undefined || fields.customHostnameId === row.custom_hostname_id;
|
|
7249
|
+
// A poll that changes nothing is not an event — skip it, so the reconcile sweep
|
|
7250
|
+
// does not append a no-op admin-log row every interval.
|
|
7251
|
+
if (row.status === fields.status &&
|
|
7252
|
+
(row.validation_records ?? null) === recordsJson &&
|
|
7253
|
+
idUnchanged) {
|
|
7254
|
+
return false;
|
|
7255
|
+
}
|
|
7256
|
+
if (fields.customHostnameId !== undefined) {
|
|
7257
|
+
this.directory
|
|
7258
|
+
.prepare('UPDATE hostnames SET status = ?, status_note = ?, custom_hostname_id = ?, validation_records = ? WHERE hostname = ?')
|
|
7259
|
+
.run(fields.status, fields.note ?? null, fields.customHostnameId, recordsJson, hostname);
|
|
7260
|
+
}
|
|
7261
|
+
else {
|
|
7262
|
+
this.directory
|
|
7263
|
+
.prepare('UPDATE hostnames SET status = ?, status_note = ?, validation_records = ? WHERE hostname = ?')
|
|
7264
|
+
.run(fields.status, fields.note ?? null, recordsJson, hostname);
|
|
7265
|
+
}
|
|
7266
|
+
return true;
|
|
7267
|
+
}, () => this.recordAdmin(actor, 'setHostnameIssuance', { tenantId: row.tenant_id, scopeId: row.scope_id }, { status: row.status }, { status: fields.status, note: fields.note ?? null }));
|
|
7225
7268
|
},
|
|
7226
7269
|
unbindHostname: async (actor, raw, owner) => {
|
|
7227
7270
|
const hostname = raw.toLowerCase(); // DNS is case-insensitive; the map is normalized
|
|
7228
7271
|
// One statement: the row it removes is the row it audits, and an owner is judged by the
|
|
7229
7272
|
// delete itself, so a name rebound to another scope in between is left alone (#2285).
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7273
|
+
// #2526: the delete and its admin row are one directory unit.
|
|
7274
|
+
let row;
|
|
7275
|
+
this.directoryWriteAndAudit(() => {
|
|
7276
|
+
row = this.directory
|
|
7277
|
+
.prepare(`DELETE FROM hostnames WHERE hostname = ?${owner ? ' AND tenant_id = ? AND scope_id = ?' : ''}
|
|
7278
|
+
RETURNING tenant_id, scope_id, status`)
|
|
7279
|
+
.get(hostname, ...(owner ? [owner.tenantId, owner.scopeId] : []));
|
|
7280
|
+
return row !== undefined; // idempotent, and a no-op is not audited
|
|
7281
|
+
}, () => this.recordAdmin(actor, 'unbindHostname', { tenantId: row.tenant_id, scopeId: row.scope_id }, { hostname, status: row.status }, null));
|
|
7237
7282
|
},
|
|
7238
7283
|
listHostnames: async (actor, filter) => {
|
|
7239
7284
|
const where = [];
|
|
@@ -7387,6 +7432,9 @@ export class SqliteScopeHost {
|
|
|
7387
7432
|
installSpec.provides = parsed.provides;
|
|
7388
7433
|
if (parsed.requires)
|
|
7389
7434
|
installSpec.requires = parsed.requires;
|
|
7435
|
+
// The declared vertical-defined connectors (vertical-connectors.md §7.2), the dashboard's door.
|
|
7436
|
+
if (parsed.connectors)
|
|
7437
|
+
installSpec.connectors = parsed.connectors;
|
|
7390
7438
|
if (parsed.provisions)
|
|
7391
7439
|
installSpec.provisions = parsed.provisions;
|
|
7392
7440
|
if (parsed.sendsEmail)
|
|
@@ -7537,8 +7585,7 @@ export class SqliteScopeHost {
|
|
|
7537
7585
|
WHERE c.vertical_slug = ? AND c.channel = 'prod'`)
|
|
7538
7586
|
.get(slug);
|
|
7539
7587
|
if (prod?.admission_note === AUTO_ADMISSION_NOTE) {
|
|
7540
|
-
throw
|
|
7541
|
-
`a staff admit must vouch for it before listing`);
|
|
7588
|
+
throw autoAdmittedProdRefusal(slug, prod.id);
|
|
7542
7589
|
}
|
|
7543
7590
|
}
|
|
7544
7591
|
return { listed: existing.listed };
|
|
@@ -7650,7 +7697,7 @@ export class SqliteScopeHost {
|
|
|
7650
7697
|
this.recordAccess(actor, 'promotionImpact', { tenantId: null }, { verticalSlug, channel, versionId }, breaks.length);
|
|
7651
7698
|
return breaks;
|
|
7652
7699
|
},
|
|
7653
|
-
promoteVersion: async (actor, verticalSlug, channel, versionId, acknowledge) => {
|
|
7700
|
+
promoteVersion: async (actor, verticalSlug, channel, versionId, acknowledge, options) => {
|
|
7654
7701
|
const incoming = await readVersion(versionId);
|
|
7655
7702
|
if (!incoming)
|
|
7656
7703
|
throw substratError('not_found', `unknown version ${versionId}`);
|
|
@@ -7695,6 +7742,7 @@ export class SqliteScopeHost {
|
|
|
7695
7742
|
ack,
|
|
7696
7743
|
channel,
|
|
7697
7744
|
baseId: outgoing?.id ?? null,
|
|
7745
|
+
permissionsChange: outgoing !== undefined && outgoing.permissionDigest !== incoming.permissionDigest,
|
|
7698
7746
|
migrationsChange: outgoing !== undefined && outgoing.migrationDigest !== incoming.migrationDigest,
|
|
7699
7747
|
read: async () => ({ base: outgoing ? reviewed(outgoing) : null, incoming: reviewed(incoming) }),
|
|
7700
7748
|
});
|
|
@@ -7728,6 +7776,17 @@ export class SqliteScopeHost {
|
|
|
7728
7776
|
.get(verticalSlug, channel);
|
|
7729
7777
|
if ((now?.version_id ?? null) !== (outgoing?.id ?? null))
|
|
7730
7778
|
return false;
|
|
7779
|
+
// #2565: the listing's rule and the builder's door, read where the listing could land.
|
|
7780
|
+
const listed = readVertical(verticalSlug)?.listed;
|
|
7781
|
+
if (listed && options?.unlistedOnly)
|
|
7782
|
+
throw listedPromoteRefusal();
|
|
7783
|
+
// A re-promote of what prod already names, and what serves (if anything does), moves
|
|
7784
|
+
// nothing, so it is not judged again. One whose serve would change what runs is.
|
|
7785
|
+
const serving = this.servingPointerOfVertical(verticalSlug)?.versionId ?? versionId;
|
|
7786
|
+
if (listed && channel === 'prod' && (outgoing?.id !== versionId || serving !== versionId) &&
|
|
7787
|
+
versionAdmission(versionId).admission_note === AUTO_ADMISSION_NOTE) {
|
|
7788
|
+
throw autoAdmittedProdRefusal(verticalSlug, versionId);
|
|
7789
|
+
}
|
|
7731
7790
|
this.directory
|
|
7732
7791
|
.prepare(`INSERT INTO vertical_channels (vertical_slug, channel, version_id, updated_at)
|
|
7733
7792
|
VALUES (?, ?, ?, ?)
|
|
@@ -8036,33 +8095,39 @@ export class SqliteScopeHost {
|
|
|
8036
8095
|
migrationTag: r.serving_migration_tag,
|
|
8037
8096
|
});
|
|
8038
8097
|
},
|
|
8039
|
-
setVerticalServing: async (actor, verticalSlug, state) => {
|
|
8098
|
+
setVerticalServing: async (actor, verticalSlug, state, expected) => {
|
|
8040
8099
|
const parsed = verticalServingState.parse(state);
|
|
8041
8100
|
const r = this.directory
|
|
8042
8101
|
.prepare('SELECT * FROM verticals WHERE slug = ?')
|
|
8043
8102
|
.get(verticalSlug);
|
|
8044
8103
|
if (!r)
|
|
8045
8104
|
throw substratError('not_found', `unknown vertical '${verticalSlug}'`);
|
|
8105
|
+
// #2577: the caller's upload diffed its DO-class migration from `expected`'s class set and tag.
|
|
8106
|
+
if (expected !== undefined && !migratesFrom(r, expected))
|
|
8107
|
+
throw servingRaceRefusal(verticalSlug);
|
|
8046
8108
|
// #2178: every install on the serving script runs the new version from this moment.
|
|
8047
8109
|
this.assertNoStrandFrom([r.serving_version_id], parsed.versionId, r.serving_ref ? this.provisionedVersionsOf(verticalSlug, r.serving_version_id ? { ref: r.serving_ref, versionId: r.serving_version_id } : null, null) : []);
|
|
8048
|
-
// #2528: a compare-and-set on the serving
|
|
8049
|
-
// (
|
|
8050
|
-
//
|
|
8110
|
+
// #2528/#2577: a compare-and-set on the serving state the gate judged, its ref, version,
|
|
8111
|
+
// class set and tag (a serve and a re-serve can bring the ref and version back over a grown
|
|
8112
|
+
// class set), and the row's `before` read in the transaction that moves it, as the
|
|
8113
|
+
// Cloudflare host does.
|
|
8051
8114
|
let before = null;
|
|
8052
8115
|
let raced = false;
|
|
8053
8116
|
this.directoryWriteAndAudit(() => {
|
|
8054
8117
|
const now = this.directory
|
|
8055
|
-
.prepare('SELECT
|
|
8118
|
+
.prepare('SELECT * FROM verticals WHERE slug = ?')
|
|
8056
8119
|
.get(verticalSlug);
|
|
8057
8120
|
if (!now)
|
|
8058
8121
|
throw substratError('not_found', `unknown vertical '${verticalSlug}'`);
|
|
8059
8122
|
before = now.serving_ref ? { ref: now.serving_ref, versionId: now.serving_version_id } : null;
|
|
8060
|
-
|
|
8123
|
+
raced = !servesAsFrom(now, servedFromRow(r));
|
|
8124
|
+
if (raced)
|
|
8125
|
+
return false;
|
|
8126
|
+
this.directory
|
|
8061
8127
|
.prepare(`UPDATE verticals SET serving_ref = ?, serving_version_id = ?,
|
|
8062
|
-
serving_do_classes = ?, serving_migration_tag = ? WHERE
|
|
8063
|
-
.run(parsed.ref, parsed.versionId, JSON.stringify(parsed.doClasses), parsed.migrationTag, verticalSlug
|
|
8064
|
-
|
|
8065
|
-
return moved;
|
|
8128
|
+
serving_do_classes = ?, serving_migration_tag = ? WHERE slug = ?`)
|
|
8129
|
+
.run(parsed.ref, parsed.versionId, JSON.stringify(parsed.doClasses), parsed.migrationTag, verticalSlug);
|
|
8130
|
+
return true;
|
|
8066
8131
|
}, () => this.recordAdmin(actor, 'setVerticalServing', { tenantId: null }, before, { vertical: verticalSlug, ref: parsed.ref, versionId: parsed.versionId }));
|
|
8067
8132
|
if (raced)
|
|
8068
8133
|
throw servingRaceRefusal(verticalSlug);
|
|
@@ -8572,6 +8637,8 @@ export class SqliteScopeHost {
|
|
|
8572
8637
|
before = readTenant(tenantId);
|
|
8573
8638
|
if (!before)
|
|
8574
8639
|
throw substratError('not_found', `unknown tenant: ${tenantId}`);
|
|
8640
|
+
if (before.status === 'reaped')
|
|
8641
|
+
throw substratError('conflict', `tenant ${tenantId} is reaped and cannot change status`);
|
|
8575
8642
|
return (status === 'deleting'
|
|
8576
8643
|
? this.directory
|
|
8577
8644
|
.prepare('UPDATE tenants SET status = ?, deleting_at = ? WHERE tenant_id = ?')
|
|
@@ -8827,11 +8894,14 @@ export class SqliteScopeHost {
|
|
|
8827
8894
|
// reason: its shadow tables cannot be replayed (the DO host rejects the name
|
|
8828
8895
|
// outright), and it is recomputable — `loadDump` rebuilds it from the content
|
|
8829
8896
|
// tables it just loaded. A dump carries the rows, never the index over them.
|
|
8897
|
+
//
|
|
8898
|
+
// The wire nonce store is dropped as well (RFC wire-signing §4): it describes calls this
|
|
8899
|
+
// store accepted, not the scope's data, so a load builds it empty.
|
|
8830
8900
|
const defs = db
|
|
8831
8901
|
.prepare(`SELECT name, sql FROM sqlite_master
|
|
8832
8902
|
WHERE type = 'table' AND name NOT GLOB 'sqlite_*' AND sql IS NOT NULL
|
|
8833
8903
|
ORDER BY name`)
|
|
8834
|
-
.all().filter(({ name }) => !isSearchIndexTable(name));
|
|
8904
|
+
.all().filter(({ name }) => !isSearchIndexTable(name) && !isWireNonceTable(name));
|
|
8835
8905
|
const tables = defs.map(({ name, sql }) => {
|
|
8836
8906
|
// `.raw(true)` gives positional rows; cells stay as-is (blobs are Buffers, not
|
|
8837
8907
|
// nulled as in a UI read) so the dump is byte-faithful. The name is from the live
|
|
@@ -9245,52 +9315,59 @@ export class SqliteScopeHost {
|
|
|
9245
9315
|
},
|
|
9246
9316
|
grantEntitlement: async (actor, tenantId, entitlementKey, plan) => {
|
|
9247
9317
|
const input = entitlementGrantInput.parse(plan ?? {});
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
|
|
9271
|
-
|
|
9272
|
-
|
|
9273
|
-
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9280
|
-
|
|
9318
|
+
// #2526: the held grant (its `before`, and the no-op judgement), the write and the admin
|
|
9319
|
+
// row are one directory unit.
|
|
9320
|
+
let existing;
|
|
9321
|
+
let next = { expiresAt: null, quota: null, plan: null };
|
|
9322
|
+
this.directoryWriteAndAudit(() => {
|
|
9323
|
+
existing = this.directory
|
|
9324
|
+
.prepare('SELECT expires_at, quota, plan FROM _substrat_entitlements WHERE tenant_id = ? AND entitlement_key = ?')
|
|
9325
|
+
.get(tenantId, entitlementKey);
|
|
9326
|
+
// PATCH semantics (entitlementGrantInput): omitted preserves, null clears —
|
|
9327
|
+
// a bare re-grant on an idempotent provisioning path must not erase a
|
|
9328
|
+
// trial's expiry and quietly turn it perpetual.
|
|
9329
|
+
next = {
|
|
9330
|
+
expiresAt: input.expiresAt !== undefined ? input.expiresAt : (existing?.expires_at ?? null),
|
|
9331
|
+
quota: input.quota !== undefined ? input.quota : (existing?.quota ?? null),
|
|
9332
|
+
plan: input.plan !== undefined ? input.plan : (existing?.plan ?? null),
|
|
9333
|
+
};
|
|
9334
|
+
// Idempotent: a grant that changes nothing is not audited.
|
|
9335
|
+
if (existing &&
|
|
9336
|
+
existing.expires_at === next.expiresAt &&
|
|
9337
|
+
existing.quota === next.quota &&
|
|
9338
|
+
existing.plan === next.plan) {
|
|
9339
|
+
return false;
|
|
9340
|
+
}
|
|
9341
|
+
const now = new Date().toISOString();
|
|
9342
|
+
// granted_at/granted_by restamp on every effective change: a renewal is a
|
|
9343
|
+
// new grant act, and the full history is the admin log.
|
|
9344
|
+
this.directory
|
|
9345
|
+
.prepare(`INSERT INTO _substrat_entitlements
|
|
9346
|
+
(tenant_id, entitlement_key, expires_at, quota, plan, granted_at, granted_by)
|
|
9347
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
9348
|
+
ON CONFLICT (tenant_id, entitlement_key) DO UPDATE SET
|
|
9349
|
+
expires_at = excluded.expires_at,
|
|
9350
|
+
quota = excluded.quota,
|
|
9351
|
+
plan = excluded.plan,
|
|
9352
|
+
granted_at = excluded.granted_at,
|
|
9353
|
+
granted_by = excluded.granted_by`)
|
|
9354
|
+
.run(tenantId, entitlementKey, next.expiresAt, next.quota, next.plan, now, actor);
|
|
9355
|
+
return true;
|
|
9356
|
+
}, () => this.recordAdmin(actor, 'grantEntitlement', { tenantId }, existing
|
|
9281
9357
|
? { entitlementKey, expiresAt: existing.expires_at, quota: existing.quota, plan: existing.plan }
|
|
9282
|
-
: null, { entitlementKey, ...next });
|
|
9358
|
+
: null, { entitlementKey, ...next }));
|
|
9283
9359
|
},
|
|
9284
9360
|
revokeEntitlement: async (actor, tenantId, entitlementKey) => {
|
|
9285
|
-
|
|
9286
|
-
|
|
9287
|
-
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
|
|
9292
|
-
|
|
9293
|
-
|
|
9361
|
+
// #2526: one statement removes the grant and answers what it held, which is the admin
|
|
9362
|
+
// row's `before`; the row commits with it.
|
|
9363
|
+
let removed;
|
|
9364
|
+
this.directoryWriteAndAudit(() => {
|
|
9365
|
+
removed = this.directory
|
|
9366
|
+
.prepare(`DELETE FROM _substrat_entitlements WHERE tenant_id = ? AND entitlement_key = ?
|
|
9367
|
+
RETURNING expires_at, quota, plan`)
|
|
9368
|
+
.get(tenantId, entitlementKey);
|
|
9369
|
+
return removed !== undefined; // nothing held, nothing changed
|
|
9370
|
+
}, () => this.recordAdmin(actor, 'revokeEntitlement', { tenantId }, { entitlementKey, expiresAt: removed.expires_at, quota: removed.quota, plan: removed.plan }, null));
|
|
9294
9371
|
},
|
|
9295
9372
|
listEntitlements: async (actor, tenantId) => {
|
|
9296
9373
|
const grants = this.directory
|