Initial project snapshot

This commit is contained in:
2026-04-28 22:29:50 +03:00
commit 8ba0561f4f
365 changed files with 91832 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
DROP TABLE IF EXISTS audit_logs;
DROP TABLE IF EXISTS secrets;
DROP TABLE IF EXISTS sessions;
DROP TABLE IF EXISTS resources;
DROP TABLE IF EXISTS devices;
DROP TABLE IF EXISTS users;
+65
View File
@@ -0,0 +1,65 @@
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
CREATE TABLE IF NOT EXISTS users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
mfa_enabled BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS devices (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
device_fingerprint TEXT NOT NULL,
trusted_at TIMESTAMPTZ,
last_seen_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS resources (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
address TEXT NOT NULL,
protocol TEXT NOT NULL,
secret_ref TEXT,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
resource_id UUID NOT NULL REFERENCES resources(id) ON DELETE RESTRICT,
controller_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
worker_id TEXT,
state TEXT NOT NULL,
detached_until TIMESTAMPTZ,
last_heartbeat_at TIMESTAMPTZ,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS secrets (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
scope TEXT NOT NULL,
encrypted_payload BYTEA NOT NULL,
key_version TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS audit_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
actor_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
action TEXT NOT NULL,
target_type TEXT NOT NULL,
target_id TEXT NOT NULL,
payload JSONB NOT NULL DEFAULT '{}'::JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_devices_user_id ON devices(user_id);
CREATE INDEX IF NOT EXISTS idx_sessions_resource_state ON sessions(resource_id, state);
CREATE INDEX IF NOT EXISTS idx_audit_logs_created_at ON audit_logs(created_at DESC);
@@ -0,0 +1,9 @@
DROP TABLE IF EXISTS auth_sessions;
DROP INDEX IF EXISTS idx_devices_user_fingerprint;
ALTER TABLE devices
DROP COLUMN IF EXISTS updated_at,
DROP COLUMN IF EXISTS revoked_reason,
DROP COLUMN IF EXISTS revoked_at,
DROP COLUMN IF EXISTS trust_status,
DROP COLUMN IF EXISTS device_label;
@@ -0,0 +1,32 @@
ALTER TABLE devices
ADD COLUMN IF NOT EXISTS device_label TEXT,
ADD COLUMN IF NOT EXISTS trust_status TEXT NOT NULL DEFAULT 'pending',
ADD COLUMN IF NOT EXISTS revoked_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS revoked_reason TEXT,
ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW();
CREATE UNIQUE INDEX IF NOT EXISTS idx_devices_user_fingerprint
ON devices(user_id, device_fingerprint);
CREATE TABLE IF NOT EXISTS auth_sessions (
id UUID PRIMARY KEY,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
device_id UUID NOT NULL REFERENCES devices(id) ON DELETE RESTRICT,
refresh_token_hash TEXT NOT NULL,
refresh_expires_at TIMESTAMPTZ NOT NULL,
last_seen_at TIMESTAMPTZ,
last_rotated_at TIMESTAMPTZ,
revoked_at TIMESTAMPTZ,
revoked_reason TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_auth_sessions_user_id
ON auth_sessions(user_id);
CREATE INDEX IF NOT EXISTS idx_auth_sessions_device_id
ON auth_sessions(device_id);
CREATE INDEX IF NOT EXISTS idx_auth_sessions_revoked_at
ON auth_sessions(revoked_at);
@@ -0,0 +1,4 @@
DROP TABLE IF EXISTS audit_events;
DROP TABLE IF EXISTS session_attachments;
DROP TABLE IF EXISTS remote_sessions;
DROP TABLE IF EXISTS resource_policies;
@@ -0,0 +1,79 @@
CREATE TABLE IF NOT EXISTS resource_policies (
resource_id UUID PRIMARY KEY REFERENCES resources(id) ON DELETE CASCADE,
max_concurrent_sessions INTEGER NOT NULL DEFAULT 1,
takeover_policy TEXT NOT NULL DEFAULT 'trusted_device',
require_trusted_device BOOLEAN NOT NULL DEFAULT TRUE,
detach_grace_period_seconds INTEGER NOT NULL DEFAULT 1800,
clipboard_enabled BOOLEAN NOT NULL DEFAULT TRUE,
file_transfer_enabled BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS remote_sessions (
id UUID PRIMARY KEY,
resource_id UUID NOT NULL REFERENCES resources(id) ON DELETE RESTRICT,
protocol TEXT NOT NULL,
state TEXT NOT NULL,
worker_id TEXT,
controller_user_id UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
detach_deadline_at TIMESTAMPTZ,
last_heartbeat_at TIMESTAMPTZ,
takeover_version INTEGER NOT NULL DEFAULT 1,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_remote_sessions_resource_id
ON remote_sessions(resource_id);
CREATE INDEX IF NOT EXISTS idx_remote_sessions_controller_user_id
ON remote_sessions(controller_user_id);
CREATE INDEX IF NOT EXISTS idx_remote_sessions_state
ON remote_sessions(state);
CREATE TABLE IF NOT EXISTS session_attachments (
id UUID PRIMARY KEY,
remote_session_id UUID NOT NULL REFERENCES remote_sessions(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
device_id UUID NOT NULL REFERENCES devices(id) ON DELETE RESTRICT,
role TEXT NOT NULL,
state TEXT NOT NULL,
superseded_by UUID REFERENCES session_attachments(id) ON DELETE SET NULL,
takeover_of UUID REFERENCES session_attachments(id) ON DELETE SET NULL,
attached_at TIMESTAMPTZ,
detached_at TIMESTAMPTZ,
last_input_at TIMESTAMPTZ,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_session_attachments_remote_session_id
ON session_attachments(remote_session_id);
CREATE INDEX IF NOT EXISTS idx_session_attachments_user_id
ON session_attachments(user_id);
CREATE INDEX IF NOT EXISTS idx_session_attachments_state
ON session_attachments(state);
CREATE TABLE IF NOT EXISTS audit_events (
id UUID PRIMARY KEY,
actor_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
actor_device_id UUID REFERENCES devices(id) ON DELETE SET NULL,
event_type TEXT NOT NULL,
target_type TEXT NOT NULL,
target_id TEXT NOT NULL,
remote_session_id UUID REFERENCES remote_sessions(id) ON DELETE SET NULL,
payload JSONB NOT NULL DEFAULT '{}'::JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_audit_events_created_at
ON audit_events(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_audit_events_remote_session_id
ON audit_events(remote_session_id);
@@ -0,0 +1,5 @@
ALTER TABLE resources
DROP CONSTRAINT IF EXISTS resources_certificate_verification_mode_check;
ALTER TABLE resources
DROP COLUMN IF EXISTS certificate_verification_mode;
@@ -0,0 +1,6 @@
ALTER TABLE resources
ADD COLUMN certificate_verification_mode TEXT NOT NULL DEFAULT 'strict';
ALTER TABLE resources
ADD CONSTRAINT resources_certificate_verification_mode_check
CHECK (certificate_verification_mode IN ('strict', 'ignore'));
@@ -0,0 +1,26 @@
DROP TABLE IF EXISTS node_agent_update_runs;
DROP TABLE IF EXISTS node_partition_states;
DROP TABLE IF EXISTS node_update_policies;
DROP TABLE IF EXISTS node_services;
DROP TABLE IF EXISTS node_capabilities;
DROP TABLE IF EXISTS nodes;
DROP TABLE IF EXISTS identity_mappings;
DROP TABLE IF EXISTS identity_sources;
DROP INDEX IF EXISTS idx_remote_sessions_organization_id;
ALTER TABLE remote_sessions
DROP COLUMN IF EXISTS organization_id;
DROP INDEX IF EXISTS idx_resources_organization_id;
ALTER TABLE resources
DROP COLUMN IF EXISTS organization_id;
DROP TABLE IF EXISTS organization_memberships;
DROP TABLE IF EXISTS organization_roles;
DROP TABLE IF EXISTS organizations;
ALTER TABLE users
DROP CONSTRAINT IF EXISTS users_platform_role_check;
ALTER TABLE users
DROP COLUMN IF EXISTS platform_role;
@@ -0,0 +1,219 @@
ALTER TABLE users
ADD COLUMN IF NOT EXISTS platform_role TEXT NOT NULL DEFAULT 'user';
ALTER TABLE users
ADD CONSTRAINT users_platform_role_check
CHECK (platform_role IN ('user', 'platform_admin', 'platform_recovery_admin'));
CREATE TABLE IF NOT EXISTS organizations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
slug TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT organizations_status_check
CHECK (status IN ('active', 'suspended', 'archived'))
);
CREATE TABLE IF NOT EXISTS organization_roles (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
scope TEXT NOT NULL DEFAULT 'organization',
description TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT organization_roles_scope_check
CHECK (scope IN ('organization'))
);
INSERT INTO organization_roles (id, name, scope, description)
VALUES
('org_owner', 'Organization Owner', 'organization', 'Full organization control including membership administration.'),
('org_admin', 'Organization Admin', 'organization', 'Administrative access within one organization.'),
('org_operator', 'Organization Operator', 'organization', 'Operational access within one organization.'),
('org_member', 'Organization Member', 'organization', 'Standard organization member access.'),
('org_viewer', 'Organization Viewer', 'organization', 'Read-only organization visibility.')
ON CONFLICT (id) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description;
CREATE TABLE IF NOT EXISTS organization_memberships (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role_id TEXT NOT NULL REFERENCES organization_roles(id) ON DELETE RESTRICT,
status TEXT NOT NULL DEFAULT 'active',
invited_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (organization_id, user_id),
CONSTRAINT organization_memberships_status_check
CHECK (status IN ('active', 'suspended', 'revoked'))
);
CREATE INDEX IF NOT EXISTS idx_organization_memberships_user_id
ON organization_memberships(user_id);
INSERT INTO organizations (slug, name, status, metadata)
VALUES ('default', 'Default Organization', 'active', '{"bootstrap":true}'::jsonb)
ON CONFLICT (slug) DO NOTHING;
ALTER TABLE resources
ADD COLUMN IF NOT EXISTS organization_id UUID REFERENCES organizations(id) ON DELETE RESTRICT;
UPDATE resources
SET organization_id = organizations.id
FROM organizations
WHERE organizations.slug = 'default'
AND resources.organization_id IS NULL;
ALTER TABLE resources
ALTER COLUMN organization_id SET NOT NULL;
CREATE INDEX IF NOT EXISTS idx_resources_organization_id
ON resources(organization_id);
ALTER TABLE remote_sessions
ADD COLUMN IF NOT EXISTS organization_id UUID REFERENCES organizations(id) ON DELETE RESTRICT;
UPDATE remote_sessions
SET organization_id = resources.organization_id
FROM resources
WHERE resources.id = remote_sessions.resource_id
AND remote_sessions.organization_id IS NULL;
ALTER TABLE remote_sessions
ALTER COLUMN organization_id SET NOT NULL;
CREATE INDEX IF NOT EXISTS idx_remote_sessions_organization_id
ON remote_sessions(organization_id);
CREATE TABLE IF NOT EXISTS identity_sources (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
kind TEXT NOT NULL,
name TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
config JSONB NOT NULL DEFAULT '{}'::JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT identity_sources_kind_check
CHECK (kind IN ('local', 'ldap_ad', 'oidc')),
CONSTRAINT identity_sources_status_check
CHECK (status IN ('active', 'disabled'))
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_identity_sources_org_name
ON identity_sources(organization_id, name);
CREATE TABLE IF NOT EXISTS identity_mappings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
identity_source_id UUID NOT NULL REFERENCES identity_sources(id) ON DELETE CASCADE,
mapping_type TEXT NOT NULL,
external_selector JSONB NOT NULL DEFAULT '{}'::JSONB,
internal_target JSONB NOT NULL DEFAULT '{}'::JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT identity_mappings_type_check
CHECK (mapping_type IN ('group_binding', 'claim_binding', 'user_binding'))
);
CREATE INDEX IF NOT EXISTS idx_identity_mappings_source_id
ON identity_mappings(identity_source_id);
CREATE TABLE IF NOT EXISTS nodes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
owner_organization_id UUID REFERENCES organizations(id) ON DELETE SET NULL,
node_key TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
ownership_type TEXT NOT NULL,
registration_status TEXT NOT NULL DEFAULT 'pending',
health_status TEXT NOT NULL DEFAULT 'unknown',
version_state TEXT NOT NULL DEFAULT 'unknown',
partition_state TEXT NOT NULL DEFAULT 'healthy',
desired_version TEXT,
reported_version TEXT,
last_seen_at TIMESTAMPTZ,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT nodes_ownership_type_check
CHECK (ownership_type IN ('platform_managed', 'customer_managed')),
CONSTRAINT nodes_registration_status_check
CHECK (registration_status IN ('pending', 'active', 'disabled', 'revoked')),
CONSTRAINT nodes_health_status_check
CHECK (health_status IN ('unknown', 'healthy', 'warning', 'critical')),
CONSTRAINT nodes_version_state_check
CHECK (version_state IN ('unknown', 'current', 'outdated', 'updating', 'rollback', 'failed')),
CONSTRAINT nodes_partition_state_check
CHECK (partition_state IN ('healthy', 'degraded', 'recovery', 'isolated'))
);
CREATE TABLE IF NOT EXISTS node_capabilities (
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
capability TEXT NOT NULL,
value JSONB NOT NULL DEFAULT '{}'::JSONB,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (node_id, capability)
);
CREATE TABLE IF NOT EXISTS node_services (
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
service_type TEXT NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT FALSE,
desired_state TEXT NOT NULL DEFAULT 'disabled',
reported_state TEXT NOT NULL DEFAULT 'unknown',
last_reported_at TIMESTAMPTZ,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (node_id, service_type),
CONSTRAINT node_services_desired_state_check
CHECK (desired_state IN ('enabled', 'disabled', 'drain')),
CONSTRAINT node_services_reported_state_check
CHECK (reported_state IN ('unknown', 'starting', 'running', 'degraded', 'stopped', 'failed'))
);
CREATE TABLE IF NOT EXISTS node_update_policies (
node_id UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
mode TEXT NOT NULL DEFAULT 'manual',
channel TEXT NOT NULL DEFAULT 'stable',
maintenance_window JSONB NOT NULL DEFAULT '{}'::JSONB,
canary BOOLEAN NOT NULL DEFAULT FALSE,
automatic_rollout BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT node_update_policies_mode_check
CHECK (mode IN ('manual', 'automatic', 'staged', 'canary'))
);
CREATE TABLE IF NOT EXISTS node_partition_states (
node_id UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
cluster_state TEXT NOT NULL DEFAULT 'healthy',
recovery_mode TEXT NOT NULL DEFAULT 'normal',
notes TEXT,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT node_partition_states_cluster_state_check
CHECK (cluster_state IN ('healthy', 'degraded', 'recovery', 'isolated')),
CONSTRAINT node_partition_states_recovery_mode_check
CHECK (recovery_mode IN ('normal', 'manual_recovery', 'emergency'))
);
CREATE TABLE IF NOT EXISTS node_agent_update_runs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
action TEXT NOT NULL,
target_version TEXT,
status TEXT NOT NULL DEFAULT 'pending',
requested_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
acknowledged_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
payload JSONB NOT NULL DEFAULT '{}'::JSONB,
CONSTRAINT node_agent_update_runs_action_check
CHECK (action IN ('update', 'rollback')),
CONSTRAINT node_agent_update_runs_status_check
CHECK (status IN ('pending', 'acknowledged', 'succeeded', 'failed'))
);
CREATE INDEX IF NOT EXISTS idx_node_agent_update_runs_node_id
ON node_agent_update_runs(node_id, requested_at DESC);
@@ -0,0 +1,7 @@
DELETE FROM organization_memberships
WHERE organization_id IN (
SELECT id
FROM organizations
WHERE slug = 'default'
)
AND invited_by_user_id IS NULL;
@@ -0,0 +1,29 @@
INSERT INTO organization_memberships (
id,
organization_id,
user_id,
role_id,
status,
invited_by_user_id,
created_at,
updated_at
)
SELECT
gen_random_uuid(),
org.id,
u.id,
CASE
WHEN u.platform_role IN ('platform_admin', 'platform_recovery_admin') THEN 'org_owner'
ELSE 'org_member'
END,
'active',
NULL,
NOW(),
NOW()
FROM users u
CROSS JOIN organizations org
LEFT JOIN organization_memberships om
ON om.organization_id = org.id
AND om.user_id = u.id
WHERE org.slug = 'default'
AND om.id IS NULL;
@@ -0,0 +1,5 @@
ALTER TABLE resource_policies
DROP CONSTRAINT IF EXISTS resource_policies_clipboard_mode_check;
ALTER TABLE resource_policies
DROP COLUMN IF EXISTS clipboard_mode;
@@ -0,0 +1,16 @@
ALTER TABLE resource_policies
ADD COLUMN IF NOT EXISTS clipboard_mode TEXT NOT NULL DEFAULT 'disabled';
UPDATE resource_policies
SET clipboard_mode = CASE
WHEN clipboard_enabled THEN 'bidirectional'
ELSE 'disabled'
END
WHERE clipboard_mode = 'disabled';
ALTER TABLE resource_policies
DROP CONSTRAINT IF EXISTS resource_policies_clipboard_mode_check;
ALTER TABLE resource_policies
ADD CONSTRAINT resource_policies_clipboard_mode_check
CHECK (clipboard_mode IN ('disabled', 'client_to_server', 'server_to_client', 'bidirectional'));
@@ -0,0 +1,5 @@
ALTER TABLE resource_policies
DROP CONSTRAINT IF EXISTS resource_policies_file_transfer_mode_check;
ALTER TABLE resource_policies
DROP COLUMN IF EXISTS file_transfer_mode;
@@ -0,0 +1,16 @@
ALTER TABLE resource_policies
ADD COLUMN IF NOT EXISTS file_transfer_mode TEXT NOT NULL DEFAULT 'disabled';
UPDATE resource_policies
SET file_transfer_mode = 'disabled',
file_transfer_enabled = FALSE
WHERE file_transfer_mode IS NULL
OR file_transfer_mode = ''
OR file_transfer_mode = 'disabled';
ALTER TABLE resource_policies
DROP CONSTRAINT IF EXISTS resource_policies_file_transfer_mode_check;
ALTER TABLE resource_policies
ADD CONSTRAINT resource_policies_file_transfer_mode_check
CHECK (file_transfer_mode IN ('disabled', 'client_to_server', 'server_to_client', 'bidirectional'));
@@ -0,0 +1 @@
DROP TABLE IF EXISTS resource_secrets;
@@ -0,0 +1,27 @@
CREATE TABLE IF NOT EXISTS resource_secrets (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
resource_id UUID NOT NULL REFERENCES resources(id) ON DELETE CASCADE,
secret_ref TEXT NOT NULL UNIQUE,
protocol TEXT NOT NULL,
version INTEGER NOT NULL DEFAULT 1,
key_id TEXT NOT NULL,
algorithm TEXT NOT NULL DEFAULT 'AES-256-GCM',
nonce BYTEA NOT NULL,
ciphertext BYTEA NOT NULL,
payload_sha256 TEXT NOT NULL,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
rotated_at TIMESTAMPTZ,
UNIQUE (resource_id)
);
CREATE INDEX IF NOT EXISTS idx_resource_secrets_organization_id
ON resource_secrets(organization_id);
CREATE INDEX IF NOT EXISTS idx_resource_secrets_resource_id
ON resource_secrets(resource_id);
CREATE INDEX IF NOT EXISTS idx_resource_secrets_secret_ref
ON resource_secrets(secret_ref);
@@ -0,0 +1,9 @@
DROP TABLE IF EXISTS cluster_audit_events;
DROP TABLE IF EXISTS node_latest_heartbeats;
DROP TABLE IF EXISTS node_heartbeats;
DROP TABLE IF EXISTS node_role_assignments;
DROP TABLE IF EXISTS node_join_requests;
DROP TABLE IF EXISTS node_join_tokens;
DROP TABLE IF EXISTS node_identities;
DROP TABLE IF EXISTS cluster_memberships;
DROP TABLE IF EXISTS clusters;
@@ -0,0 +1,186 @@
CREATE TABLE IF NOT EXISTS clusters (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
slug TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
region TEXT,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT clusters_status_check
CHECK (status IN ('active', 'disabled', 'archived', 'degraded'))
);
INSERT INTO clusters (slug, name, status, region, metadata)
VALUES ('default', 'Default Cluster', 'active', NULL, '{"bootstrap":true}'::jsonb)
ON CONFLICT (slug) DO NOTHING;
CREATE TABLE IF NOT EXISTS cluster_memberships (
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
membership_status TEXT NOT NULL DEFAULT 'active',
joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
last_seen_at TIMESTAMPTZ,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
PRIMARY KEY (cluster_id, node_id),
CONSTRAINT cluster_memberships_status_check
CHECK (membership_status IN ('active', 'draining', 'disabled', 'revoked'))
);
INSERT INTO cluster_memberships (cluster_id, node_id, membership_status, joined_at, last_seen_at, metadata)
SELECT c.id, n.id, 'active', COALESCE(n.created_at, NOW()), n.last_seen_at, '{"backfilled":true}'::jsonb
FROM clusters c
CROSS JOIN nodes n
WHERE c.slug = 'default'
ON CONFLICT (cluster_id, node_id) DO NOTHING;
CREATE TABLE IF NOT EXISTS node_identities (
node_id UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
public_key TEXT NOT NULL,
certificate_serial TEXT,
certificate_not_before TIMESTAMPTZ,
certificate_not_after TIMESTAMPTZ,
identity_status TEXT NOT NULL DEFAULT 'pending',
rotated_at TIMESTAMPTZ,
revoked_at TIMESTAMPTZ,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT node_identities_status_check
CHECK (identity_status IN ('pending', 'active', 'rotating', 'revoked'))
);
CREATE TABLE IF NOT EXISTS node_join_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
token_hash TEXT NOT NULL UNIQUE,
scope JSONB NOT NULL DEFAULT '{}'::JSONB,
expires_at TIMESTAMPTZ NOT NULL,
max_uses INTEGER NOT NULL DEFAULT 1,
used_count INTEGER NOT NULL DEFAULT 0,
status TEXT NOT NULL DEFAULT 'active',
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
revoked_at TIMESTAMPTZ,
CONSTRAINT node_join_tokens_status_check
CHECK (status IN ('active', 'revoked', 'expired')),
CONSTRAINT node_join_tokens_max_uses_check
CHECK (max_uses > 0),
CONSTRAINT node_join_tokens_used_count_check
CHECK (used_count >= 0)
);
CREATE INDEX IF NOT EXISTS idx_node_join_tokens_cluster_status
ON node_join_tokens(cluster_id, status, expires_at);
CREATE TABLE IF NOT EXISTS node_join_requests (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
join_token_id UUID REFERENCES node_join_tokens(id) ON DELETE SET NULL,
node_name TEXT NOT NULL,
node_fingerprint TEXT NOT NULL,
public_key TEXT NOT NULL,
reported_capabilities JSONB NOT NULL DEFAULT '{}'::JSONB,
reported_facts JSONB NOT NULL DEFAULT '{}'::JSONB,
requested_roles JSONB NOT NULL DEFAULT '[]'::JSONB,
status TEXT NOT NULL DEFAULT 'pending',
reviewed_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
reviewed_at TIMESTAMPTZ,
approved_node_id UUID REFERENCES nodes(id) ON DELETE SET NULL,
rejection_reason TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT node_join_requests_status_check
CHECK (status IN ('pending', 'approved', 'rejected', 'cancelled'))
);
CREATE INDEX IF NOT EXISTS idx_node_join_requests_cluster_status
ON node_join_requests(cluster_id, status, created_at DESC);
CREATE UNIQUE INDEX IF NOT EXISTS idx_node_join_requests_pending_fingerprint
ON node_join_requests(cluster_id, node_fingerprint)
WHERE status = 'pending';
CREATE TABLE IF NOT EXISTS node_role_assignments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
role TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
policy JSONB NOT NULL DEFAULT '{}'::JSONB,
assigned_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
revoked_at TIMESTAMPTZ,
CONSTRAINT node_role_assignments_status_check
CHECK (status IN ('active', 'disabled', 'revoked')),
CONSTRAINT node_role_assignments_role_check
CHECK (role IN (
'entry-node',
'relay-node',
'core-mesh',
'rdp-worker',
'vnc-worker',
'vpn-exit',
'vpn-connector',
'file-storage-cache',
'update-cache',
'video-relay'
))
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_node_role_assignments_unique_active
ON node_role_assignments(cluster_id, node_id, role, COALESCE(organization_id, '00000000-0000-0000-0000-000000000000'::uuid))
WHERE status = 'active';
CREATE INDEX IF NOT EXISTS idx_node_role_assignments_cluster
ON node_role_assignments(cluster_id, role, status);
CREATE TABLE IF NOT EXISTS node_heartbeats (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
health_status TEXT NOT NULL DEFAULT 'unknown',
reported_version TEXT,
capabilities JSONB NOT NULL DEFAULT '{}'::JSONB,
service_states JSONB NOT NULL DEFAULT '{}'::JSONB,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT node_heartbeats_health_status_check
CHECK (health_status IN ('unknown', 'healthy', 'warning', 'critical'))
);
CREATE INDEX IF NOT EXISTS idx_node_heartbeats_cluster_node_observed
ON node_heartbeats(cluster_id, node_id, observed_at DESC);
CREATE TABLE IF NOT EXISTS node_latest_heartbeats (
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
heartbeat_id UUID REFERENCES node_heartbeats(id) ON DELETE SET NULL,
health_status TEXT NOT NULL DEFAULT 'unknown',
reported_version TEXT,
capabilities JSONB NOT NULL DEFAULT '{}'::JSONB,
service_states JSONB NOT NULL DEFAULT '{}'::JSONB,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (cluster_id, node_id),
CONSTRAINT node_latest_heartbeats_health_status_check
CHECK (health_status IN ('unknown', 'healthy', 'warning', 'critical'))
);
CREATE TABLE IF NOT EXISTS cluster_audit_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cluster_id UUID REFERENCES clusters(id) ON DELETE SET NULL,
actor_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
event_type TEXT NOT NULL,
target_type TEXT NOT NULL,
target_id TEXT,
payload JSONB NOT NULL DEFAULT '{}'::JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_cluster_audit_events_cluster_created
ON cluster_audit_events(cluster_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_cluster_audit_events_type_created
ON cluster_audit_events(event_type, created_at DESC);
@@ -0,0 +1,3 @@
DROP TABLE IF EXISTS node_workload_latest_statuses;
DROP TABLE IF EXISTS node_workload_status_reports;
DROP TABLE IF EXISTS node_workload_desired_states;
@@ -0,0 +1,57 @@
CREATE TABLE IF NOT EXISTS node_workload_desired_states (
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
service_type TEXT NOT NULL,
desired_state TEXT NOT NULL DEFAULT 'disabled',
version TEXT,
runtime_mode TEXT NOT NULL DEFAULT 'container',
artifact_ref TEXT,
config JSONB NOT NULL DEFAULT '{}'::JSONB,
environment JSONB NOT NULL DEFAULT '{}'::JSONB,
updated_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (cluster_id, node_id, service_type),
CONSTRAINT node_workload_desired_states_desired_state_check
CHECK (desired_state IN ('enabled', 'disabled', 'drain')),
CONSTRAINT node_workload_desired_states_runtime_mode_check
CHECK (runtime_mode IN ('native', 'container'))
);
CREATE INDEX IF NOT EXISTS idx_node_workload_desired_states_cluster
ON node_workload_desired_states(cluster_id, service_type, desired_state);
CREATE TABLE IF NOT EXISTS node_workload_status_reports (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
service_type TEXT NOT NULL,
reported_state TEXT NOT NULL DEFAULT 'unknown',
runtime_mode TEXT NOT NULL DEFAULT 'container',
version TEXT,
status_payload JSONB NOT NULL DEFAULT '{}'::JSONB,
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT node_workload_status_reports_reported_state_check
CHECK (reported_state IN ('unknown', 'starting', 'running', 'degraded', 'stopped', 'failed', 'not_implemented')),
CONSTRAINT node_workload_status_reports_runtime_mode_check
CHECK (runtime_mode IN ('native', 'container'))
);
CREATE INDEX IF NOT EXISTS idx_node_workload_status_reports_node_observed
ON node_workload_status_reports(cluster_id, node_id, observed_at DESC);
CREATE TABLE IF NOT EXISTS node_workload_latest_statuses (
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
service_type TEXT NOT NULL,
status_report_id UUID REFERENCES node_workload_status_reports(id) ON DELETE SET NULL,
reported_state TEXT NOT NULL DEFAULT 'unknown',
runtime_mode TEXT NOT NULL DEFAULT 'container',
version TEXT,
status_payload JSONB NOT NULL DEFAULT '{}'::JSONB,
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (cluster_id, node_id, service_type),
CONSTRAINT node_workload_latest_statuses_reported_state_check
CHECK (reported_state IN ('unknown', 'starting', 'running', 'degraded', 'stopped', 'failed', 'not_implemented')),
CONSTRAINT node_workload_latest_statuses_runtime_mode_check
CHECK (runtime_mode IN ('native', 'container'))
);
@@ -0,0 +1,4 @@
DROP TABLE IF EXISTS mesh_qos_policies;
DROP TABLE IF EXISTS mesh_route_intents;
DROP TABLE IF EXISTS mesh_latest_links;
DROP TABLE IF EXISTS mesh_link_observations;
@@ -0,0 +1,94 @@
CREATE TABLE IF NOT EXISTS mesh_link_observations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
source_node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
target_node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
link_status TEXT NOT NULL DEFAULT 'unknown',
latency_ms INTEGER,
quality_score INTEGER,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT mesh_link_observations_status_check
CHECK (link_status IN ('unknown', 'reachable', 'degraded', 'unreachable')),
CONSTRAINT mesh_link_observations_quality_check
CHECK (quality_score IS NULL OR (quality_score >= 0 AND quality_score <= 100))
);
CREATE INDEX IF NOT EXISTS idx_mesh_link_observations_cluster_observed
ON mesh_link_observations(cluster_id, observed_at DESC);
CREATE TABLE IF NOT EXISTS mesh_latest_links (
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
source_node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
target_node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
observation_id UUID REFERENCES mesh_link_observations(id) ON DELETE SET NULL,
link_status TEXT NOT NULL DEFAULT 'unknown',
latency_ms INTEGER,
quality_score INTEGER,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (cluster_id, source_node_id, target_node_id),
CONSTRAINT mesh_latest_links_status_check
CHECK (link_status IN ('unknown', 'reachable', 'degraded', 'unreachable')),
CONSTRAINT mesh_latest_links_quality_check
CHECK (quality_score IS NULL OR (quality_score >= 0 AND quality_score <= 100))
);
CREATE TABLE IF NOT EXISTS mesh_route_intents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
source_selector JSONB NOT NULL DEFAULT '{}'::JSONB,
destination_selector JSONB NOT NULL DEFAULT '{}'::JSONB,
service_class TEXT NOT NULL,
priority INTEGER NOT NULL DEFAULT 100,
status TEXT NOT NULL DEFAULT 'active',
policy JSONB NOT NULL DEFAULT '{}'::JSONB,
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT mesh_route_intents_service_class_check
CHECK (service_class IN ('input', 'control', 'render', 'clipboard', 'file_transfer', 'vpn_packets', 'telemetry')),
CONSTRAINT mesh_route_intents_status_check
CHECK (status IN ('active', 'disabled'))
);
CREATE INDEX IF NOT EXISTS idx_mesh_route_intents_cluster_class
ON mesh_route_intents(cluster_id, service_class, status);
CREATE TABLE IF NOT EXISTS mesh_qos_policies (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
service_class TEXT NOT NULL,
priority INTEGER NOT NULL,
reliability_mode TEXT NOT NULL,
drop_policy TEXT NOT NULL,
bandwidth_policy JSONB NOT NULL DEFAULT '{}'::JSONB,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (cluster_id, service_class),
CONSTRAINT mesh_qos_policies_service_class_check
CHECK (service_class IN ('input', 'control', 'render', 'clipboard', 'file_transfer', 'vpn_packets', 'telemetry')),
CONSTRAINT mesh_qos_policies_reliability_check
CHECK (reliability_mode IN ('reliable', 'droppable', 'adaptive')),
CONSTRAINT mesh_qos_policies_drop_policy_check
CHECK (drop_policy IN ('never', 'latest_only', 'adaptive'))
);
INSERT INTO mesh_qos_policies (
cluster_id, service_class, priority, reliability_mode, drop_policy, bandwidth_policy, metadata
)
SELECT c.id, defaults.service_class, defaults.priority, defaults.reliability_mode,
defaults.drop_policy, '{}'::jsonb, '{"default":true}'::jsonb
FROM clusters c
CROSS JOIN (
VALUES
('input', 10, 'reliable', 'never'),
('control', 20, 'reliable', 'never'),
('clipboard', 40, 'reliable', 'never'),
('render', 60, 'droppable', 'latest_only'),
('file_transfer', 80, 'reliable', 'never'),
('telemetry', 120, 'adaptive', 'adaptive'),
('vpn_packets', 160, 'adaptive', 'adaptive')
) AS defaults(service_class, priority, reliability_mode, drop_policy)
ON CONFLICT (cluster_id, service_class) DO NOTHING;
@@ -0,0 +1,2 @@
DROP VIEW IF EXISTS cluster_admin_summaries;
DROP TABLE IF EXISTS cluster_authority_states;
@@ -0,0 +1,40 @@
CREATE TABLE IF NOT EXISTS cluster_authority_states (
cluster_id UUID PRIMARY KEY REFERENCES clusters(id) ON DELETE CASCADE,
authority_state TEXT NOT NULL DEFAULT 'authoritative',
mutation_mode TEXT NOT NULL DEFAULT 'normal',
term BIGINT NOT NULL DEFAULT 1,
notes TEXT,
updated_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT cluster_authority_states_authority_check
CHECK (authority_state IN ('authoritative', 'minority', 'isolated', 'recovery')),
CONSTRAINT cluster_authority_states_mutation_check
CHECK (mutation_mode IN ('normal', 'read_only', 'recovery_override'))
);
INSERT INTO cluster_authority_states (cluster_id, authority_state, mutation_mode, term, notes)
SELECT id, 'authoritative', 'normal', 1, 'default authority state'
FROM clusters
ON CONFLICT (cluster_id) DO NOTHING;
CREATE OR REPLACE VIEW cluster_admin_summaries AS
SELECT
c.id AS cluster_id,
c.slug,
c.name,
c.status,
c.region,
COALESCE(cas.authority_state, 'authoritative') AS authority_state,
COALESCE(cas.mutation_mode, 'normal') AS mutation_mode,
COUNT(DISTINCT cm.node_id) AS node_count,
COUNT(DISTINCT CASE WHEN n.health_status = 'healthy' THEN n.id END) AS healthy_node_count,
COUNT(DISTINCT CASE WHEN njr.status = 'pending' THEN njr.id END) AS pending_join_count,
COUNT(DISTINCT nra.id) AS active_role_assignment_count,
MAX(n.last_seen_at) AS last_node_seen_at
FROM clusters c
LEFT JOIN cluster_authority_states cas ON cas.cluster_id = c.id
LEFT JOIN cluster_memberships cm ON cm.cluster_id = c.id
LEFT JOIN nodes n ON n.id = cm.node_id
LEFT JOIN node_join_requests njr ON njr.cluster_id = c.id
LEFT JOIN node_role_assignments nra ON nra.cluster_id = c.id AND nra.status = 'active'
GROUP BY c.id, c.slug, c.name, c.status, c.region, cas.authority_state, cas.mutation_mode;
@@ -0,0 +1,4 @@
DROP TABLE IF EXISTS vpn_connection_leases;
DROP TABLE IF EXISTS vpn_connection_route_policies;
DROP TABLE IF EXISTS vpn_connection_allowed_nodes;
DROP TABLE IF EXISTS vpn_connections;
@@ -0,0 +1,125 @@
CREATE TABLE IF NOT EXISTS vpn_connections (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
name TEXT NOT NULL,
target_endpoint JSONB NOT NULL DEFAULT '{}'::JSONB,
protocol_family TEXT NOT NULL DEFAULT 'generic',
credential_ref TEXT,
mode TEXT NOT NULL DEFAULT 'single_active',
desired_state TEXT NOT NULL DEFAULT 'disabled',
allowed_node_policy JSONB NOT NULL DEFAULT '{"mode":"explicit","node_ids":[]}'::JSONB,
routing_usage JSONB NOT NULL DEFAULT '[]'::JSONB,
route_policy JSONB NOT NULL DEFAULT '{}'::JSONB,
qos_policy JSONB NOT NULL DEFAULT '{}'::JSONB,
placement_policy JSONB NOT NULL DEFAULT '{}'::JSONB,
status TEXT NOT NULL DEFAULT 'disabled',
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
updated_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT vpn_connections_mode_check
CHECK (mode IN ('single_active')),
CONSTRAINT vpn_connections_desired_state_check
CHECK (desired_state IN ('enabled', 'disabled')),
CONSTRAINT vpn_connections_status_check
CHECK (status IN ('disabled', 'enabled', 'connecting', 'active', 'degraded', 'failed')),
CONSTRAINT vpn_connections_protocol_family_check
CHECK (protocol_family IN ('generic', 'wireguard', 'ipsec', 'openvpn'))
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_vpn_connections_cluster_org_name
ON vpn_connections(cluster_id, organization_id, lower(name));
CREATE INDEX IF NOT EXISTS idx_vpn_connections_cluster_org_state
ON vpn_connections(cluster_id, organization_id, desired_state, status);
CREATE TABLE IF NOT EXISTS vpn_connection_allowed_nodes (
vpn_connection_id UUID NOT NULL REFERENCES vpn_connections(id) ON DELETE CASCADE,
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
role_preference TEXT NOT NULL DEFAULT 'candidate',
status TEXT NOT NULL DEFAULT 'active',
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (vpn_connection_id, node_id),
CONSTRAINT vpn_connection_allowed_nodes_membership_fk
FOREIGN KEY (cluster_id, node_id)
REFERENCES cluster_memberships(cluster_id, node_id)
ON DELETE CASCADE,
CONSTRAINT vpn_connection_allowed_nodes_preference_check
CHECK (role_preference IN ('candidate', 'standby', 'preferred')),
CONSTRAINT vpn_connection_allowed_nodes_status_check
CHECK (status IN ('active', 'disabled'))
);
CREATE INDEX IF NOT EXISTS idx_vpn_connection_allowed_nodes_cluster_node
ON vpn_connection_allowed_nodes(cluster_id, node_id, status);
CREATE TABLE IF NOT EXISTS vpn_connection_route_policies (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
vpn_connection_id UUID NOT NULL REFERENCES vpn_connections(id) ON DELETE CASCADE,
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
route_type TEXT NOT NULL,
destination TEXT NOT NULL,
action TEXT NOT NULL DEFAULT 'allow',
service_type TEXT,
priority INTEGER NOT NULL DEFAULT 100,
policy JSONB NOT NULL DEFAULT '{}'::JSONB,
status TEXT NOT NULL DEFAULT 'active',
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT vpn_connection_route_policies_route_type_check
CHECK (route_type IN ('cidr', 'dns_suffix', 'service', 'resource')),
CONSTRAINT vpn_connection_route_policies_action_check
CHECK (action IN ('allow', 'deny')),
CONSTRAINT vpn_connection_route_policies_status_check
CHECK (status IN ('active', 'disabled'))
);
CREATE INDEX IF NOT EXISTS idx_vpn_connection_route_policies_connection
ON vpn_connection_route_policies(vpn_connection_id, status, priority);
CREATE INDEX IF NOT EXISTS idx_vpn_connection_route_policies_cluster_org
ON vpn_connection_route_policies(cluster_id, organization_id, route_type, status);
CREATE TABLE IF NOT EXISTS vpn_connection_leases (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
vpn_connection_id UUID NOT NULL REFERENCES vpn_connections(id) ON DELETE CASCADE,
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
owner_node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
lease_generation BIGINT NOT NULL,
fencing_token TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
acquired_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
renewed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ NOT NULL,
released_at TIMESTAMPTZ,
fenced_at TIMESTAMPTZ,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
CONSTRAINT vpn_connection_leases_membership_fk
FOREIGN KEY (cluster_id, owner_node_id)
REFERENCES cluster_memberships(cluster_id, node_id)
ON DELETE CASCADE,
CONSTRAINT vpn_connection_leases_status_check
CHECK (status IN ('active', 'released', 'expired', 'fenced')),
CONSTRAINT vpn_connection_leases_expiry_check
CHECK (expires_at > acquired_at)
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_vpn_connection_leases_generation
ON vpn_connection_leases(vpn_connection_id, lease_generation);
CREATE UNIQUE INDEX IF NOT EXISTS idx_vpn_connection_leases_single_active
ON vpn_connection_leases(vpn_connection_id)
WHERE status = 'active';
CREATE INDEX IF NOT EXISTS idx_vpn_connection_leases_owner
ON vpn_connection_leases(cluster_id, owner_node_id, status, expires_at);
CREATE INDEX IF NOT EXISTS idx_vpn_connection_leases_connection_time
ON vpn_connection_leases(vpn_connection_id, acquired_at DESC);
@@ -0,0 +1,2 @@
DROP TABLE IF EXISTS vpn_connection_assignment_latest_statuses;
DROP TABLE IF EXISTS vpn_connection_assignment_status_reports;
@@ -0,0 +1,42 @@
CREATE TABLE IF NOT EXISTS vpn_connection_assignment_status_reports (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
vpn_connection_id UUID NOT NULL REFERENCES vpn_connections(id) ON DELETE CASCADE,
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
observed_status TEXT NOT NULL DEFAULT 'unknown',
status_payload JSONB NOT NULL DEFAULT '{}'::JSONB,
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT vpn_connection_assignment_status_membership_fk
FOREIGN KEY (cluster_id, node_id)
REFERENCES cluster_memberships(cluster_id, node_id)
ON DELETE CASCADE,
CONSTRAINT vpn_connection_assignment_status_check
CHECK (observed_status IN ('not_started', 'assigned', 'lease_required', 'blocked', 'unknown'))
);
CREATE INDEX IF NOT EXISTS idx_vpn_assignment_status_reports_node
ON vpn_connection_assignment_status_reports(cluster_id, node_id, observed_at DESC);
CREATE INDEX IF NOT EXISTS idx_vpn_assignment_status_reports_connection
ON vpn_connection_assignment_status_reports(vpn_connection_id, observed_at DESC);
CREATE TABLE IF NOT EXISTS vpn_connection_assignment_latest_statuses (
vpn_connection_id UUID NOT NULL REFERENCES vpn_connections(id) ON DELETE CASCADE,
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
report_id UUID NOT NULL REFERENCES vpn_connection_assignment_status_reports(id) ON DELETE CASCADE,
observed_status TEXT NOT NULL,
status_payload JSONB NOT NULL DEFAULT '{}'::JSONB,
observed_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (vpn_connection_id, node_id),
CONSTRAINT vpn_connection_assignment_latest_membership_fk
FOREIGN KEY (cluster_id, node_id)
REFERENCES cluster_memberships(cluster_id, node_id)
ON DELETE CASCADE,
CONSTRAINT vpn_connection_assignment_latest_status_check
CHECK (observed_status IN ('not_started', 'assigned', 'lease_required', 'blocked', 'unknown'))
);
CREATE INDEX IF NOT EXISTS idx_vpn_assignment_latest_node
ON vpn_connection_assignment_latest_statuses(cluster_id, node_id, updated_at DESC);
@@ -0,0 +1,5 @@
DROP INDEX IF EXISTS idx_node_telemetry_cluster_node_observed;
DROP TABLE IF EXISTS node_telemetry_observations;
DROP INDEX IF EXISTS idx_fabric_testing_flags_unique_scope;
DROP TABLE IF EXISTS fabric_testing_flags;
@@ -0,0 +1,43 @@
CREATE TABLE IF NOT EXISTS fabric_testing_flags (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
scope_type TEXT NOT NULL,
scope_id UUID,
cluster_id UUID REFERENCES clusters(id) ON DELETE CASCADE,
enabled BOOLEAN NOT NULL DEFAULT FALSE,
telemetry_enabled BOOLEAN NOT NULL DEFAULT FALSE,
synthetic_links_enabled BOOLEAN NOT NULL DEFAULT FALSE,
history_retention_hours INTEGER NOT NULL DEFAULT 24,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
updated_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT fabric_testing_flags_scope_check
CHECK (scope_type IN ('platform', 'organization', 'node')),
CONSTRAINT fabric_testing_flags_retention_check
CHECK (history_retention_hours BETWEEN 1 AND 720)
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_fabric_testing_flags_unique_scope
ON fabric_testing_flags (
scope_type,
COALESCE(scope_id, '00000000-0000-0000-0000-000000000000'::uuid),
COALESCE(cluster_id, '00000000-0000-0000-0000-000000000000'::uuid)
);
CREATE TABLE IF NOT EXISTS node_telemetry_observations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
cpu_percent DOUBLE PRECISION,
memory_used_bytes BIGINT,
memory_total_bytes BIGINT,
disk_used_bytes BIGINT,
disk_total_bytes BIGINT,
network_rx_bytes BIGINT,
network_tx_bytes BIGINT,
process_count INTEGER,
payload JSONB NOT NULL DEFAULT '{}'::JSONB,
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_node_telemetry_cluster_node_observed
ON node_telemetry_observations(cluster_id, node_id, observed_at DESC);
@@ -0,0 +1,2 @@
DROP TABLE IF EXISTS cluster_node_group_memberships;
DROP TABLE IF EXISTS cluster_node_groups;
@@ -0,0 +1,40 @@
CREATE TABLE IF NOT EXISTS cluster_node_groups (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
parent_group_id UUID REFERENCES cluster_node_groups(id) ON DELETE SET NULL,
name TEXT NOT NULL,
description TEXT,
sort_order INTEGER NOT NULL DEFAULT 0,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT cluster_node_groups_name_check CHECK (length(trim(name)) > 0),
CONSTRAINT cluster_node_groups_not_self_parent CHECK (parent_group_id IS NULL OR parent_group_id <> id),
UNIQUE (cluster_id, id)
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_cluster_node_groups_unique_sibling_name
ON cluster_node_groups(cluster_id, COALESCE(parent_group_id, '00000000-0000-0000-0000-000000000000'::uuid), lower(name));
CREATE INDEX IF NOT EXISTS idx_cluster_node_groups_parent
ON cluster_node_groups(cluster_id, parent_group_id, sort_order, name);
CREATE TABLE IF NOT EXISTS cluster_node_group_memberships (
cluster_id UUID NOT NULL,
node_id UUID NOT NULL,
group_id UUID NOT NULL,
assigned_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
PRIMARY KEY (cluster_id, node_id),
FOREIGN KEY (cluster_id, node_id)
REFERENCES cluster_memberships(cluster_id, node_id)
ON DELETE CASCADE,
FOREIGN KEY (cluster_id, group_id)
REFERENCES cluster_node_groups(cluster_id, id)
ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_cluster_node_group_memberships_group
ON cluster_node_group_memberships(cluster_id, group_id);
@@ -0,0 +1,4 @@
DROP TABLE IF EXISTS fabric_egress_pool_nodes;
DROP TABLE IF EXISTS fabric_egress_pools;
DROP TABLE IF EXISTS fabric_entry_point_nodes;
DROP TABLE IF EXISTS fabric_entry_points;
@@ -0,0 +1,83 @@
CREATE TABLE IF NOT EXISTS fabric_entry_points (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
name TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
endpoint_type TEXT NOT NULL DEFAULT 'client_access',
public_endpoint TEXT,
policy JSONB NOT NULL DEFAULT '{}'::JSONB,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT fabric_entry_points_status_check
CHECK (status IN ('active', 'disabled', 'maintenance')),
CONSTRAINT fabric_entry_points_type_check
CHECK (endpoint_type IN ('client_access', 'admin', 'api', 'other'))
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_fabric_entry_points_cluster_name
ON fabric_entry_points(cluster_id, LOWER(name));
CREATE INDEX IF NOT EXISTS idx_fabric_entry_points_cluster_status
ON fabric_entry_points(cluster_id, status, endpoint_type);
CREATE TABLE IF NOT EXISTS fabric_entry_point_nodes (
entry_point_id UUID NOT NULL REFERENCES fabric_entry_points(id) ON DELETE CASCADE,
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
status TEXT NOT NULL DEFAULT 'active',
priority INTEGER NOT NULL DEFAULT 100,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
added_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
added_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (entry_point_id, node_id),
CONSTRAINT fabric_entry_point_nodes_status_check
CHECK (status IN ('active', 'disabled', 'maintenance')),
CONSTRAINT fabric_entry_point_nodes_priority_check
CHECK (priority >= 0)
);
CREATE INDEX IF NOT EXISTS idx_fabric_entry_point_nodes_cluster_node
ON fabric_entry_point_nodes(cluster_id, node_id, status);
CREATE TABLE IF NOT EXISTS fabric_egress_pools (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
name TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
description TEXT,
route_scope JSONB NOT NULL DEFAULT '{}'::JSONB,
policy JSONB NOT NULL DEFAULT '{}'::JSONB,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT fabric_egress_pools_status_check
CHECK (status IN ('active', 'disabled', 'maintenance'))
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_fabric_egress_pools_cluster_name
ON fabric_egress_pools(cluster_id, LOWER(name));
CREATE INDEX IF NOT EXISTS idx_fabric_egress_pools_cluster_status
ON fabric_egress_pools(cluster_id, status);
CREATE TABLE IF NOT EXISTS fabric_egress_pool_nodes (
egress_pool_id UUID NOT NULL REFERENCES fabric_egress_pools(id) ON DELETE CASCADE,
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
status TEXT NOT NULL DEFAULT 'active',
priority INTEGER NOT NULL DEFAULT 100,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
added_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
added_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (egress_pool_id, node_id),
CONSTRAINT fabric_egress_pool_nodes_status_check
CHECK (status IN ('active', 'disabled', 'maintenance')),
CONSTRAINT fabric_egress_pool_nodes_priority_check
CHECK (priority >= 0)
);
CREATE INDEX IF NOT EXISTS idx_fabric_egress_pool_nodes_cluster_node
ON fabric_egress_pool_nodes(cluster_id, node_id, status);
@@ -0,0 +1,23 @@
WITH ranked AS (
SELECT ctid,
ROW_NUMBER() OVER (
PARTITION BY cluster_id, source_node_id, target_node_id
ORDER BY observed_at DESC
) AS rn
FROM mesh_latest_links
)
DELETE FROM mesh_latest_links
USING ranked
WHERE mesh_latest_links.ctid = ranked.ctid
AND ranked.rn > 1;
DROP INDEX IF EXISTS idx_mesh_latest_links_cluster_observed;
ALTER TABLE mesh_latest_links
DROP CONSTRAINT IF EXISTS mesh_latest_links_pkey;
ALTER TABLE mesh_latest_links
ADD PRIMARY KEY (cluster_id, source_node_id, target_node_id);
ALTER TABLE mesh_latest_links
DROP COLUMN IF EXISTS observation_key;
@@ -0,0 +1,11 @@
ALTER TABLE mesh_latest_links
ADD COLUMN IF NOT EXISTS observation_key TEXT NOT NULL DEFAULT 'default';
ALTER TABLE mesh_latest_links
DROP CONSTRAINT IF EXISTS mesh_latest_links_pkey;
ALTER TABLE mesh_latest_links
ADD PRIMARY KEY (cluster_id, source_node_id, target_node_id, observation_key);
CREATE INDEX IF NOT EXISTS idx_mesh_latest_links_cluster_observed
ON mesh_latest_links(cluster_id, observed_at DESC);
@@ -0,0 +1,4 @@
DROP INDEX IF EXISTS idx_platform_role_grants_unique_install_role;
DROP INDEX IF EXISTS idx_platform_role_grants_user_active;
DROP TABLE IF EXISTS platform_role_grants;
DROP TABLE IF EXISTS installation_authority;
@@ -0,0 +1,39 @@
CREATE TABLE IF NOT EXISTS installation_authority (
id SMALLINT PRIMARY KEY DEFAULT 1,
install_id TEXT NOT NULL,
authority_state TEXT NOT NULL DEFAULT 'active',
product_root_key_fingerprint TEXT NOT NULL DEFAULT '',
activation_payload JSONB NOT NULL,
activation_signature TEXT NOT NULL,
bootstrapped_owner_email TEXT NOT NULL,
bootstrapped_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT installation_authority_singleton_check CHECK (id = 1),
CONSTRAINT installation_authority_state_check CHECK (authority_state IN ('active', 'recovery_required', 'locked'))
);
CREATE TABLE IF NOT EXISTS platform_role_grants (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role TEXT NOT NULL,
install_id TEXT NOT NULL,
grant_payload JSONB NOT NULL,
grant_signature TEXT NOT NULL,
grant_source TEXT NOT NULL DEFAULT 'installation_activation',
granted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ,
revoked_at TIMESTAMPTZ,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
CONSTRAINT platform_role_grants_role_check
CHECK (role IN ('platform_admin', 'platform_recovery_admin')),
CONSTRAINT platform_role_grants_source_check
CHECK (grant_source IN ('installation_activation', 'recovery_manifest', 'dev_insecure'))
);
CREATE INDEX IF NOT EXISTS idx_platform_role_grants_user_active
ON platform_role_grants(user_id, role, revoked_at, expires_at);
CREATE UNIQUE INDEX IF NOT EXISTS idx_platform_role_grants_unique_install_role
ON platform_role_grants(user_id, role, install_id)
WHERE revoked_at IS NULL;
@@ -0,0 +1,33 @@
DROP VIEW IF EXISTS cluster_admin_summaries;
CREATE VIEW cluster_admin_summaries AS
SELECT
c.id AS cluster_id,
c.slug,
c.name,
c.status,
c.region,
COALESCE(cas.authority_state, 'authoritative') AS authority_state,
COALESCE(cas.mutation_mode, 'normal') AS mutation_mode,
COUNT(DISTINCT cm.node_id) AS node_count,
COUNT(DISTINCT CASE WHEN n.health_status = 'healthy' THEN n.id END) AS healthy_node_count,
COUNT(DISTINCT CASE WHEN njr.status = 'pending' THEN njr.id END) AS pending_join_count,
COUNT(DISTINCT nra.id) AS active_role_assignment_count,
MAX(n.last_seen_at) AS last_node_seen_at
FROM clusters c
LEFT JOIN cluster_authority_states cas ON cas.cluster_id = c.id
LEFT JOIN cluster_memberships cm ON cm.cluster_id = c.id
LEFT JOIN nodes n ON n.id = cm.node_id
LEFT JOIN node_join_requests njr ON njr.cluster_id = c.id
LEFT JOIN node_role_assignments nra ON nra.cluster_id = c.id AND nra.status = 'active'
GROUP BY c.id, c.slug, c.name, c.status, c.region, cas.authority_state, cas.mutation_mode;
ALTER TABLE node_join_requests
DROP COLUMN IF EXISTS approval_signature,
DROP COLUMN IF EXISTS approval_payload;
ALTER TABLE node_join_tokens
DROP COLUMN IF EXISTS authority_signature,
DROP COLUMN IF EXISTS authority_payload;
DROP TABLE IF EXISTS cluster_authorities;
@@ -0,0 +1,54 @@
CREATE TABLE IF NOT EXISTS cluster_authorities (
cluster_id UUID PRIMARY KEY REFERENCES clusters(id) ON DELETE CASCADE,
authority_state TEXT NOT NULL DEFAULT 'active',
key_algorithm TEXT NOT NULL DEFAULT 'ed25519',
public_key TEXT NOT NULL,
public_key_fingerprint TEXT NOT NULL,
private_key TEXT NOT NULL,
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
CONSTRAINT cluster_authorities_state_check
CHECK (authority_state IN ('active', 'rotating', 'revoked', 'recovery_required')),
CONSTRAINT cluster_authorities_algorithm_check
CHECK (key_algorithm = 'ed25519')
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_cluster_authorities_fingerprint
ON cluster_authorities(public_key_fingerprint);
ALTER TABLE node_join_tokens
ADD COLUMN IF NOT EXISTS authority_payload JSONB NOT NULL DEFAULT '{}'::JSONB,
ADD COLUMN IF NOT EXISTS authority_signature JSONB NOT NULL DEFAULT '{}'::JSONB;
ALTER TABLE node_join_requests
ADD COLUMN IF NOT EXISTS approval_payload JSONB NOT NULL DEFAULT '{}'::JSONB,
ADD COLUMN IF NOT EXISTS approval_signature JSONB NOT NULL DEFAULT '{}'::JSONB;
DROP VIEW IF EXISTS cluster_admin_summaries;
CREATE VIEW cluster_admin_summaries AS
SELECT
c.id AS cluster_id,
c.slug,
c.name,
c.status,
c.region,
COALESCE(cas.authority_state, 'authoritative') AS authority_state,
COALESCE(cas.mutation_mode, 'normal') AS mutation_mode,
ca.key_algorithm AS cluster_key_algorithm,
ca.public_key_fingerprint AS cluster_key_fingerprint,
COUNT(DISTINCT cm.node_id) AS node_count,
COUNT(DISTINCT CASE WHEN n.health_status = 'healthy' THEN n.id END) AS healthy_node_count,
COUNT(DISTINCT CASE WHEN njr.status = 'pending' THEN njr.id END) AS pending_join_count,
COUNT(DISTINCT nra.id) AS active_role_assignment_count,
MAX(n.last_seen_at) AS last_node_seen_at
FROM clusters c
LEFT JOIN cluster_authority_states cas ON cas.cluster_id = c.id
LEFT JOIN cluster_authorities ca ON ca.cluster_id = c.id
LEFT JOIN cluster_memberships cm ON cm.cluster_id = c.id
LEFT JOIN nodes n ON n.id = cm.node_id
LEFT JOIN node_join_requests njr ON njr.cluster_id = c.id
LEFT JOIN node_role_assignments nra ON nra.cluster_id = c.id AND nra.status = 'active'
GROUP BY c.id, c.slug, c.name, c.status, c.region, cas.authority_state, cas.mutation_mode, ca.key_algorithm, ca.public_key_fingerprint;
@@ -0,0 +1,21 @@
UPDATE mesh_route_intents
SET service_class = 'control',
updated_at = NOW()
WHERE service_class = 'synthetic';
DELETE FROM mesh_qos_policies
WHERE service_class = 'synthetic';
ALTER TABLE mesh_route_intents
DROP CONSTRAINT IF EXISTS mesh_route_intents_service_class_check;
ALTER TABLE mesh_route_intents
ADD CONSTRAINT mesh_route_intents_service_class_check
CHECK (service_class IN ('input', 'control', 'render', 'clipboard', 'file_transfer', 'vpn_packets', 'telemetry'));
ALTER TABLE mesh_qos_policies
DROP CONSTRAINT IF EXISTS mesh_qos_policies_service_class_check;
ALTER TABLE mesh_qos_policies
ADD CONSTRAINT mesh_qos_policies_service_class_check
CHECK (service_class IN ('input', 'control', 'render', 'clipboard', 'file_transfer', 'vpn_packets', 'telemetry'));
@@ -0,0 +1,21 @@
ALTER TABLE mesh_route_intents
DROP CONSTRAINT IF EXISTS mesh_route_intents_service_class_check;
ALTER TABLE mesh_route_intents
ADD CONSTRAINT mesh_route_intents_service_class_check
CHECK (service_class IN ('input', 'control', 'synthetic', 'render', 'clipboard', 'file_transfer', 'vpn_packets', 'telemetry'));
ALTER TABLE mesh_qos_policies
DROP CONSTRAINT IF EXISTS mesh_qos_policies_service_class_check;
ALTER TABLE mesh_qos_policies
ADD CONSTRAINT mesh_qos_policies_service_class_check
CHECK (service_class IN ('input', 'control', 'synthetic', 'render', 'clipboard', 'file_transfer', 'vpn_packets', 'telemetry'));
INSERT INTO mesh_qos_policies (
cluster_id, service_class, priority, reliability_mode, drop_policy, bandwidth_policy, metadata
)
SELECT c.id, 'synthetic', 30, 'reliable', 'never', '{}'::jsonb,
'{"default":true,"control_plane_only":true}'::jsonb
FROM clusters c
ON CONFLICT (cluster_id, service_class) DO NOTHING;