131 lines
5.7 KiB
SQL
131 lines
5.7 KiB
SQL
-- Sense Outbox relay fencing and Bell global audit facts.
|
|||
|
|
|
||
|
|
ALTER TABLE sense.device_operation_outbox
|
||
|
|
ADD COLUMN IF NOT EXISTS relay_lease_owner text,
|
||
|
|
ADD COLUMN IF NOT EXISTS relay_lease_token bigint NOT NULL DEFAULT 0,
|
||
|
|
ADD COLUMN IF NOT EXISTS relay_lease_until timestamptz,
|
||
|
|
ADD COLUMN IF NOT EXISTS last_error_code text,
|
||
|
|
ADD COLUMN IF NOT EXISTS dead_lettered_at timestamptz;
|
||
|
|
|
||
|
|
ALTER TABLE sense.device_operation_outbox
|
||
|
|
DROP CONSTRAINT IF EXISTS sense_outbox_relay_lease_pair;
|
||
|
|
ALTER TABLE sense.device_operation_outbox
|
||
|
|
ADD CONSTRAINT sense_outbox_relay_lease_pair CHECK (
|
||
|
|
(relay_lease_owner IS NULL AND relay_lease_until IS NULL)
|
||
|
|
OR (relay_lease_owner IS NOT NULL AND btrim(relay_lease_owner) <> ''
|
||
|
|
AND relay_lease_until IS NOT NULL)
|
||
|
|
);
|
||
|
|
ALTER TABLE sense.device_operation_outbox
|
||
|
|
DROP CONSTRAINT IF EXISTS sense_outbox_relay_token_nonnegative;
|
||
|
|
ALTER TABLE sense.device_operation_outbox
|
||
|
|
ADD CONSTRAINT sense_outbox_relay_token_nonnegative CHECK (relay_lease_token >= 0);
|
||
|
|
ALTER TABLE sense.device_operation_outbox
|
||
|
|
DROP CONSTRAINT IF EXISTS sense_outbox_relay_error_length;
|
||
|
|
ALTER TABLE sense.device_operation_outbox
|
||
|
|
ADD CONSTRAINT sense_outbox_relay_error_length CHECK (
|
||
|
|
last_error_code IS NULL OR (
|
||
|
|
char_length(last_error_code) BETWEEN 1 AND 64
|
||
|
|
AND last_error_code ~ '^[a-z][a-z0-9_]*$'
|
||
|
|
)
|
||
|
|
);
|
||
|
|
ALTER TABLE sense.device_operation_outbox
|
||
|
|
DROP CONSTRAINT IF EXISTS sense_outbox_relay_terminal_state;
|
||
|
|
ALTER TABLE sense.device_operation_outbox
|
||
|
|
ADD CONSTRAINT sense_outbox_relay_terminal_state CHECK (
|
||
|
|
delivered_at IS NULL OR dead_lettered_at IS NULL
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS sense_outbox_relay_due_idx
|
||
|
|
ON sense.device_operation_outbox(
|
||
|
|
COALESCE(next_attempt_at, available_at), event_id
|
||
|
|
)
|
||
|
|
WHERE delivered_at IS NULL AND dead_lettered_at IS NULL;
|
||
|
|
|
||
|
|
CREATE TABLE IF NOT EXISTS bell.audit_events (
|
||
|
|
source_system text NOT NULL,
|
||
|
|
event_id text NOT NULL,
|
||
|
|
schema_version smallint NOT NULL,
|
||
|
|
event_type text NOT NULL,
|
||
|
|
tenant_id text NOT NULL,
|
||
|
|
site_id text NOT NULL,
|
||
|
|
device_id text NOT NULL,
|
||
|
|
actor_type text NOT NULL,
|
||
|
|
actor_id text NOT NULL,
|
||
|
|
reason text,
|
||
|
|
trace_id text,
|
||
|
|
aggregate_generation bigint NOT NULL,
|
||
|
|
quota_source_version bigint,
|
||
|
|
area_policy_source_version bigint,
|
||
|
|
payload jsonb NOT NULL,
|
||
|
|
occurred_at timestamptz NOT NULL,
|
||
|
|
received_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||
|
|
record_hash bytea NOT NULL,
|
||
|
|
PRIMARY KEY (source_system, event_id),
|
||
|
|
CONSTRAINT bell_audit_source_system CHECK (source_system = 'sense'),
|
||
|
|
CONSTRAINT bell_audit_event_id CHECK (event_id ~ '^audit_[0-9a-f]{32}$'),
|
||
|
|
CONSTRAINT bell_audit_schema_version CHECK (schema_version IN (1, 2)),
|
||
|
|
CONSTRAINT bell_audit_event_type CHECK (event_type IN (
|
||
|
|
'device.created',
|
||
|
|
'device.desired_state.accepted',
|
||
|
|
'device.configuration.accepted'
|
||
|
|
)),
|
||
|
|
CONSTRAINT bell_audit_identity_not_blank CHECK (
|
||
|
|
btrim(tenant_id) <> '' AND btrim(site_id) <> '' AND btrim(device_id) <> ''
|
||
|
|
AND btrim(actor_id) <> ''
|
||
|
|
),
|
||
|
|
CONSTRAINT bell_audit_actor_type CHECK (actor_type IN ('user', 'service', 'system')),
|
||
|
|
CONSTRAINT bell_audit_generation_positive CHECK (aggregate_generation >= 1),
|
||
|
|
CONSTRAINT bell_audit_projection_versions CHECK (
|
||
|
|
(quota_source_version IS NULL OR quota_source_version >= 1)
|
||
|
|
AND (area_policy_source_version IS NULL OR area_policy_source_version >= 1)
|
||
|
|
),
|
||
|
|
CONSTRAINT bell_audit_reason_length CHECK (reason IS NULL OR char_length(reason) <= 500),
|
||
|
|
CONSTRAINT bell_audit_trace_length CHECK (trace_id IS NULL OR char_length(trace_id) <= 128),
|
||
|
|
CONSTRAINT bell_audit_payload_object CHECK (jsonb_typeof(payload) = 'object'),
|
||
|
|
CONSTRAINT bell_audit_record_hash_length CHECK (octet_length(record_hash) = 32)
|
||
|
|
);
|
||
|
|
ALTER TABLE bell.audit_events OWNER TO bell_app;
|
||
|
|
|
||
|
|
DROP TRIGGER IF EXISTS bell_audit_events_immutable ON bell.audit_events;
|
||
|
|
CREATE TRIGGER bell_audit_events_immutable
|
||
|
|
BEFORE UPDATE OR DELETE ON bell.audit_events
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION bell.reject_immutable_change();
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS bell_audit_scope_time_idx
|
||
|
|
ON bell.audit_events(tenant_id, site_id, occurred_at DESC, event_id DESC);
|
||
|
|
CREATE INDEX IF NOT EXISTS bell_audit_device_time_idx
|
||
|
|
ON bell.audit_events(tenant_id, site_id, device_id, occurred_at DESC, event_id DESC);
|
||
|
|
|
||
|
|
CREATE TABLE IF NOT EXISTS bell.audit_relay_receipts (
|
||
|
|
key_id text NOT NULL,
|
||
|
|
nonce text NOT NULL,
|
||
|
|
request_hash bytea NOT NULL,
|
||
|
|
response_status integer NOT NULL,
|
||
|
|
response_body jsonb NOT NULL,
|
||
|
|
received_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||
|
|
expires_at timestamptz NOT NULL,
|
||
|
|
PRIMARY KEY (key_id, nonce),
|
||
|
|
CONSTRAINT bell_audit_receipt_key_id CHECK (
|
||
|
|
char_length(key_id) BETWEEN 1 AND 64 AND key_id ~ '^[A-Za-z0-9][A-Za-z0-9._-]*$'
|
||
|
|
),
|
||
|
|
CONSTRAINT bell_audit_receipt_nonce CHECK (
|
||
|
|
char_length(nonce) BETWEEN 22 AND 64 AND nonce ~ '^[A-Za-z0-9_-]+$'
|
||
|
|
),
|
||
|
|
CONSTRAINT bell_audit_receipt_hash_length CHECK (octet_length(request_hash) = 32),
|
||
|
|
CONSTRAINT bell_audit_receipt_status CHECK (response_status = 200),
|
||
|
|
CONSTRAINT bell_audit_receipt_body CHECK (jsonb_typeof(response_body) = 'object'),
|
||
|
|
CONSTRAINT bell_audit_receipt_ttl CHECK (
|
||
|
|
expires_at >= received_at + interval '10 minutes'
|
||
|
|
AND expires_at <= received_at + interval '11 minutes'
|
||
|
|
)
|
||
|
|
);
|
||
|
|
ALTER TABLE bell.audit_relay_receipts OWNER TO bell_app;
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS bell_audit_receipt_expiry_idx
|
||
|
|
ON bell.audit_relay_receipts(expires_at, key_id, nonce);
|
||
|
|
|
||
|
|
INSERT INTO bell.schema_migrations(version) VALUES (4)
|
||
|
|
ON CONFLICT (version) DO NOTHING;
|
||
|
|
INSERT INTO sense.schema_migrations(version) VALUES (6)
|
||
|
|
ON CONFLICT (version) DO NOTHING;
|