\set ON_ERROR_STOP on DO $assertions$ DECLARE actual_columns text[]; default_quota integer; initial_version bigint; updated_version bigint; area_initial_version bigint; area_updated_version bigint; BEGIN IF current_setting('server_version_num')::integer < 170000 OR current_setting('server_version_num')::integer >= 180000 THEN RAISE EXCEPTION 'YoVision requires PostgreSQL 17.x'; END IF; SELECT array_agg(column_name ORDER BY ordinal_position) INTO actual_columns FROM information_schema.columns WHERE table_schema = 'bell' AND table_name = 'site_quota_v1'; IF actual_columns IS DISTINCT FROM ARRAY[ 'tenant_id', 'site_id', 'max_video_channels', 'source_version', 'source_updated_at' ] THEN RAISE EXCEPTION 'site_quota_v1 signature drift: %', actual_columns; END IF; IF NOT has_table_privilege('sense_app', 'bell.site_quota_v1', 'SELECT') THEN RAISE EXCEPTION 'sense_app lacks SELECT on quota view'; END IF; IF has_table_privilege('sense_app', 'bell.site_quota_v1', 'INSERT') OR has_table_privilege('sense_app', 'bell.site_quota_v1', 'UPDATE') OR has_table_privilege('sense_app', 'bell.site_quota_v1', 'DELETE') THEN RAISE EXCEPTION 'sense_app has forbidden quota-view write privileges'; END IF; IF has_table_privilege('sense_app', 'bell.sites', 'SELECT') OR has_table_privilege('sense_app', 'bell.sites', 'INSERT') OR has_table_privilege('sense_app', 'bell.sites', 'UPDATE') OR has_table_privilege('sense_app', 'bell.sites', 'DELETE') THEN RAISE EXCEPTION 'sense_app has forbidden Bell source-table privileges'; END IF; SELECT array_agg(column_name ORDER BY ordinal_position) INTO actual_columns FROM information_schema.columns WHERE table_schema = 'bell' AND table_name = 'area_policy_v1'; IF actual_columns IS DISTINCT FROM ARRAY[ 'tenant_id', 'site_id', 'area_id', 'capture_policy', 'source_version', 'source_updated_at' ] THEN RAISE EXCEPTION 'area_policy_v1 signature drift: %', actual_columns; END IF; IF NOT has_table_privilege('sense_app', 'bell.area_policy_v1', 'SELECT') THEN RAISE EXCEPTION 'sense_app lacks SELECT on Area policy view'; END IF; IF has_table_privilege('sense_app', 'bell.area_policy_v1', 'INSERT') OR has_table_privilege('sense_app', 'bell.area_policy_v1', 'UPDATE') OR has_table_privilege('sense_app', 'bell.area_policy_v1', 'DELETE') OR has_table_privilege('sense_app', 'bell.areas', 'SELECT') OR has_table_privilege('sense_app', 'bell.areas', 'INSERT') OR has_table_privilege('sense_app', 'bell.areas', 'UPDATE') OR has_table_privilege('sense_app', 'bell.areas', 'DELETE') THEN RAISE EXCEPTION 'sense_app violates the Area projection boundary'; END IF; IF has_function_privilege('sense_app', 'bell.bump_area_version()', 'EXECUTE') THEN RAISE EXCEPTION 'sense_app can execute the Bell Area version trigger function'; END IF; IF NOT has_table_privilege('sense_app', 'sense.device_operation_outbox', 'SELECT') OR NOT has_table_privilege('sense_app', 'sense.device_operation_outbox', 'INSERT') OR NOT has_table_privilege('sense_app', 'sense.device_operation_outbox', 'UPDATE') OR NOT has_table_privilege('sense_app', 'sense.device_operation_outbox', 'DELETE') THEN RAISE EXCEPTION 'sense_app lacks access to its local audit Outbox'; END IF; IF (SELECT max(version) FROM bell.schema_migrations) <> 5 OR (SELECT max(version) FROM sense.schema_migrations) <> 6 THEN RAISE EXCEPTION 'schema migration version drift'; END IF; IF NOT EXISTS ( SELECT 1 FROM pg_roles WHERE rolname = 'bell_runtime' AND NOT rolcanlogin AND NOT rolsuper AND NOT rolcreatedb AND NOT rolcreaterole AND NOT rolreplication ) THEN RAISE EXCEPTION 'bell_runtime role is missing or unsafe'; END IF; IF NOT has_table_privilege('yovision_t015_bell', 'bell.events', 'SELECT,INSERT') OR NOT has_table_privilege('yovision_t015_bell', 'bell.event_outcomes', 'SELECT,INSERT') OR has_table_privilege('yovision_t015_bell', 'bell.events', 'UPDATE') OR has_table_privilege('yovision_t015_bell', 'bell.events', 'DELETE') OR has_table_privilege('yovision_t015_bell', 'bell.events', 'TRUNCATE') OR has_table_privilege('yovision_t015_bell', 'bell.event_outcomes', 'UPDATE') OR has_table_privilege('yovision_t015_bell', 'bell.event_outcomes', 'DELETE') OR has_table_privilege('yovision_t015_bell', 'bell.event_outcomes', 'TRUNCATE') THEN RAISE EXCEPTION 'Bell runtime violates immutable event privileges'; END IF; IF has_table_privilege('public', 'bell.events', 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE') OR has_table_privilege('public', 'bell.event_outcomes', 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE') THEN RAISE EXCEPTION 'Bell event facts leaked to PUBLIC'; END IF; INSERT INTO bell.sites(tenant_id, id, name) VALUES ('assertion-tenant', 'assertion-site', 'Assertion Site') RETURNING max_video_channels, version INTO default_quota, initial_version; IF default_quota <> 16 OR initial_version <> 1 THEN RAISE EXCEPTION 'Bell site default quota/version drift'; END IF; UPDATE bell.sites SET name = 'Assertion Site Updated' WHERE tenant_id = 'assertion-tenant' AND id = 'assertion-site' RETURNING version INTO updated_version; IF updated_version <> initial_version + 1 THEN RAISE EXCEPTION 'Bell site source_version is not monotonic'; END IF; INSERT INTO bell.areas(tenant_id, site_id, id, name, capture_policy) VALUES ( 'assertion-tenant', 'assertion-site', 'assertion-area', 'Assertion Area', 'video_allowed' ) RETURNING version INTO area_initial_version; UPDATE bell.areas SET capture_policy = 'non_imaging_only' WHERE tenant_id = 'assertion-tenant' AND id = 'assertion-area' RETURNING version INTO area_updated_version; IF area_initial_version <> 1 OR area_updated_version <> area_initial_version + 1 THEN RAISE EXCEPTION 'Bell Area source_version is not monotonic'; END IF; BEGIN INSERT INTO bell.areas(tenant_id, site_id, id, name, capture_policy) VALUES ('assertion-tenant', 'assertion-site', 'invalid-area', 'Invalid', 'unknown'); RAISE EXCEPTION 'unknown capture_policy unexpectedly accepted'; EXCEPTION WHEN check_violation THEN NULL; END; BEGIN INSERT INTO bell.sites(tenant_id, id, name, max_video_channels) VALUES ('assertion-tenant', 'invalid-site', 'Invalid', 129); RAISE EXCEPTION 'quota 129 unexpectedly accepted'; EXCEPTION WHEN check_violation THEN NULL; END; DELETE FROM bell.areas WHERE tenant_id = 'assertion-tenant' AND id = 'assertion-area'; DELETE FROM bell.sites WHERE tenant_id = 'assertion-tenant' AND id = 'assertion-site'; END $assertions$; DO $control_api$ BEGIN IF NOT has_table_privilege('yovision_t012_sense', 'sense.control_idempotency_receipts', 'SELECT,INSERT,UPDATE,DELETE') OR NOT has_table_privilege('yovision_t012_sense', 'sense.batch_operations', 'SELECT,INSERT,UPDATE,DELETE') OR NOT has_table_privilege('yovision_t012_sense', 'sense.batch_operation_items', 'SELECT,INSERT,UPDATE,DELETE') THEN RAISE EXCEPTION 'Sense runtime lacks Control API table privileges'; END IF; IF has_table_privilege('public', 'sense.control_idempotency_receipts', 'SELECT') OR has_table_privilege('public', 'sense.batch_operations', 'SELECT') THEN RAISE EXCEPTION 'Control API durable state leaked to PUBLIC'; END IF; IF NOT EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema = 'sense' AND table_name = 'devices' AND column_name = 'resource_version' ) OR NOT EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema = 'sense' AND table_name = 'devices' AND column_name = 'profile_token' ) THEN RAISE EXCEPTION 'Control API device columns are missing'; END IF; END $control_api$; DO $reconcile_safety$ DECLARE target text; BEGIN FOREACH target IN ARRAY ARRAY[ 'sense.media_path_ownership', 'sense.operational_leases', 'sense.orphan_scan_runs', 'sense.orphan_scan_findings', 'sense.orphan_cleanup_actions' ] LOOP IF NOT has_table_privilege('yovision_t012_sense', target, 'SELECT,INSERT,UPDATE,DELETE') THEN RAISE EXCEPTION 'Sense runtime lacks reconciliation safety privilege on %', target; END IF; IF has_table_privilege('public', target, 'SELECT,INSERT,UPDATE,DELETE') THEN RAISE EXCEPTION 'Reconciliation safety state leaked to PUBLIC on %', target; END IF; END LOOP; IF NOT EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema = 'sense' AND table_name = 'reconcile_state' AND column_name = 'lease_token' ) THEN RAISE EXCEPTION 'reconcile fencing columns are missing'; END IF; END $reconcile_safety$; DO $audit_relay$ BEGIN IF NOT has_table_privilege('yovision_t012_sense', 'sense.device_operation_outbox', 'SELECT,INSERT,UPDATE,DELETE') OR has_table_privilege('yovision_t012_sense', 'bell.audit_events', 'SELECT,INSERT,UPDATE,DELETE') OR has_table_privilege('yovision_t012_sense', 'bell.audit_relay_receipts', 'SELECT,INSERT,UPDATE,DELETE') THEN RAISE EXCEPTION 'Sense runtime violates audit relay schema ownership'; END IF; IF NOT has_table_privilege('yovision_t015_bell', 'bell.audit_events', 'SELECT,INSERT') OR has_table_privilege('yovision_t015_bell', 'bell.audit_events', 'UPDATE,DELETE,TRUNCATE') OR NOT has_table_privilege('yovision_t015_bell', 'bell.audit_relay_receipts', 'SELECT,INSERT,DELETE') OR has_table_privilege('yovision_t015_bell', 'bell.audit_relay_receipts', 'UPDATE,TRUNCATE') THEN RAISE EXCEPTION 'Bell runtime violates audit relay privileges'; END IF; IF has_table_privilege('public', 'bell.audit_events', 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE') OR has_table_privilege('public', 'bell.audit_relay_receipts', 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE') THEN RAISE EXCEPTION 'Bell audit relay tables leaked to PUBLIC'; END IF; IF NOT EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema='sense' AND table_name='device_operation_outbox' AND column_name='relay_lease_token' ) THEN RAISE EXCEPTION 'Sense audit relay fencing columns are missing'; END IF; END $audit_relay$; DO $event_ingress$ BEGIN IF NOT has_table_privilege('yovision_t015_bell', 'bell.event_ingress_bindings', 'SELECT') OR has_table_privilege('yovision_t015_bell', 'bell.event_ingress_bindings', 'INSERT,UPDATE,DELETE,TRUNCATE') OR NOT has_table_privilege('yovision_t015_bell', 'bell.event_ingress_receipts', 'SELECT,INSERT') OR has_table_privilege('yovision_t015_bell', 'bell.event_ingress_receipts', 'UPDATE,DELETE,TRUNCATE') OR NOT has_table_privilege('yovision_t015_bell', 'bell.event_ingress_nonces', 'SELECT,INSERT,DELETE') OR has_table_privilege('yovision_t015_bell', 'bell.event_ingress_nonces', 'UPDATE,TRUNCATE') THEN RAISE EXCEPTION 'Bell runtime violates event ingress privileges'; END IF; IF NOT has_column_privilege('yovision_t015_bell', 'sense.devices', 'id', 'SELECT') OR NOT has_column_privilege('yovision_t015_bell', 'sense.devices', 'area_id', 'SELECT') OR has_column_privilege('yovision_t015_bell', 'sense.devices', 'endpoint_ref', 'SELECT') OR has_column_privilege('yovision_t015_bell', 'sense.devices', 'credential_ref', 'SELECT') OR has_column_privilege('yovision_t015_bell', 'sense.devices', 'profile_token', 'SELECT') OR has_column_privilege('yovision_t015_bell', 'sense.devices', 'path_name', 'SELECT') THEN RAISE EXCEPTION 'Bell runtime Sense device projection privilege drift'; END IF; IF has_table_privilege('public', 'bell.event_ingress_bindings', 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE') OR has_table_privilege('public', 'bell.event_ingress_receipts', 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE') OR has_table_privilege('public', 'bell.event_ingress_nonces', 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE') THEN RAISE EXCEPTION 'Bell event ingress tables leaked to PUBLIC'; END IF; END $event_ingress$;