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) );