Publish ITWorx Pulse source
Public source validation / validate (push) Failing after 3m8s

This commit is contained in:
ITWorx Pulse release export
2026-09-03 02:09:19 +02:00
commit bd774932d5
614 changed files with 77116 additions and 0 deletions
@@ -0,0 +1,154 @@
CREATE TABLE users (
id uuid PRIMARY KEY,
external_subject text NOT NULL UNIQUE,
display_name text NOT NULL,
email text,
status text NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled')),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
last_login_at timestamptz
);
CREATE TABLE roles (
id uuid PRIMARY KEY,
name text NOT NULL UNIQUE CHECK (name IN ('viewer', 'operator', 'editor', 'administrator')),
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE user_roles (
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role_id uuid NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
created_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (user_id, role_id)
);
CREATE TABLE data_sources (
id uuid PRIMARY KEY,
type text NOT NULL,
name text NOT NULL,
enabled boolean NOT NULL DEFAULT true,
configuration_ref text NOT NULL,
capability_document jsonb NOT NULL DEFAULT '{}'::jsonb,
health_state text NOT NULL DEFAULT 'unknown' CHECK (health_state IN ('healthy', 'degraded', 'unhealthy', 'unknown')),
last_success_at timestamptz,
last_error_code text,
last_error_message text,
freshness_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE collectors (
id uuid PRIMARY KEY,
datasource_id uuid NOT NULL REFERENCES data_sources(id) ON DELETE CASCADE,
kind text NOT NULL,
version text NOT NULL,
heartbeat timestamptz,
capabilities jsonb NOT NULL DEFAULT '[]'::jsonb,
status text NOT NULL DEFAULT 'unknown',
UNIQUE (datasource_id, kind)
);
CREATE TABLE entities (
id uuid PRIMARY KEY,
entity_type text NOT NULL,
canonical_name text NOT NULL,
display_name text NOT NULL,
status text NOT NULL DEFAULT 'unknown',
status_reasons jsonb NOT NULL DEFAULT '[]'::jsonb,
first_seen_at timestamptz NOT NULL,
last_seen_at timestamptz,
tombstoned_at timestamptz,
attributes jsonb NOT NULL DEFAULT '{}'::jsonb
);
CREATE TABLE entity_aliases (
entity_id uuid NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
source_id uuid NOT NULL REFERENCES data_sources(id) ON DELETE CASCADE,
external_type text NOT NULL,
external_id text NOT NULL,
PRIMARY KEY (source_id, external_type, external_id)
);
CREATE TABLE dashboards (
id uuid PRIMARY KEY,
slug text NOT NULL UNIQUE,
name text NOT NULL,
description text NOT NULL DEFAULT '',
owner_user_id uuid REFERENCES users(id) ON DELETE SET NULL,
scope text NOT NULL CHECK (scope IN ('personal', 'shared', 'system')),
archived_at timestamptz,
current_version_id uuid,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE dashboard_versions (
id uuid PRIMARY KEY,
dashboard_id uuid NOT NULL REFERENCES dashboards(id) ON DELETE CASCADE,
version_number integer NOT NULL CHECK (version_number > 0),
schema_version integer NOT NULL CHECK (schema_version > 0),
document jsonb NOT NULL,
change_summary text NOT NULL DEFAULT '',
created_by uuid REFERENCES users(id) ON DELETE SET NULL,
created_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (dashboard_id, version_number)
);
ALTER TABLE dashboards
ADD CONSTRAINT dashboards_current_version_fk
FOREIGN KEY (current_version_id) REFERENCES dashboard_versions(id) ON DELETE SET NULL;
CREATE TABLE events (
id uuid PRIMARY KEY,
event_type text NOT NULL,
severity text NOT NULL,
entity_id uuid REFERENCES entities(id) ON DELETE SET NULL,
source_id uuid REFERENCES data_sources(id) ON DELETE SET NULL,
occurred_at timestamptz NOT NULL,
received_at timestamptz NOT NULL DEFAULT now(),
dedup_key text NOT NULL,
summary text NOT NULL,
attributes jsonb NOT NULL DEFAULT '{}'::jsonb,
correlation_id text,
UNIQUE (source_id, dedup_key, occurred_at)
);
CREATE TABLE audit_events (
id uuid PRIMARY KEY,
actor text NOT NULL,
action text NOT NULL,
resource_type text NOT NULL,
resource_id uuid,
result text NOT NULL,
occurred_at timestamptz NOT NULL DEFAULT now(),
correlation_id text,
before_diff jsonb,
after_diff jsonb
);
CREATE TABLE job_runs (
id uuid PRIMARY KEY,
job_type text NOT NULL,
job_key text NOT NULL,
scheduled_at timestamptz NOT NULL,
started_at timestamptz,
completed_at timestamptz,
status text NOT NULL,
counts jsonb NOT NULL DEFAULT '{}'::jsonb,
error_code text,
correlation_id text,
UNIQUE (job_type, job_key, scheduled_at)
);
CREATE TABLE system_settings (
key text PRIMARY KEY,
value jsonb NOT NULL,
version bigint NOT NULL DEFAULT 1 CHECK (version > 0),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX entities_type_status_idx ON entities (entity_type, status);
CREATE INDEX events_occurred_at_idx ON events (occurred_at DESC);
CREATE INDEX audit_events_occurred_at_idx ON audit_events (occurred_at DESC);
CREATE INDEX job_runs_status_idx ON job_runs (status, scheduled_at);
@@ -0,0 +1,36 @@
CREATE TABLE entity_facts (
entity_id uuid NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
field_name text NOT NULL,
source_id uuid NOT NULL REFERENCES data_sources(id) ON DELETE CASCADE,
value jsonb NOT NULL,
observed_at timestamptz NOT NULL,
confidence numeric(5,4) NOT NULL CHECK (confidence >= 0 AND confidence <= 1),
valid_until timestamptz,
PRIMARY KEY (entity_id, field_name, source_id)
);
CREATE TABLE entity_overrides (
entity_id uuid NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
field_name text NOT NULL,
value jsonb NOT NULL,
user_id uuid REFERENCES users(id) ON DELETE SET NULL,
updated_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (entity_id, field_name)
);
CREATE TABLE entity_relations (
id uuid PRIMARY KEY,
source_entity_id uuid NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
relation_type text NOT NULL,
target_entity_id uuid NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
source_id uuid NOT NULL REFERENCES data_sources(id) ON DELETE CASCADE,
confidence numeric(5,4) NOT NULL CHECK (confidence >= 0 AND confidence <= 1),
confirmed boolean NOT NULL DEFAULT false,
first_seen_at timestamptz NOT NULL,
last_seen_at timestamptz,
tombstoned_at timestamptz,
UNIQUE (source_entity_id, relation_type, target_entity_id, source_id)
);
CREATE INDEX entity_facts_source_observed_idx ON entity_facts (source_id, observed_at DESC);
CREATE INDEX entity_relations_source_idx ON entity_relations (source_id, last_seen_at DESC);
@@ -0,0 +1,11 @@
CREATE OR REPLACE FUNCTION prevent_dashboard_version_mutation() RETURNS trigger AS $$
BEGIN
RAISE EXCEPTION 'dashboard versions are immutable';
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER dashboard_versions_immutable_update
BEFORE UPDATE ON dashboard_versions
FOR EACH ROW EXECUTE FUNCTION prevent_dashboard_version_mutation();
CREATE INDEX IF NOT EXISTS dashboard_versions_created_at_idx ON dashboard_versions (dashboard_id, created_at DESC, version_number DESC);
@@ -0,0 +1,6 @@
ALTER TABLE dashboards ADD COLUMN IF NOT EXISTS revision bigint NOT NULL DEFAULT 1 CHECK (revision > 0);
CREATE INDEX IF NOT EXISTS dashboards_owner_revision_idx ON dashboards (owner_user_id, revision DESC, id ASC);
CREATE INDEX IF NOT EXISTS dashboards_name_id_idx ON dashboards (name ASC, id ASC);
CREATE INDEX IF NOT EXISTS dashboards_scope_name_id_idx ON dashboards (scope, name ASC, id ASC);
CREATE INDEX IF NOT EXISTS dashboards_owner_name_id_idx ON dashboards (owner_user_id, name ASC, id ASC);
@@ -0,0 +1,124 @@
CREATE TABLE services (
id uuid PRIMARY KEY,
entity_id uuid REFERENCES entities(id) ON DELETE SET NULL,
source_id uuid REFERENCES data_sources(id) ON DELETE SET NULL,
name text NOT NULL CHECK (length(name) BETWEEN 1 AND 160),
description text NOT NULL DEFAULT '',
state text NOT NULL DEFAULT 'unknown' CHECK (state IN ('up', 'degraded', 'down', 'unknown')),
labels jsonb NOT NULL DEFAULT '{}'::jsonb,
revision bigint NOT NULL DEFAULT 1 CHECK (revision > 0),
archived_at timestamptz,
created_by uuid REFERENCES users(id) ON DELETE SET NULL,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX services_entity_idx ON services (entity_id) WHERE archived_at IS NULL;
CREATE INDEX services_source_state_idx ON services (source_id, state, updated_at DESC);
CREATE TABLE service_endpoints (
id uuid PRIMARY KEY,
service_id uuid NOT NULL REFERENCES services(id) ON DELETE RESTRICT,
source_id uuid REFERENCES data_sources(id) ON DELETE SET NULL,
name text NOT NULL CHECK (length(name) BETWEEN 1 AND 160),
endpoint_type text NOT NULL CHECK (endpoint_type IN ('http', 'tcp', 'dns', 'icmp', 'tls')),
target jsonb NOT NULL,
enabled boolean NOT NULL DEFAULT true,
revision bigint NOT NULL DEFAULT 1 CHECK (revision > 0),
archived_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX service_endpoints_active_idx ON service_endpoints (service_id, enabled) WHERE archived_at IS NULL;
CREATE UNIQUE INDEX service_endpoints_active_name_idx ON service_endpoints (service_id, name) WHERE archived_at IS NULL;
CREATE TABLE probes (
id uuid PRIMARY KEY,
service_id uuid NOT NULL REFERENCES services(id) ON DELETE RESTRICT,
endpoint_id uuid REFERENCES service_endpoints(id) ON DELETE SET NULL,
source_id uuid REFERENCES data_sources(id) ON DELETE SET NULL,
name text NOT NULL CHECK (length(name) BETWEEN 1 AND 160),
probe_type text NOT NULL CHECK (probe_type IN ('http', 'tcp', 'dns', 'icmp', 'tls')),
target jsonb NOT NULL,
interval_seconds integer NOT NULL CHECK (interval_seconds BETWEEN 5 AND 86400),
timeout_seconds integer NOT NULL CHECK (timeout_seconds BETWEEN 1 AND 120),
enabled boolean NOT NULL DEFAULT true,
expected_status_codes jsonb NOT NULL DEFAULT '[]'::jsonb,
follow_redirects boolean NOT NULL DEFAULT false,
verify_tls boolean NOT NULL DEFAULT true,
content_assertion jsonb,
secret_reference text,
network_policy_id uuid,
revision bigint NOT NULL DEFAULT 1 CHECK (revision > 0),
archived_at timestamptz,
created_by uuid REFERENCES users(id) ON DELETE SET NULL,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX probes_schedule_idx ON probes (enabled, interval_seconds, updated_at) WHERE archived_at IS NULL;
CREATE INDEX probes_service_idx ON probes (service_id, updated_at DESC);
CREATE UNIQUE INDEX probes_active_name_idx ON probes (service_id, name) WHERE archived_at IS NULL;
CREATE TABLE probe_results (
id uuid PRIMARY KEY,
probe_id uuid NOT NULL REFERENCES probes(id) ON DELETE RESTRICT,
source_id uuid REFERENCES data_sources(id) ON DELETE SET NULL,
observed_at timestamptz NOT NULL,
completed_at timestamptz NOT NULL,
state text NOT NULL CHECK (state IN ('up', 'degraded', 'down', 'unknown')),
response_time_ms integer CHECK (response_time_ms IS NULL OR response_time_ms >= 0),
status_code integer CHECK (status_code IS NULL OR status_code BETWEEN 100 AND 599),
error_class text,
error_message text,
attributes jsonb NOT NULL DEFAULT '{}'::jsonb,
UNIQUE (probe_id, observed_at)
);
CREATE INDEX probe_results_history_idx ON probe_results (probe_id, observed_at DESC);
CREATE INDEX probe_results_state_idx ON probe_results (state, observed_at DESC);
CREATE TABLE service_certificates (
id uuid PRIMARY KEY,
service_id uuid NOT NULL REFERENCES services(id) ON DELETE RESTRICT,
endpoint_id uuid REFERENCES service_endpoints(id) ON DELETE SET NULL,
source_id uuid REFERENCES data_sources(id) ON DELETE SET NULL,
observed_at timestamptz NOT NULL,
expires_at timestamptz,
issuer text,
subject text,
hostname_valid boolean,
verification_state text NOT NULL CHECK (verification_state IN ('valid', 'attention', 'invalid', 'unknown')),
attributes jsonb NOT NULL DEFAULT '{}'::jsonb,
UNIQUE (service_id, endpoint_id, observed_at)
);
CREATE INDEX service_certificates_expiry_idx ON service_certificates (expires_at, observed_at DESC);
CREATE UNIQUE INDEX service_certificates_without_endpoint_unique_idx ON service_certificates (service_id, observed_at) WHERE endpoint_id IS NULL;
CREATE TABLE service_dependencies (
id uuid PRIMARY KEY,
service_id uuid NOT NULL REFERENCES services(id) ON DELETE RESTRICT,
depends_on_service_id uuid NOT NULL REFERENCES services(id) ON DELETE RESTRICT,
source_id uuid REFERENCES data_sources(id) ON DELETE SET NULL,
relation_type text NOT NULL CHECK (relation_type IN ('depends_on', 'backs', 'exposes')),
confidence numeric(5,4) NOT NULL CHECK (confidence >= 0 AND confidence <= 1),
confirmed boolean NOT NULL DEFAULT false,
first_seen_at timestamptz NOT NULL,
last_seen_at timestamptz,
archived_at timestamptz,
UNIQUE (service_id, depends_on_service_id, relation_type, source_id),
CHECK (service_id <> depends_on_service_id)
);
CREATE INDEX service_dependencies_source_idx ON service_dependencies (source_id, last_seen_at DESC);
CREATE UNIQUE INDEX service_dependencies_manual_unique_idx ON service_dependencies (service_id, depends_on_service_id, relation_type) WHERE source_id IS NULL;
CREATE TABLE service_permissions (
service_id uuid NOT NULL REFERENCES services(id) ON DELETE CASCADE,
role_id uuid NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
permission text NOT NULL CHECK (permission IN ('view', 'operate', 'edit', 'admin')),
created_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (service_id, role_id, permission)
);
@@ -0,0 +1,41 @@
CREATE TABLE alert_rules (
id uuid PRIMARY KEY,
schema_version integer NOT NULL CHECK (schema_version = 1),
name text NOT NULL CHECK (length(name) BETWEEN 1 AND 160),
enabled boolean NOT NULL DEFAULT false,
severity text NOT NULL CHECK (severity IN ('attention', 'degraded', 'critical')),
scope jsonb NOT NULL DEFAULT '{}'::jsonb,
condition jsonb NOT NULL,
evaluation_interval_seconds integer NOT NULL CHECK (evaluation_interval_seconds BETWEEN 5 AND 3600),
pending_seconds integer NOT NULL CHECK (pending_seconds BETWEEN 0 AND 2592000),
resolve_seconds integer NOT NULL CHECK (resolve_seconds BETWEEN 0 AND 2592000),
unknown_behavior text NOT NULL CHECK (unknown_behavior IN ('retain-firing-as-unknown', 'become-unknown', 'ignore-short-gap')),
group_by jsonb NOT NULL DEFAULT '[]'::jsonb,
suppress_when jsonb NOT NULL DEFAULT '[]'::jsonb,
message jsonb NOT NULL,
current_version_id uuid,
revision bigint NOT NULL DEFAULT 1 CHECK (revision > 0),
created_by uuid REFERENCES users(id) ON DELETE SET NULL,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE alert_rule_versions (
id uuid PRIMARY KEY,
rule_id uuid NOT NULL REFERENCES alert_rules(id) ON DELETE RESTRICT,
version_number integer NOT NULL CHECK (version_number > 0),
document jsonb NOT NULL,
change_summary text NOT NULL DEFAULT '' CHECK (length(change_summary) <= 500),
created_by uuid REFERENCES users(id) ON DELETE SET NULL,
created_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (rule_id, version_number)
);
ALTER TABLE alert_rules
ADD CONSTRAINT alert_rules_current_version_fk
FOREIGN KEY (current_version_id) REFERENCES alert_rule_versions(id)
ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX alert_rules_enabled_idx ON alert_rules (enabled, evaluation_interval_seconds, updated_at DESC) WHERE enabled = true;
CREATE INDEX alert_rules_severity_idx ON alert_rules (severity, updated_at DESC);
CREATE INDEX alert_rule_versions_history_idx ON alert_rule_versions (rule_id, version_number DESC);
@@ -0,0 +1,6 @@
ALTER TABLE job_runs
ADD COLUMN lease_owner text,
ADD COLUMN lease_until timestamptz;
CREATE INDEX job_runs_lease_idx ON job_runs (job_type, job_key, scheduled_at, lease_until)
WHERE status IN ('queued', 'running');
@@ -0,0 +1,41 @@
CREATE TABLE alert_instances (
id uuid PRIMARY KEY,
rule_id uuid NOT NULL REFERENCES alert_rules(id) ON DELETE RESTRICT,
rule_version_id uuid NOT NULL REFERENCES alert_rule_versions(id) ON DELETE RESTRICT,
fingerprint text NOT NULL CHECK (length(fingerprint) BETWEEN 1 AND 160),
entity_id uuid REFERENCES entities(id) ON DELETE RESTRICT,
current_state text NOT NULL DEFAULT 'inactive' CHECK (current_state IN ('inactive', 'pending', 'firing', 'acknowledged', 'resolved', 'unknown')),
retained_state text NOT NULL DEFAULT 'inactive' CHECK (retained_state IN ('inactive', 'pending', 'firing', 'acknowledged', 'resolved', 'unknown')),
active_since timestamptz,
recovery_since timestamptz,
last_evaluated_at timestamptz NOT NULL,
last_known_at timestamptz,
last_value jsonb NOT NULL DEFAULT 'null'::jsonb,
reason text NOT NULL DEFAULT '' CHECK (length(reason) <= 500),
source_health jsonb NOT NULL DEFAULT '{}'::jsonb,
acknowledged_by text,
acknowledged_at timestamptz,
revision bigint NOT NULL DEFAULT 1 CHECK (revision > 0),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (rule_id, fingerprint)
);
CREATE TABLE alert_occurrences (
id uuid PRIMARY KEY,
instance_id uuid NOT NULL REFERENCES alert_instances(id) ON DELETE RESTRICT,
evaluation_key text NOT NULL CHECK (length(evaluation_key) BETWEEN 1 AND 160),
event_type text NOT NULL CHECK (event_type IN ('evaluation', 'transition', 'acknowledge')),
from_state text NOT NULL CHECK (from_state IN ('inactive', 'pending', 'firing', 'acknowledged', 'resolved', 'unknown')),
to_state text NOT NULL CHECK (to_state IN ('inactive', 'pending', 'firing', 'acknowledged', 'resolved', 'unknown')),
observed_at timestamptz NOT NULL,
value jsonb NOT NULL DEFAULT 'null'::jsonb,
reason text NOT NULL DEFAULT '' CHECK (length(reason) <= 500),
source_health jsonb NOT NULL DEFAULT '{}'::jsonb,
created_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (instance_id, evaluation_key)
);
CREATE INDEX alert_instances_state_idx ON alert_instances (current_state, last_evaluated_at DESC, id ASC);
CREATE INDEX alert_instances_rule_idx ON alert_instances (rule_id, current_state, updated_at DESC, id ASC);
CREATE INDEX alert_occurrences_history_idx ON alert_occurrences (instance_id, observed_at DESC, id ASC);
@@ -0,0 +1,8 @@
ALTER TABLE alert_rules
ADD COLUMN cooldown_seconds integer NOT NULL DEFAULT 0 CHECK (cooldown_seconds BETWEEN 0 AND 2592000);
ALTER TABLE alert_instances
ADD COLUMN cooldown_until timestamptz;
CREATE INDEX alert_instances_cooldown_idx ON alert_instances (cooldown_until, current_state, id)
WHERE cooldown_until IS NOT NULL;
@@ -0,0 +1,42 @@
CREATE TABLE alert_silences (
id uuid PRIMARY KEY,
name text NOT NULL CHECK (char_length(name) BETWEEN 1 AND 160),
reason text NOT NULL CHECK (char_length(reason) BETWEEN 1 AND 500),
owner text NOT NULL CHECK (char_length(owner) BETWEEN 1 AND 255),
matchers jsonb NOT NULL CHECK (jsonb_typeof(matchers) = 'object'),
starts_at timestamptz NOT NULL,
expires_at timestamptz NOT NULL CHECK (expires_at > starts_at),
status text NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'expired', 'revoked')),
created_by text NOT NULL CHECK (char_length(created_by) BETWEEN 1 AND 255),
created_at timestamptz NOT NULL DEFAULT now(),
revoked_by text,
revoked_at timestamptz,
expired_at timestamptz,
revision bigint NOT NULL DEFAULT 1 CHECK (revision > 0),
CHECK ((status = 'revoked' AND revoked_at IS NOT NULL) OR (status <> 'revoked' AND revoked_at IS NULL)),
CHECK ((status = 'expired' AND expired_at IS NOT NULL) OR (status <> 'expired' AND expired_at IS NULL))
);
CREATE INDEX alert_silences_active_expiry_idx ON alert_silences (expires_at, id) WHERE status = 'active';
CREATE INDEX alert_silences_listing_idx ON alert_silences (starts_at DESC, id DESC);
CREATE TABLE maintenance_windows (
id uuid PRIMARY KEY,
name text NOT NULL CHECK (char_length(name) BETWEEN 1 AND 160),
reason text NOT NULL CHECK (char_length(reason) BETWEEN 1 AND 500),
selector jsonb NOT NULL CHECK (jsonb_typeof(selector) = 'object'),
starts_at timestamptz NOT NULL,
ends_at timestamptz NOT NULL CHECK (ends_at > starts_at),
status text NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'expired', 'revoked')),
created_by text NOT NULL CHECK (char_length(created_by) BETWEEN 1 AND 255),
created_at timestamptz NOT NULL DEFAULT now(),
revoked_by text,
revoked_at timestamptz,
expired_at timestamptz,
revision bigint NOT NULL DEFAULT 1 CHECK (revision > 0),
CHECK ((status = 'revoked' AND revoked_at IS NOT NULL) OR (status <> 'revoked' AND revoked_at IS NULL)),
CHECK ((status = 'expired' AND expired_at IS NOT NULL) OR (status <> 'expired' AND expired_at IS NULL))
);
CREATE INDEX maintenance_windows_active_expiry_idx ON maintenance_windows (ends_at, id) WHERE status = 'active';
CREATE INDEX maintenance_windows_listing_idx ON maintenance_windows (starts_at DESC, id DESC);
@@ -0,0 +1,8 @@
ALTER TABLE alert_occurrences
DROP CONSTRAINT alert_occurrences_event_type_check;
ALTER TABLE alert_occurrences
ADD CONSTRAINT alert_occurrences_event_type_check
CHECK (event_type IN ('evaluation', 'transition', 'acknowledge', 'unacknowledge'));
CREATE INDEX alert_instances_acknowledged_idx ON alert_instances (acknowledged_at DESC, id ASC) WHERE current_state = 'acknowledged';
@@ -0,0 +1,42 @@
CREATE TABLE notification_channels (
id uuid PRIMARY KEY,
name text NOT NULL CHECK (char_length(name) BETWEEN 1 AND 160),
channel_type text NOT NULL CHECK (channel_type IN ('memory', 'webhook', 'email')),
enabled boolean NOT NULL DEFAULT true,
secret_ref text NOT NULL CHECK (char_length(secret_ref) BETWEEN 1 AND 255),
configuration jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(configuration) = 'object'),
revision bigint NOT NULL DEFAULT 1 CHECK (revision > 0),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE notification_outbox (
id uuid PRIMARY KEY,
idempotency_key text NOT NULL UNIQUE CHECK (char_length(idempotency_key) BETWEEN 1 AND 255),
channel_id uuid NOT NULL REFERENCES notification_channels(id) ON DELETE RESTRICT,
event_type text NOT NULL CHECK (event_type IN ('firing', 'recovery', 'unknown')),
subject text NOT NULL CHECK (char_length(subject) BETWEEN 1 AND 240),
body text NOT NULL CHECK (char_length(body) BETWEEN 1 AND 8000),
status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'delivering', 'retry', 'delivered', 'failed')),
attempts integer NOT NULL DEFAULT 0 CHECK (attempts >= 0 AND attempts <= 10),
next_attempt_at timestamptz NOT NULL DEFAULT now(),
locked_until timestamptz,
last_error text CHECK (last_error IS NULL OR char_length(last_error) <= 500),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
delivered_at timestamptz
);
CREATE TABLE notification_deliveries (
id uuid PRIMARY KEY,
outbox_id uuid NOT NULL REFERENCES notification_outbox(id) ON DELETE CASCADE,
attempt integer NOT NULL CHECK (attempt > 0),
status text NOT NULL CHECK (status IN ('delivering', 'delivered', 'failed')),
error text CHECK (error IS NULL OR char_length(error) <= 500),
occurred_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (outbox_id, attempt)
);
CREATE INDEX notification_outbox_due_idx ON notification_outbox (next_attempt_at, id) WHERE status IN ('pending', 'retry');
CREATE INDEX notification_outbox_channel_idx ON notification_outbox (channel_id, status, updated_at DESC, id ASC);
CREATE INDEX notification_deliveries_history_idx ON notification_deliveries (outbox_id, occurred_at DESC, id ASC);
@@ -0,0 +1,48 @@
CREATE TABLE incidents (
id uuid PRIMARY KEY,
correlation_key text NOT NULL CHECK (char_length(correlation_key) BETWEEN 1 AND 255),
title text NOT NULL CHECK (char_length(title) BETWEEN 1 AND 240),
summary text NOT NULL DEFAULT '' CHECK (char_length(summary) <= 2000),
severity text NOT NULL CHECK (severity IN ('attention', 'degraded', 'critical')),
status text NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'acknowledged', 'resolved')),
started_at timestamptz NOT NULL,
resolved_at timestamptz,
owner_user_id uuid REFERENCES users(id) ON DELETE SET NULL,
correlation_method text NOT NULL CHECK (char_length(correlation_method) BETWEEN 1 AND 80),
confidence numeric(4,3) NOT NULL CHECK (confidence >= 0 AND confidence <= 1),
revision bigint NOT NULL DEFAULT 1 CHECK (revision > 0),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
CHECK (status <> 'resolved' OR resolved_at IS NOT NULL),
CHECK (status = 'resolved' OR resolved_at IS NULL)
);
CREATE UNIQUE INDEX incidents_active_correlation_key_uq ON incidents (correlation_key) WHERE status <> 'resolved';
CREATE INDEX incidents_list_idx ON incidents (status, severity, updated_at DESC, id ASC);
CREATE INDEX incidents_correlation_idx ON incidents (correlation_key, updated_at DESC, id ASC);
CREATE TABLE incident_alerts (
incident_id uuid NOT NULL REFERENCES incidents(id) ON DELETE CASCADE,
alert_id uuid NOT NULL REFERENCES alert_instances(id) ON DELETE RESTRICT,
rationale text NOT NULL CHECK (char_length(rationale) BETWEEN 1 AND 500),
confidence numeric(4,3) NOT NULL CHECK (confidence >= 0 AND confidence <= 1),
correlation_method text NOT NULL CHECK (char_length(correlation_method) BETWEEN 1 AND 80),
is_manual boolean NOT NULL DEFAULT false,
added_by text NOT NULL DEFAULT '' CHECK (char_length(added_by) <= 160),
created_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (incident_id, alert_id)
);
CREATE INDEX incident_alerts_alert_idx ON incident_alerts (alert_id, incident_id);
CREATE INDEX incident_alerts_incident_idx ON incident_alerts (incident_id, created_at ASC, alert_id ASC);
CREATE TABLE incident_entities (
incident_id uuid NOT NULL REFERENCES incidents(id) ON DELETE CASCADE,
entity_id uuid NOT NULL REFERENCES entities(id) ON DELETE RESTRICT,
rationale text NOT NULL CHECK (char_length(rationale) BETWEEN 1 AND 500),
confidence numeric(4,3) NOT NULL CHECK (confidence >= 0 AND confidence <= 1),
created_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (incident_id, entity_id)
);
CREATE INDEX incident_entities_entity_idx ON incident_entities (entity_id, incident_id);
@@ -0,0 +1,9 @@
CREATE TABLE incident_notes (
id uuid PRIMARY KEY,
incident_id uuid NOT NULL REFERENCES incidents(id) ON DELETE CASCADE,
author text NOT NULL CHECK (char_length(author) BETWEEN 1 AND 160),
body text NOT NULL CHECK (char_length(body) BETWEEN 1 AND 2000),
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX incident_notes_history_idx ON incident_notes (incident_id, created_at ASC, id ASC);
@@ -0,0 +1 @@
CREATE INDEX IF NOT EXISTS entities_canonical_name_idx ON entities (canonical_name ASC, id ASC);
@@ -0,0 +1,17 @@
-- pulse-agent writes the newest bounded telemetry snapshot per capability here and
-- pulse-api reads it. There is exactly one row per (agent, capability): history lives in
-- Prometheus, not in this table, so the transport cannot grow without bound.
CREATE TABLE agent_snapshots (
agent_id text NOT NULL CHECK (char_length(agent_id) BETWEEN 1 AND 128),
capability text NOT NULL CHECK (capability IN ('host', 'processes', 'containers', 'array', 'disks', 'pools', 'shares')),
observed_at timestamptz NOT NULL,
received_at timestamptz NOT NULL DEFAULT now(),
-- The authoritative size bound is enforced by the writer against the encoded payload
-- (agentstore.MaxPayloadBytes). This check is a storage backstop against a writer that
-- bypasses the store; pg_column_size reports the stored, possibly compressed size.
payload jsonb NOT NULL CHECK (jsonb_typeof(payload) = 'object' AND pg_column_size(payload) <= 2097152),
PRIMARY KEY (agent_id, capability)
);
-- The API reads the newest snapshot for one capability across agents on every request.
CREATE INDEX agent_snapshots_capability_freshness_idx ON agent_snapshots (capability, observed_at DESC);
@@ -0,0 +1,38 @@
-- Worker runtime support.
--
-- container_aliases is the durable memory the background discovery job needs to
-- be idempotent across restarts: reconciliation.ReconcileContainers must be able
-- to compare the current runtime snapshot against the previous one to keep a
-- stable entity identity across container recreation, and lifecycle event
-- derivation must compare the previous observed state/health/restart count to
-- decide whether anything actually changed. Both inputs are per runtime alias,
-- not per entity, so they cannot be expressed with entity_aliases (which has no
-- runtime identity or observation columns).
--
-- A runtime alias that stops being observed is tombstoned, never deleted, which
-- is what keeps a temporarily unhealthy source from erasing inventory.
CREATE TABLE container_aliases (
source_id uuid NOT NULL REFERENCES data_sources(id) ON DELETE CASCADE,
runtime_id text NOT NULL CHECK (length(runtime_id) BETWEEN 1 AND 255),
entity_id uuid NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
name text NOT NULL CHECK (length(name) BETWEEN 1 AND 255),
project text NOT NULL DEFAULT '' CHECK (length(project) <= 255),
service text NOT NULL DEFAULT '' CHECK (length(service) <= 255),
image_digest text NOT NULL DEFAULT '' CHECK (length(image_digest) <= 255),
observed_state text NOT NULL DEFAULT '' CHECK (length(observed_state) <= 64),
observed_health text NOT NULL DEFAULT '' CHECK (length(observed_health) <= 64),
restart_count integer NOT NULL DEFAULT 0 CHECK (restart_count >= 0),
intentional_stop boolean NOT NULL DEFAULT false,
first_seen_at timestamptz NOT NULL,
last_seen_at timestamptz NOT NULL,
tombstoned_at timestamptz,
PRIMARY KEY (source_id, runtime_id)
);
CREATE INDEX container_aliases_entity_idx ON container_aliases (entity_id, last_seen_at DESC);
CREATE INDEX container_aliases_active_idx ON container_aliases (source_id, last_seen_at DESC) WHERE tombstoned_at IS NULL;
-- The system status endpoint reports each background job's last outcome by
-- reading the newest job_runs row per job_type. job_runs_status_idx leads with
-- status, so it cannot serve that lookup; this index can.
CREATE INDEX job_runs_recent_idx ON job_runs (job_type, scheduled_at DESC, id ASC);
@@ -0,0 +1,16 @@
CREATE INDEX IF NOT EXISTS entities_type_canonical_idx
ON entities (entity_type, canonical_name, id)
WHERE tombstoned_at IS NULL;
CREATE INDEX IF NOT EXISTS entities_status_canonical_idx
ON entities (status, canonical_name, id)
WHERE tombstoned_at IS NULL;
CREATE INDEX IF NOT EXISTS entity_relations_source_entity_idx
ON entity_relations (source_entity_id, relation_type, target_entity_id);
CREATE INDEX IF NOT EXISTS entity_relations_target_entity_idx
ON entity_relations (target_entity_id, relation_type, source_entity_id);
CREATE INDEX IF NOT EXISTS entity_facts_freshness_idx
ON entity_facts (entity_id, valid_until, field_name, observed_at DESC);
@@ -0,0 +1,14 @@
CREATE TABLE capacity_samples (
entity_kind text NOT NULL CHECK (entity_kind IN ('share', 'pool', 'disk')),
entity_id text NOT NULL CHECK (length(entity_id) BETWEEN 1 AND 128),
entity_name text NOT NULL CHECK (length(entity_name) BETWEEN 1 AND 255),
source_id text NOT NULL CHECK (length(source_id) BETWEEN 1 AND 128),
sampled_at timestamptz NOT NULL,
observed_at timestamptz NOT NULL,
used_bytes bigint NOT NULL CHECK (used_bytes >= 0),
capacity_bytes bigint NOT NULL CHECK (capacity_bytes >= 0),
PRIMARY KEY (entity_kind, entity_id, source_id, sampled_at)
);
CREATE INDEX capacity_samples_history_idx
ON capacity_samples (entity_kind, entity_id, sampled_at DESC);
@@ -0,0 +1,2 @@
CREATE INDEX service_certificates_service_history_idx
ON service_certificates (service_id, observed_at DESC, id ASC);