import { readPostgresMigrationHistory, type PostgresMigrationQueryable, } from '../migrations/postgresMigrationStreamStore'; import { auditMigrationStreamHistory } from '@qinglong/runtime-core'; import { postgresqlMainMigrationManifest } from '../migration/migrationManifest'; import { postgresqlControlSchemaContract, type PostgresSchemaContract, } from './schemaContract'; export const POSTGRES_SCHEMA_READINESS_ERROR_CODES = [ 'server_version_unsupported', 'server_not_writable_primary', 'migration_history_invalid', 'capability_invalid', 'schema_contract_invalid', 'runtime_role_invalid', 'admin_role_invalid', 'automation_manager_role_invalid', 'approval_manager_role_invalid', 'run_manager_role_invalid', 'package_manager_role_invalid', 'package_executor_role_invalid', 'worker_credential_manager_role_invalid', 'worker_credential_executor_role_invalid', 'worker_ingress_role_invalid', ] as const; export type PostgresSchemaReadinessErrorCode = (typeof POSTGRES_SCHEMA_READINESS_ERROR_CODES)[number]; export class PostgresSchemaReadinessError extends Error { constructor( readonly code: PostgresSchemaReadinessErrorCode, readonly facts: readonly string[] = [], ) { super(`PostgreSQL schema is not ready: ${code}`); this.name = 'PostgresSchemaReadinessError'; } } export interface PostgresSchemaReadinessReport { readonly ready: true; readonly writablePrimary: true; readonly serverVersionNum: number; readonly serverMajor: number; readonly currentUser: string; readonly contractName: string; readonly contractVersion: number; readonly migrationIds: readonly string[]; } interface ServerRow extends Record { serverVersionNum: unknown; currentUser: unknown; inRecovery: unknown; transactionReadOnly: unknown; } interface CapabilityRow extends Record { contractName: unknown; contractVersion: unknown; migrationId: unknown; capabilities: unknown; } interface ColumnRow extends Record { tableName: unknown; columnName: unknown; } interface IndexRow extends Record { indexName: unknown; } interface ConstraintRow extends Record { constraintName: unknown; constraintType: unknown; } interface FunctionRow extends Record { functionName: unknown; identityArguments: unknown; owner: unknown; securityDefiner: unknown; volatility: unknown; configuration: unknown; publicExecute: unknown; } interface SchemaPrivilegeRow extends Record { schemaUsage: unknown; schemaCreate: unknown; } interface RoleSecurityRow extends Record { canLogin: unknown; superuser: unknown; createDatabase: unknown; createRole: unknown; replication: unknown; bypassRowLevelSecurity: unknown; databaseConnect: unknown; } interface TablePrivilegeRow extends Record { tableName: unknown; selectAllowed: unknown; insertAllowed: unknown; updateAllowed: unknown; deleteAllowed: unknown; isOwner: unknown; } interface FunctionPrivilegeRow extends Record { functionName: unknown; executeAllowed: unknown; isOwner: unknown; } interface ColumnPrivilegeRow extends Record { columnName: unknown; updateAllowed: unknown; } const REQUIRED_RUNTIME_PRIVILEGES = Object.freeze({ schema_migrations: Object.freeze({ select: true, insert: false, update: false, delete: false, }), schema_capabilities: Object.freeze({ select: true, insert: false, update: false, delete: false, }), projects: Object.freeze({ select: true, insert: true, update: true, delete: false, }), plugin_package_installs: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_install_heads: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_install_mutations: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_materialized_revisions: Object.freeze({ select: false, insert: false, update: false, delete: false, }), project_tool_definition_snapshots: Object.freeze({ select: false, insert: false, update: false, delete: false, }), project_tool_definition_snapshot_sources: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_quarantine_events: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_withdrawal_receipts: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_withdrawal_tasks: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_lifecycle_events: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_lifecycle_heads: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_lifecycle_receipts: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_lifecycle_tasks: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_lifecycle_plans: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_automation_publications: Object.freeze({ select: true, insert: false, update: false, delete: false, }), plugin_package_automation_disposition_events: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_automation_publication_heads: Object.freeze({ select: true, insert: false, update: false, delete: false, }), plugin_package_workflow_admissions: Object.freeze({ select: true, insert: true, update: false, delete: false, }), plugin_package_workflow_admission_steps: Object.freeze({ select: true, insert: true, update: false, delete: false, }), plugin_package_workflow_task_attempt_admissions: Object.freeze({ select: true, insert: true, update: false, delete: false, }), plugin_package_publisher_provenance: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_publisher_revocation_receipts: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_publisher_revocation_impacts: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_publisher_revocation_impact_items: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_publisher_trust_snapshots: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_publisher_trust_heads: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_publisher_revocation_proposals: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_publisher_trust_transition_proposals: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_publisher_trust_transition_receipts: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_task_ownerships: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_task_reconciliations: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_task_reconciliation_items: Object.freeze({ select: false, insert: false, update: false, delete: false, }), approval_requests: Object.freeze({ select: false, insert: false, update: false, delete: false, }), approved_action_dispatches: Object.freeze({ select: false, insert: false, update: false, delete: false, }), approved_action_executions: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_install_proposals: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_management_quota_buckets: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_identity_keyset_ledger: Object.freeze({ select: false, insert: false, update: false, delete: false, }), worker_credential_management_quota_buckets: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_admission_receipts: Object.freeze({ select: false, insert: false, update: false, delete: false, }), task_definitions: Object.freeze({ select: true, insert: false, update: false, delete: false, }), task_definition_revisions: Object.freeze({ select: true, insert: false, update: false, delete: false, }), task_execution_revisions: Object.freeze({ select: true, insert: false, update: false, delete: false, }), triggers: Object.freeze({ select: true, insert: false, update: false, delete: false, }), trigger_revisions: Object.freeze({ select: true, insert: false, update: false, delete: false, }), trigger_schedules: Object.freeze({ select: true, insert: false, update: true, delete: false, }), project_role_bindings: Object.freeze({ select: true, insert: true, update: false, delete: false, }), identity_subjects: Object.freeze({ select: true, insert: false, update: false, delete: false, }), api_credentials: Object.freeze({ select: true, insert: false, update: false, delete: false, }), security_audit_events: Object.freeze({ select: false, insert: true, update: false, delete: false, }), identity_subject_mutations: Object.freeze({ select: false, insert: false, update: false, delete: false, }), api_credential_mutations: Object.freeze({ select: false, insert: false, update: false, delete: false, }), runs: Object.freeze({ select: true, insert: true, update: true, delete: false, }), step_runs: Object.freeze({ select: true, insert: true, update: true, delete: false, }), tool_execution_trace_anchors: Object.freeze({ select: true, insert: true, update: false, delete: false, }), tool_execution_audit_receipts: Object.freeze({ select: true, insert: true, update: false, delete: false, }), tool_execution_start_barriers: Object.freeze({ select: true, insert: true, update: false, delete: false, }), tool_execution_start_artifact_bindings: Object.freeze({ select: true, insert: true, update: false, delete: false, }), tool_execution_completions: Object.freeze({ select: true, insert: true, update: false, delete: false, }), tool_execution_failure_completions: Object.freeze({ select: true, insert: true, update: false, delete: false, }), tool_result_key_catalog_generations: Object.freeze({ select: true, insert: false, update: false, delete: false, }), tool_execution_result_key_bindings: Object.freeze({ select: true, insert: true, update: false, delete: false, }), tool_execution_result_rekey_overlays: Object.freeze({ select: true, insert: false, update: false, delete: false, }), tool_execution_result_rekey_heads: Object.freeze({ select: true, insert: false, update: false, delete: false, }), tool_result_key_retirement_receipts: Object.freeze({ select: false, insert: false, update: false, delete: false, }), tool_invocation_input_artifacts: Object.freeze({ select: true, insert: true, update: false, delete: false, }), tool_invocation_preview_artifacts: Object.freeze({ select: true, insert: true, update: false, delete: false, }), run_attempts: Object.freeze({ select: true, insert: true, update: true, delete: false, }), run_attempt_log_retention_controls: Object.freeze({ select: true, insert: true, update: true, delete: true, }), run_attempt_log_artifact_tombstones: Object.freeze({ select: true, insert: true, update: false, delete: false, }), worker_sessions: Object.freeze({ select: true, insert: true, update: true, delete: false, }), run_dispatch_leases: Object.freeze({ select: true, insert: true, update: true, delete: false, }), worker_credentials: Object.freeze({ select: false, insert: false, update: false, delete: false, }), worker_credential_management_plans: Object.freeze({ select: false, insert: false, update: false, delete: false, }), worker_credential_mutations: Object.freeze({ select: false, insert: false, update: false, delete: false, }), worker_credential_deliveries: Object.freeze({ select: false, insert: false, update: false, delete: false, }), worker_credential_stage_discards: Object.freeze({ select: false, insert: false, update: false, delete: false, }), worker_execution_attestations: Object.freeze({ select: true, insert: false, update: false, delete: false, }), run_recovery_controls: Object.freeze({ select: true, insert: true, update: true, delete: false, }), run_events: Object.freeze({ select: true, insert: true, update: false, delete: false, }), step_run_mutations: Object.freeze({ select: true, insert: true, update: false, delete: false, }), run_retry_policies: Object.freeze({ select: true, insert: true, update: true, delete: false, }), }); const REQUIRED_ADMIN_PRIVILEGES = Object.freeze({ schema_migrations: Object.freeze({ select: true, insert: false, update: false, delete: false, }), schema_capabilities: Object.freeze({ select: true, insert: false, update: false, delete: false, }), projects: Object.freeze({ select: true, insert: false, update: false, delete: false, }), plugin_package_installs: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_install_heads: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_install_mutations: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_materialized_revisions: Object.freeze({ select: false, insert: false, update: false, delete: false, }), project_tool_definition_snapshots: Object.freeze({ select: false, insert: false, update: false, delete: false, }), project_tool_definition_snapshot_sources: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_quarantine_events: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_withdrawal_receipts: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_withdrawal_tasks: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_lifecycle_events: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_lifecycle_heads: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_lifecycle_receipts: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_lifecycle_tasks: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_lifecycle_plans: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_automation_publications: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_automation_disposition_events: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_automation_publication_heads: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_workflow_admissions: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_workflow_admission_steps: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_workflow_task_attempt_admissions: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_publisher_provenance: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_publisher_revocation_receipts: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_publisher_revocation_impacts: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_publisher_revocation_impact_items: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_publisher_trust_snapshots: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_publisher_trust_heads: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_publisher_revocation_proposals: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_publisher_trust_transition_proposals: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_publisher_trust_transition_receipts: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_task_ownerships: Object.freeze({ select: true, insert: false, update: false, delete: false, }), plugin_package_task_reconciliations: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_task_reconciliation_items: Object.freeze({ select: false, insert: false, update: false, delete: false, }), approval_requests: Object.freeze({ select: false, insert: false, update: false, delete: false, }), approved_action_dispatches: Object.freeze({ select: false, insert: false, update: false, delete: false, }), approved_action_executions: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_install_proposals: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_management_quota_buckets: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_identity_keyset_ledger: Object.freeze({ select: false, insert: false, update: false, delete: false, }), worker_credential_management_quota_buckets: Object.freeze({ select: false, insert: false, update: false, delete: false, }), plugin_package_admission_receipts: Object.freeze({ select: false, insert: false, update: false, delete: false, }), task_definitions: Object.freeze({ select: false, insert: false, update: false, delete: false, }), task_definition_revisions: Object.freeze({ select: false, insert: false, update: false, delete: false, }), task_execution_revisions: Object.freeze({ select: false, insert: false, update: false, delete: false, }), triggers: Object.freeze({ select: false, insert: false, update: false, delete: false, }), trigger_revisions: Object.freeze({ select: false, insert: false, update: false, delete: false, }), trigger_schedules: Object.freeze({ select: false, insert: false, update: false, delete: false, }), project_role_bindings: Object.freeze({ select: false, insert: false, update: false, delete: false, }), identity_subjects: Object.freeze({ select: true, insert: true, update: true, delete: false, }), api_credentials: Object.freeze({ select: true, insert: true, update: false, delete: false, }), security_audit_events: Object.freeze({ select: true, insert: true, update: false, delete: false, }), identity_subject_mutations: Object.freeze({ select: true, insert: true, update: false, delete: false, }), api_credential_mutations: Object.freeze({ select: true, insert: true, update: false, delete: false, }), runs: Object.freeze({ select: false, insert: false, update: false, delete: false, }), step_runs: Object.freeze({ select: false, insert: false, update: false, delete: false, }), tool_execution_trace_anchors: Object.freeze({ select: false, insert: false, update: false, delete: false, }), tool_execution_audit_receipts: Object.freeze({ select: false, insert: false, update: false, delete: false, }), tool_execution_start_barriers: Object.freeze({ select: false, insert: false, update: false, delete: false, }), tool_execution_start_artifact_bindings: Object.freeze({ select: false, insert: false, update: false, delete: false, }), tool_execution_completions: Object.freeze({ select: true, insert: false, update: false, delete: false, }), tool_execution_failure_completions: Object.freeze({ select: false, insert: false, update: false, delete: false, }), tool_result_key_catalog_generations: Object.freeze({ select: true, insert: true, update: false, delete: false, }), tool_execution_result_key_bindings: Object.freeze({ select: true, insert: false, update: false, delete: false, }), tool_execution_result_rekey_overlays: Object.freeze({ select: true, insert: true, update: false, delete: false, }), tool_execution_result_rekey_heads: Object.freeze({ select: true, insert: true, update: true, delete: false, }), tool_result_key_retirement_receipts: Object.freeze({ select: true, insert: true, update: false, delete: false, }), tool_invocation_input_artifacts: Object.freeze({ select: false, insert: false, update: false, delete: false, }), tool_invocation_preview_artifacts: Object.freeze({ select: false, insert: false, update: false, delete: false, }), run_attempts: Object.freeze({ select: false, insert: false, update: false, delete: false, }), run_attempt_log_retention_controls: Object.freeze({ select: false, insert: false, update: false, delete: false, }), run_attempt_log_artifact_tombstones: Object.freeze({ select: false, insert: false, update: false, delete: false, }), worker_sessions: Object.freeze({ select: false, insert: false, update: false, delete: false, }), run_dispatch_leases: Object.freeze({ select: false, insert: false, update: false, delete: false, }), worker_credentials: Object.freeze({ select: true, insert: true, update: true, delete: false, }), worker_credential_management_plans: Object.freeze({ select: false, insert: false, update: false, delete: false, }), worker_credential_mutations: Object.freeze({ select: true, insert: true, update: false, delete: false, }), worker_credential_deliveries: Object.freeze({ select: true, insert: true, update: false, delete: false, }), worker_credential_stage_discards: Object.freeze({ select: true, insert: true, update: false, delete: false, }), worker_execution_attestations: Object.freeze({ select: false, insert: false, update: false, delete: false, }), run_recovery_controls: Object.freeze({ select: false, insert: false, update: false, delete: false, }), run_events: Object.freeze({ select: false, insert: false, update: false, delete: false, }), step_run_mutations: Object.freeze({ select: false, insert: false, update: false, delete: false, }), run_retry_policies: Object.freeze({ select: false, insert: false, update: false, delete: false, }), }); type RequiredPrivileges = Readonly< Record< string, Readonly<{ select: boolean; insert: boolean; update: boolean; delete: boolean; }> > >; type RequiredFunctionPrivileges = Readonly>; const NO_TABLE_PRIVILEGES = Object.freeze({ select: false, insert: false, update: false, delete: false, }); const REQUIRED_PACKAGE_MANAGER_PRIVILEGES: RequiredPrivileges = Object.freeze( Object.fromEntries( postgresqlControlSchemaContract.tables.map(({ name }) => [ name, Object.freeze( name === 'schema_migrations' || name === 'schema_capabilities' || name === 'projects' || name === 'project_role_bindings' || name === 'plugin_package_lifecycle_plans' || name === 'plugin_package_automation_publications' || name === 'plugin_package_automation_publication_heads' || name === 'plugin_package_publisher_trust_transition_receipts' ? { ...NO_TABLE_PRIVILEGES, select: true } : name === 'security_audit_events' || name === 'plugin_package_install_proposals' || name === 'plugin_package_publisher_trust_snapshots' || name === 'plugin_package_publisher_trust_heads' || name === 'plugin_package_publisher_revocation_proposals' || name === 'plugin_package_publisher_trust_transition_proposals' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true } : name === 'approval_requests' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true, update: true, } : name === 'plugin_package_management_quota_buckets' || name === 'plugin_package_identity_keyset_ledger' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true, update: true, } : NO_TABLE_PRIVILEGES, ), ]), ), ); const REQUIRED_PACKAGE_EXECUTOR_PRIVILEGES: RequiredPrivileges = Object.freeze( Object.fromEntries( postgresqlControlSchemaContract.tables.map(({ name }) => [ name, Object.freeze( name === 'schema_migrations' || name === 'schema_capabilities' || name === 'projects' || name === 'project_role_bindings' || name === 'plugin_package_install_proposals' || name === 'plugin_package_publisher_revocation_proposals' || name === 'plugin_package_publisher_trust_transition_proposals' ? { ...NO_TABLE_PRIVILEGES, select: true } : name === 'security_audit_events' || name === 'approved_action_dispatches' || name === 'plugin_package_install_mutations' || name === 'plugin_package_materialized_revisions' || name === 'project_tool_definition_snapshots' || name === 'project_tool_definition_snapshot_sources' || name === 'plugin_package_lifecycle_plans' || name === 'plugin_package_automation_publications' || name === 'plugin_package_admission_receipts' || name === 'plugin_package_publisher_trust_transition_receipts' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true } : name === 'approval_requests' ? { ...NO_TABLE_PRIVILEGES, select: true, update: true } : name === 'approved_action_executions' || name === 'plugin_package_installs' || name === 'plugin_package_install_heads' || name === 'plugin_package_automation_publication_heads' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true, update: true, } : name === 'plugin_package_publisher_trust_heads' ? { ...NO_TABLE_PRIVILEGES, select: true, update: true, } : name === 'plugin_package_publisher_trust_snapshots' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true, } : name === 'plugin_package_task_ownerships' || name === 'plugin_package_task_reconciliations' || name === 'plugin_package_task_reconciliation_items' || name === 'task_definitions' || name === 'task_definition_revisions' || name === 'task_execution_revisions' ? { ...NO_TABLE_PRIVILEGES, select: true } : name === 'plugin_package_quarantine_events' || name === 'plugin_package_withdrawal_receipts' || name === 'plugin_package_withdrawal_tasks' || name === 'plugin_package_lifecycle_events' || name === 'plugin_package_lifecycle_heads' || name === 'plugin_package_lifecycle_receipts' || name === 'plugin_package_lifecycle_tasks' ? { ...NO_TABLE_PRIVILEGES, select: true } : name === 'plugin_package_publisher_provenance' || name === 'plugin_package_publisher_revocation_receipts' || name === 'plugin_package_publisher_revocation_impacts' || name === 'plugin_package_publisher_revocation_impact_items' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true } : NO_TABLE_PRIVILEGES, ), ]), ), ); const REQUIRED_WORKER_INGRESS_PRIVILEGES: RequiredPrivileges = Object.freeze( Object.fromEntries( postgresqlControlSchemaContract.tables.map(({ name }) => [ name, Object.freeze( name === 'schema_migrations' || name === 'schema_capabilities' ? { ...NO_TABLE_PRIVILEGES, select: true } : name === 'security_audit_events' ? { ...NO_TABLE_PRIVILEGES, insert: true } : name === 'run_attempts' || name === 'run_dispatch_leases' || name === 'worker_credentials' ? { ...NO_TABLE_PRIVILEGES, select: true } : name === 'worker_sessions' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true, update: true } : name === 'worker_credential_deliveries' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true } : name === 'worker_execution_attestations' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true } : NO_TABLE_PRIVILEGES, ), ]), ), ); const REQUIRED_AUTOMATION_MANAGER_PRIVILEGES: RequiredPrivileges = Object.freeze( Object.fromEntries( postgresqlControlSchemaContract.tables.map(({ name }) => [ name, Object.freeze( name === 'schema_migrations' || name === 'schema_capabilities' || name === 'projects' || name === 'project_role_bindings' || name === 'plugin_package_task_ownerships' ? { ...NO_TABLE_PRIVILEGES, select: true } : name === 'plugin_package_identity_keyset_ledger' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true, update: true, } : name === 'security_audit_events' || name === 'task_definition_revisions' || name === 'task_execution_revisions' || name === 'trigger_revisions' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true } : name === 'task_definitions' || name === 'triggers' || name === 'trigger_schedules' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true, update: true, } : NO_TABLE_PRIVILEGES, ), ]), ), ); const REQUIRED_APPROVAL_MANAGER_PRIVILEGES: RequiredPrivileges = Object.freeze( Object.fromEntries( postgresqlControlSchemaContract.tables.map(({ name }) => [ name, Object.freeze( name === 'schema_migrations' || name === 'schema_capabilities' || name === 'projects' || name === 'project_role_bindings' || name === 'tool_invocation_preview_artifacts' ? { ...NO_TABLE_PRIVILEGES, select: true } : name === 'security_audit_events' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true } : name === 'approval_requests' ? { ...NO_TABLE_PRIVILEGES, select: true, update: true } : name === 'plugin_package_identity_keyset_ledger' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true, update: true, } : NO_TABLE_PRIVILEGES, ), ]), ), ); const REQUIRED_RUN_MANAGER_PRIVILEGES: RequiredPrivileges = Object.freeze( Object.fromEntries( postgresqlControlSchemaContract.tables.map(({ name }) => [ name, Object.freeze( name === 'schema_migrations' || name === 'schema_capabilities' || name === 'projects' || name === 'project_role_bindings' || name === 'task_definitions' || name === 'task_definition_revisions' || name === 'task_execution_revisions' ? { ...NO_TABLE_PRIVILEGES, select: true } : name === 'runs' || name === 'run_attempts' || name === 'run_events' || name === 'security_audit_events' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true } : name === 'plugin_package_identity_keyset_ledger' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true, update: true, } : NO_TABLE_PRIVILEGES, ), ]), ), ); const REQUIRED_WORKER_CREDENTIAL_MANAGER_PRIVILEGES: RequiredPrivileges = Object.freeze( Object.fromEntries( postgresqlControlSchemaContract.tables.map(({ name }) => [ name, Object.freeze( name === 'schema_migrations' || name === 'schema_capabilities' || name === 'projects' || name === 'project_role_bindings' ? { ...NO_TABLE_PRIVILEGES, select: true } : name === 'worker_credential_management_plans' || name === 'security_audit_events' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true } : name === 'approval_requests' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true, update: true, } : name === 'worker_credential_management_quota_buckets' || name === 'plugin_package_identity_keyset_ledger' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true, update: true, } : NO_TABLE_PRIVILEGES, ), ]), ), ); const REQUIRED_WORKER_CREDENTIAL_EXECUTOR_PRIVILEGES: RequiredPrivileges = Object.freeze( Object.fromEntries( postgresqlControlSchemaContract.tables.map(({ name }) => [ name, Object.freeze( name === 'schema_migrations' || name === 'schema_capabilities' || name === 'projects' || name === 'project_role_bindings' || name === 'worker_credential_management_plans' ? { ...NO_TABLE_PRIVILEGES, select: true } : name === 'security_audit_events' || name === 'approved_action_dispatches' || name === 'approved_action_executions' || name === 'worker_credentials' || name === 'worker_credential_mutations' || name === 'worker_credential_deliveries' || name === 'worker_credential_stage_discards' ? name === 'approved_action_executions' ? { ...NO_TABLE_PRIVILEGES, select: true, insert: true, update: true, } : { ...NO_TABLE_PRIVILEGES, select: true, insert: true } : name === 'approval_requests' ? { ...NO_TABLE_PRIVILEGES, select: true, update: true } : NO_TABLE_PRIVILEGES, ), ]), ), ); const NO_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges = Object.freeze( Object.fromEntries( postgresqlControlSchemaContract.functions.map(({ name }) => [name, false]), ), ); const REQUIRED_ADMIN_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges = NO_FUNCTION_PRIVILEGES; const REQUIRED_RUNTIME_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges = Object.freeze({ commit_plugin_package_lifecycle: false, commit_plugin_package_quarantine: false, commit_plugin_package_task_reconciliation: false, enforce_plugin_package_stage_provenance: false, lock_active_plugin_package_project: false, lock_approval_policy_fence: false, lock_run_management_policy_fence: true, plugin_package_automation_start_allowed: true, plugin_package_workflow_admission_snapshot: true, plugin_package_workflow_task_attempt_snapshot: true, plugin_package_lifecycle_blocking_runs: false, plugin_package_run_start_allowed: true, plugin_package_tool_start_allowed: true, register_plugin_package_automation_disposition_event: false, }); const REQUIRED_PACKAGE_MANAGER_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges = Object.freeze({ commit_plugin_package_lifecycle: false, commit_plugin_package_quarantine: false, commit_plugin_package_task_reconciliation: false, enforce_plugin_package_stage_provenance: false, lock_active_plugin_package_project: false, lock_approval_policy_fence: true, lock_run_management_policy_fence: false, plugin_package_automation_start_allowed: false, plugin_package_workflow_admission_snapshot: false, plugin_package_workflow_task_attempt_snapshot: false, plugin_package_lifecycle_blocking_runs: false, plugin_package_run_start_allowed: false, plugin_package_tool_start_allowed: false, register_plugin_package_automation_disposition_event: false, }); const REQUIRED_PACKAGE_EXECUTOR_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges = Object.freeze({ commit_plugin_package_lifecycle: true, commit_plugin_package_quarantine: true, commit_plugin_package_task_reconciliation: true, enforce_plugin_package_stage_provenance: false, lock_active_plugin_package_project: true, lock_approval_policy_fence: true, lock_run_management_policy_fence: false, plugin_package_automation_start_allowed: false, plugin_package_workflow_admission_snapshot: false, plugin_package_workflow_task_attempt_snapshot: false, plugin_package_lifecycle_blocking_runs: true, plugin_package_run_start_allowed: false, plugin_package_tool_start_allowed: false, register_plugin_package_automation_disposition_event: false, }); const REQUIRED_WORKER_CREDENTIAL_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges = Object.freeze({ ...NO_FUNCTION_PRIVILEGES, lock_approval_policy_fence: true, }); const REQUIRED_APPROVAL_MANAGER_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges = Object.freeze({ ...NO_FUNCTION_PRIVILEGES, lock_approval_policy_fence: true, }); const REQUIRED_RUN_MANAGER_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges = Object.freeze({ ...NO_FUNCTION_PRIVILEGES, lock_run_management_policy_fence: true, }); function safeInteger(value: unknown): number | null { if (typeof value === 'number' && Number.isSafeInteger(value)) return value; if (typeof value === 'string' && /^(0|[1-9]\d*)$/.test(value)) { const parsed = Number(value); return Number.isSafeInteger(parsed) ? parsed : null; } return null; } function sorted(values: Iterable): string[] { return [...values].sort((left, right) => left.localeCompare(right)); } function exactJsonObject( actual: unknown, expected: Readonly>, ): boolean { if (!actual || typeof actual !== 'object' || Array.isArray(actual)) { return false; } const actualObject = actual as Record; const actualKeys = sorted(Object.keys(actualObject)); const expectedKeys = sorted(Object.keys(expected)); return ( actualKeys.length === expectedKeys.length && actualKeys.every( (key, index) => key === expectedKeys[index] && actualObject[key] === expected[key], ) ); } async function readServer( queryable: PostgresMigrationQueryable, contract: PostgresSchemaContract, ): Promise<{ writablePrimary: true; serverVersionNum: number; serverMajor: number; currentUser: string; }> { const result = await queryable.query( ` SELECT current_setting('server_version_num') AS "serverVersionNum", current_user AS "currentUser", pg_is_in_recovery() AS "inRecovery", current_setting('transaction_read_only') AS "transactionReadOnly" `.trim(), ); const row = result.rows[0]; const serverVersionNum = safeInteger(row?.serverVersionNum); const currentUser = row?.currentUser; const inRecovery = row?.inRecovery; const transactionReadOnly = row?.transactionReadOnly; const serverMajor = serverVersionNum === null ? null : Math.floor(serverVersionNum / 10_000); if ( result.rows.length !== 1 || serverVersionNum === null || serverMajor === null || serverMajor < contract.minimumServerMajor || serverMajor > contract.maximumServerMajor || typeof currentUser !== 'string' || currentUser.length === 0 ) { throw new PostgresSchemaReadinessError('server_version_unsupported', [ String(serverVersionNum ?? 'invalid'), ]); } if (inRecovery !== false || transactionReadOnly !== 'off') { throw new PostgresSchemaReadinessError('server_not_writable_primary', [ `in-recovery:${String(inRecovery)}`, `transaction-read-only:${String(transactionReadOnly)}`, ]); } return { writablePrimary: true, serverVersionNum, serverMajor, currentUser, }; } async function assertHistory( queryable: PostgresMigrationQueryable, ): Promise { const history = await readPostgresMigrationHistory(queryable); try { auditMigrationStreamHistory(history, postgresqlMainMigrationManifest); return Object.freeze(history.map(({ migrationId }) => migrationId)); } catch (error) { throw new PostgresSchemaReadinessError('migration_history_invalid', [ error instanceof Error ? error.name : 'UnknownError', ]); } } async function assertCapability( queryable: PostgresMigrationQueryable, contract: PostgresSchemaContract, ): Promise { const result = await queryable.query( ` SELECT contract_name AS "contractName", contract_version AS "contractVersion", migration_id AS "migrationId", capabilities FROM "${contract.schema}"."schema_capabilities" WHERE contract_name = $1 `.trim(), [contract.contractName], ); const row = result.rows[0]; if ( result.rows.length !== 1 || !row || row.contractName !== contract.contractName || safeInteger(row.contractVersion) !== contract.contractVersion || row.migrationId !== contract.migrationId || !exactJsonObject(row.capabilities, contract.capabilities) ) { throw new PostgresSchemaReadinessError('capability_invalid'); } } async function assertSchemaContract( queryable: PostgresMigrationQueryable, contract: PostgresSchemaContract, ): Promise { const [columnsResult, indexesResult, constraintsResult, functionsResult] = await Promise.all([ queryable.query( ` SELECT tables.relname AS "tableName", columns.attname AS "columnName" FROM pg_class tables JOIN pg_namespace schemas ON schemas.oid = tables.relnamespace JOIN pg_attribute columns ON columns.attrelid = tables.oid WHERE schemas.nspname = $1 AND tables.relkind IN ('r', 'p') AND columns.attnum > 0 AND NOT columns.attisdropped ORDER BY tables.relname, columns.attnum `.trim(), [contract.schema], ), queryable.query( ` SELECT indexname AS "indexName" FROM pg_indexes WHERE schemaname = $1 ORDER BY indexname `.trim(), [contract.schema], ), queryable.query( ` SELECT constraints.conname AS "constraintName", CASE constraints.contype WHEN 'c' THEN 'check' WHEN 'f' THEN 'foreign_key' END AS "constraintType" FROM pg_constraint constraints JOIN pg_class tables ON tables.oid = constraints.conrelid JOIN pg_namespace schemas ON schemas.oid = tables.relnamespace WHERE schemas.nspname = $1 AND constraints.contype IN ('c', 'f') ORDER BY constraints.contype, constraints.conname `.trim(), [contract.schema], ), queryable.query( ` SELECT routines.proname AS "functionName", pg_get_function_identity_arguments(routines.oid) AS "identityArguments", pg_get_userbyid(routines.proowner) AS "owner", routines.prosecdef AS "securityDefiner", CASE routines.provolatile WHEN 'v' THEN 'volatile' WHEN 's' THEN 'stable' WHEN 'i' THEN 'immutable' END AS "volatility", COALESCE(routines.proconfig, ARRAY[]::text[]) AS "configuration", EXISTS ( SELECT 1 FROM aclexplode( COALESCE( routines.proacl, acldefault('f', routines.proowner) ) ) AS privileges WHERE privileges.grantee = 0 AND privileges.privilege_type = 'EXECUTE' ) AS "publicExecute" FROM pg_proc routines JOIN pg_namespace schemas ON schemas.oid = routines.pronamespace WHERE schemas.nspname = $1 ORDER BY routines.proname, pg_get_function_identity_arguments(routines.oid) `.trim(), [contract.schema], ), ]); const actualTables = new Map>(); for (const row of columnsResult.rows) { if ( typeof row.tableName !== 'string' || typeof row.columnName !== 'string' ) { throw new PostgresSchemaReadinessError('schema_contract_invalid'); } const columns = actualTables.get(row.tableName) ?? new Set(); columns.add(row.columnName); actualTables.set(row.tableName, columns); } const expectedTables = new Map( contract.tables.map((table) => [table.name, new Set(table.columns)]), ); const findings: string[] = []; for (const [tableName, expectedColumns] of expectedTables) { const actualColumns = actualTables.get(tableName); if (!actualColumns) { findings.push(`missing-table:${tableName}`); continue; } for (const column of expectedColumns) { if (!actualColumns.has(column)) { findings.push(`missing-column:${tableName}.${column}`); } } for (const column of actualColumns) { if (!expectedColumns.has(column)) { findings.push(`unknown-column:${tableName}.${column}`); } } } for (const tableName of actualTables.keys()) { if (!expectedTables.has(tableName)) findings.push(`unknown-table:${tableName}`); } const actualIndexes = new Set(); for (const row of indexesResult.rows) { if (typeof row.indexName !== 'string') { throw new PostgresSchemaReadinessError('schema_contract_invalid'); } actualIndexes.add(row.indexName); } const expectedIndexes = new Set(contract.indexes); for (const index of expectedIndexes) { if (!actualIndexes.has(index)) findings.push(`missing-index:${index}`); } for (const index of actualIndexes) { if (!expectedIndexes.has(index)) findings.push(`unknown-index:${index}`); } const actualChecks = new Set(); const actualForeignKeys = new Set(); for (const row of constraintsResult.rows) { if ( typeof row.constraintName !== 'string' || (row.constraintType !== 'check' && row.constraintType !== 'foreign_key') ) { throw new PostgresSchemaReadinessError('schema_contract_invalid'); } const target = row.constraintType === 'check' ? actualChecks : actualForeignKeys; target.add(row.constraintName); } for (const check of contract.checks) { if (!actualChecks.has(check)) findings.push(`missing-check:${check}`); } for (const check of actualChecks) { if (!contract.checks.includes(check)) findings.push(`unknown-check:${check}`); } for (const foreignKey of contract.foreignKeys) { if (!actualForeignKeys.has(foreignKey)) { findings.push(`missing-foreign-key:${foreignKey}`); } } for (const foreignKey of actualForeignKeys) { if (!contract.foreignKeys.includes(foreignKey)) { findings.push(`unknown-foreign-key:${foreignKey}`); } } const actualFunctions = new Map< string, { identityArguments: string; owner: string; securityDefiner: boolean; volatility: string; configuration: readonly string[]; publicExecute: boolean; } >(); for (const row of functionsResult.rows) { if ( typeof row.functionName !== 'string' || typeof row.identityArguments !== 'string' || typeof row.owner !== 'string' || typeof row.securityDefiner !== 'boolean' || typeof row.volatility !== 'string' || !Array.isArray(row.configuration) || row.configuration.some((value) => typeof value !== 'string') || typeof row.publicExecute !== 'boolean' ) { throw new PostgresSchemaReadinessError('schema_contract_invalid'); } actualFunctions.set(`${row.functionName}(${row.identityArguments})`, { identityArguments: row.identityArguments, owner: row.owner, securityDefiner: row.securityDefiner, volatility: row.volatility, configuration: row.configuration as string[], publicExecute: row.publicExecute, }); } const expectedFunctions = new Set(); for (const expected of contract.functions) { const identity = `${expected.name}(${expected.identityArguments})`; expectedFunctions.add(identity); const actual = actualFunctions.get(identity); if (!actual) { findings.push(`missing-function:${identity}`); continue; } if ( actual.owner !== expected.owner || actual.securityDefiner !== expected.securityDefiner || actual.volatility !== expected.volatility || actual.publicExecute !== false || JSON.stringify(actual.configuration) !== JSON.stringify(expected.configuration) ) { findings.push(`function-contract:${identity}`); } } for (const identity of actualFunctions.keys()) { if (!expectedFunctions.has(identity)) { findings.push(`unknown-function:${identity}`); } } if (findings.length > 0) { throw new PostgresSchemaReadinessError( 'schema_contract_invalid', sorted(findings), ); } } async function assertRole( queryable: PostgresMigrationQueryable, contract: PostgresSchemaContract, requiredPrivileges: RequiredPrivileges, requiredFunctionPrivileges: RequiredFunctionPrivileges, errorCode: | 'runtime_role_invalid' | 'admin_role_invalid' | 'automation_manager_role_invalid' | 'approval_manager_role_invalid' | 'run_manager_role_invalid' | 'package_manager_role_invalid' | 'package_executor_role_invalid' | 'worker_credential_manager_role_invalid' | 'worker_credential_executor_role_invalid' | 'worker_ingress_role_invalid', ): Promise { const roleResult = await queryable.query( ` SELECT rolcanlogin AS "canLogin", rolsuper AS "superuser", rolcreatedb AS "createDatabase", rolcreaterole AS "createRole", rolreplication AS "replication", rolbypassrls AS "bypassRowLevelSecurity", has_database_privilege(current_user, current_database(), 'CONNECT') AS "databaseConnect" FROM pg_catalog.pg_roles WHERE rolname = current_user `.trim(), ); const schemaResult = await queryable.query( ` SELECT has_schema_privilege(current_user, $1, 'USAGE') AS "schemaUsage", has_schema_privilege(current_user, $1, 'CREATE') AS "schemaCreate" `.trim(), [contract.schema], ); const schema = schemaResult.rows[0]; const tableNames = Object.keys(requiredPrivileges); const tableResult = await queryable.query( ` SELECT requested.table_name AS "tableName", has_table_privilege(current_user, format('%I.%I', $1, requested.table_name), 'SELECT') AS "selectAllowed", has_table_privilege(current_user, format('%I.%I', $1, requested.table_name), 'INSERT') AS "insertAllowed", has_table_privilege(current_user, format('%I.%I', $1, requested.table_name), 'UPDATE') AS "updateAllowed", has_table_privilege(current_user, format('%I.%I', $1, requested.table_name), 'DELETE') AS "deleteAllowed", pg_get_userbyid(classes.relowner) = current_user AS "isOwner" FROM unnest($2::text[]) AS requested(table_name) JOIN pg_namespace namespaces ON namespaces.nspname = $1 JOIN pg_class classes ON classes.relnamespace = namespaces.oid AND classes.relname = requested.table_name ORDER BY requested.table_name `.trim(), [contract.schema, tableNames], ); const functionNames = contract.functions.map(({ name }) => name); const functionIdentityArguments = contract.functions.map( ({ identityArguments }) => identityArguments, ); const functionResult = await queryable.query( ` SELECT requested.function_name AS "functionName", has_function_privilege(current_user, routines.oid, 'EXECUTE') AS "executeAllowed", pg_get_userbyid(routines.proowner) = current_user AS "isOwner" FROM unnest($2::text[], $3::text[]) AS requested(function_name, identity_arguments) JOIN pg_namespace namespaces ON namespaces.nspname = $1 JOIN pg_proc routines ON routines.pronamespace = namespaces.oid AND routines.proname = requested.function_name AND pg_get_function_identity_arguments(routines.oid) = requested.identity_arguments ORDER BY requested.function_name `.trim(), [contract.schema, functionNames, functionIdentityArguments], ); const findings: string[] = []; const role = roleResult.rows[0]; if ( roleResult.rows.length !== 1 || role?.canLogin !== true || role?.superuser !== false || role?.createDatabase !== false || role?.createRole !== false || role?.replication !== false || role?.bypassRowLevelSecurity !== false || role?.databaseConnect !== true ) { findings.push('role-security'); } if ( schemaResult.rows.length !== 1 || schema?.schemaUsage !== true || schema?.schemaCreate !== false ) { findings.push('schema-privileges'); } const privilegesByTable = new Map( tableResult.rows.map((row) => [row.tableName, row]), ); for (const tableName of tableNames) { const expected = requiredPrivileges[tableName]!; const actual = privilegesByTable.get(tableName); if ( !actual || actual.selectAllowed !== expected.select || actual.insertAllowed !== expected.insert || actual.updateAllowed !== expected.update || actual.deleteAllowed !== expected.delete || actual.isOwner !== false ) { findings.push(`table-privileges:${tableName}`); } } if (privilegesByTable.size !== tableNames.length) { findings.push('table-privilege-row-count'); } const privilegesByFunction = new Map( functionResult.rows.map((row) => [row.functionName, row]), ); for (const [functionName, executeAllowed] of Object.entries( requiredFunctionPrivileges, )) { const actual = privilegesByFunction.get(functionName); if ( !actual || actual.executeAllowed !== executeAllowed || actual.isOwner !== false ) { findings.push(`function-privileges:${functionName}`); } } if ( privilegesByFunction.size !== Object.keys(requiredFunctionPrivileges).length ) { findings.push('function-privilege-row-count'); } if (findings.length > 0) { throw new PostgresSchemaReadinessError(errorCode, sorted(findings)); } } async function assertRunManagerColumnPrivileges( queryable: PostgresMigrationQueryable, contract: PostgresSchemaContract, ): Promise { const run = contract.tables.find(({ name }) => name === 'runs'); if (!run) { throw new PostgresSchemaReadinessError('run_manager_role_invalid', [ 'missing-runs-contract', ]); } const result = await queryable.query( ` SELECT requested.column_name AS "columnName", has_column_privilege( current_user, format('%I.%I', $1::text, 'runs'), requested.column_name, 'UPDATE' ) AS "updateAllowed" FROM unnest($2::text[]) AS requested(column_name) ORDER BY requested.column_name `.trim(), [contract.schema, run.columns], ); const allowed = new Set([ 'cancel_requested_at_ms', 'cancel_reason', 'version', 'event_sequence', ]); const actual = new Map(result.rows.map((row) => [row.columnName, row])); const findings: string[] = []; for (const columnName of run.columns) { const row = actual.get(columnName); if (!row || row.updateAllowed !== allowed.has(columnName)) { findings.push(`column-update-privilege:runs.${columnName}`); } } if (actual.size !== run.columns.length) { findings.push('column-privilege-row-count:runs'); } if (findings.length > 0) { throw new PostgresSchemaReadinessError( 'run_manager_role_invalid', sorted(findings), ); } } export async function assertPostgresSchemaReady( queryable: PostgresMigrationQueryable, contract: PostgresSchemaContract = postgresqlControlSchemaContract, ): Promise { const server = await readServer(queryable, contract); const migrationIds = await assertHistory(queryable); await assertCapability(queryable, contract); await assertSchemaContract(queryable, contract); await assertRole( queryable, contract, REQUIRED_RUNTIME_PRIVILEGES, REQUIRED_RUNTIME_FUNCTION_PRIVILEGES, 'runtime_role_invalid', ); return Object.freeze({ ready: true, ...server, contractName: contract.contractName, contractVersion: contract.contractVersion, migrationIds, }); } export async function assertPostgresAdminSchemaReady( queryable: PostgresMigrationQueryable, contract: PostgresSchemaContract = postgresqlControlSchemaContract, ): Promise { const server = await readServer(queryable, contract); const migrationIds = await assertHistory(queryable); await assertCapability(queryable, contract); await assertSchemaContract(queryable, contract); await assertRole( queryable, contract, REQUIRED_ADMIN_PRIVILEGES, REQUIRED_ADMIN_FUNCTION_PRIVILEGES, 'admin_role_invalid', ); return Object.freeze({ ready: true, ...server, contractName: contract.contractName, contractVersion: contract.contractVersion, migrationIds, }); } export async function assertPostgresWorkerIngressSchemaReady( queryable: PostgresMigrationQueryable, contract: PostgresSchemaContract = postgresqlControlSchemaContract, ): Promise { const server = await readServer(queryable, contract); const migrationIds = await assertHistory(queryable); await assertCapability(queryable, contract); await assertSchemaContract(queryable, contract); await assertRole( queryable, contract, REQUIRED_WORKER_INGRESS_PRIVILEGES, NO_FUNCTION_PRIVILEGES, 'worker_ingress_role_invalid', ); return Object.freeze({ ready: true, ...server, contractName: contract.contractName, contractVersion: contract.contractVersion, migrationIds, }); } export async function assertPostgresAutomationManagerSchemaReady( queryable: PostgresMigrationQueryable, contract: PostgresSchemaContract = postgresqlControlSchemaContract, ): Promise { const server = await readServer(queryable, contract); const migrationIds = await assertHistory(queryable); await assertCapability(queryable, contract); await assertSchemaContract(queryable, contract); await assertRole( queryable, contract, REQUIRED_AUTOMATION_MANAGER_PRIVILEGES, NO_FUNCTION_PRIVILEGES, 'automation_manager_role_invalid', ); return Object.freeze({ ready: true, ...server, contractName: contract.contractName, contractVersion: contract.contractVersion, migrationIds, }); } export async function assertPostgresApprovalManagerSchemaReady( queryable: PostgresMigrationQueryable, contract: PostgresSchemaContract = postgresqlControlSchemaContract, ): Promise { const server = await readServer(queryable, contract); const migrationIds = await assertHistory(queryable); await assertCapability(queryable, contract); await assertSchemaContract(queryable, contract); await assertRole( queryable, contract, REQUIRED_APPROVAL_MANAGER_PRIVILEGES, REQUIRED_APPROVAL_MANAGER_FUNCTION_PRIVILEGES, 'approval_manager_role_invalid', ); return Object.freeze({ ready: true, ...server, contractName: contract.contractName, contractVersion: contract.contractVersion, migrationIds, }); } export async function assertPostgresRunManagerSchemaReady( queryable: PostgresMigrationQueryable, contract: PostgresSchemaContract = postgresqlControlSchemaContract, ): Promise { const server = await readServer(queryable, contract); const migrationIds = await assertHistory(queryable); await assertCapability(queryable, contract); await assertSchemaContract(queryable, contract); await assertRole( queryable, contract, REQUIRED_RUN_MANAGER_PRIVILEGES, REQUIRED_RUN_MANAGER_FUNCTION_PRIVILEGES, 'run_manager_role_invalid', ); await assertRunManagerColumnPrivileges(queryable, contract); return Object.freeze({ ready: true, ...server, contractName: contract.contractName, contractVersion: contract.contractVersion, migrationIds, }); } export async function assertPostgresPackageManagerSchemaReady( queryable: PostgresMigrationQueryable, contract: PostgresSchemaContract = postgresqlControlSchemaContract, ): Promise { const server = await readServer(queryable, contract); const migrationIds = await assertHistory(queryable); await assertCapability(queryable, contract); await assertSchemaContract(queryable, contract); await assertRole( queryable, contract, REQUIRED_PACKAGE_MANAGER_PRIVILEGES, REQUIRED_PACKAGE_MANAGER_FUNCTION_PRIVILEGES, 'package_manager_role_invalid', ); return Object.freeze({ ready: true, ...server, contractName: contract.contractName, contractVersion: contract.contractVersion, migrationIds, }); } export async function assertPostgresPackageExecutorSchemaReady( queryable: PostgresMigrationQueryable, contract: PostgresSchemaContract = postgresqlControlSchemaContract, ): Promise { const server = await readServer(queryable, contract); const migrationIds = await assertHistory(queryable); await assertCapability(queryable, contract); await assertSchemaContract(queryable, contract); await assertRole( queryable, contract, REQUIRED_PACKAGE_EXECUTOR_PRIVILEGES, REQUIRED_PACKAGE_EXECUTOR_FUNCTION_PRIVILEGES, 'package_executor_role_invalid', ); return Object.freeze({ ready: true, ...server, contractName: contract.contractName, contractVersion: contract.contractVersion, migrationIds, }); } export async function assertPostgresWorkerCredentialManagerSchemaReady( queryable: PostgresMigrationQueryable, contract: PostgresSchemaContract = postgresqlControlSchemaContract, ): Promise { const server = await readServer(queryable, contract); const migrationIds = await assertHistory(queryable); await assertCapability(queryable, contract); await assertSchemaContract(queryable, contract); await assertRole( queryable, contract, REQUIRED_WORKER_CREDENTIAL_MANAGER_PRIVILEGES, REQUIRED_WORKER_CREDENTIAL_FUNCTION_PRIVILEGES, 'worker_credential_manager_role_invalid', ); return Object.freeze({ ready: true, ...server, contractName: contract.contractName, contractVersion: contract.contractVersion, migrationIds, }); } export async function assertPostgresWorkerCredentialExecutorSchemaReady( queryable: PostgresMigrationQueryable, contract: PostgresSchemaContract = postgresqlControlSchemaContract, ): Promise { const server = await readServer(queryable, contract); const migrationIds = await assertHistory(queryable); await assertCapability(queryable, contract); await assertSchemaContract(queryable, contract); await assertRole( queryable, contract, REQUIRED_WORKER_CREDENTIAL_EXECUTOR_PRIVILEGES, REQUIRED_WORKER_CREDENTIAL_FUNCTION_PRIVILEGES, 'worker_credential_executor_role_invalid', ); return Object.freeze({ ready: true, ...server, contractName: contract.contractName, contractVersion: contract.contractVersion, migrationIds, }); }