DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'dodo') THEN CREATE SCHEMA dodo; END IF; END $EF$; CREATE TABLE IF NOT EXISTS dodo."__EFMigrationsHistory" ( migration_id character varying(150) NOT NULL, product_version character varying(32) NOT NULL, CONSTRAINT pk___ef_migrations_history PRIMARY KEY (migration_id) ); START TRANSACTION; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'dodo') THEN CREATE SCHEMA dodo; END IF; END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'dodo') THEN CREATE SCHEMA dodo; END IF; END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE EXTENSION IF NOT EXISTS citext; END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE TABLE dodo.key_log ( sequence bigint GENERATED ALWAYS AS IDENTITY, user_id uuid NOT NULL, generation integer NOT NULL, encryption_public_key bytea NOT NULL, signing_public_key bytea NOT NULL, statement_signature bytea NOT NULL, previous_hash bytea NOT NULL, hash bytea NOT NULL, created_at_utc timestamp with time zone NOT NULL, CONSTRAINT pk_key_log PRIMARY KEY (sequence) ); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE TABLE dodo.sync_change ( sequence bigint GENERATED ALWAYS AS IDENTITY, vault_id uuid NOT NULL, entity_type integer NOT NULL, entity_id uuid NOT NULL, operation integer NOT NULL, revision integer NOT NULL, actor_user_id uuid NOT NULL, occurred_at_utc timestamp with time zone NOT NULL, CONSTRAINT pk_sync_change PRIMARY KEY (sequence) ); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE TABLE dodo.sync_operation_receipt ( operation_id uuid NOT NULL, vault_id uuid NOT NULL, applied_change_sequence bigint, result_version integer, created_at_utc timestamp with time zone NOT NULL, CONSTRAINT pk_sync_operation_receipt PRIMARY KEY (operation_id) ); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE TABLE dodo.team ( id uuid NOT NULL, name character varying(256) NOT NULL, slug citext NOT NULL, description character varying(2048), created_by_user_id uuid NOT NULL, created_at_utc timestamp with time zone NOT NULL, deleted_at_utc timestamp with time zone, CONSTRAINT pk_team PRIMARY KEY (id) ); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE TABLE dodo.user_account ( id uuid NOT NULL, issuer character varying(512) NOT NULL, subject character varying(256) NOT NULL, email citext, display_name character varying(256), status integer NOT NULL, enrolled_at_utc timestamp with time zone, created_at_utc timestamp with time zone NOT NULL, updated_at_utc timestamp with time zone NOT NULL, last_seen_at_utc timestamp with time zone, deleted_at_utc timestamp with time zone, CONSTRAINT pk_user_account PRIMARY KEY (id) ); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE TABLE dodo.device ( id uuid NOT NULL, user_id uuid NOT NULL, name character varying(256) NOT NULL, platform integer NOT NULL, public_key bytea NOT NULL, enrolled_at_utc timestamp with time zone NOT NULL, last_seen_at_utc timestamp with time zone, revoked_at_utc timestamp with time zone, CONSTRAINT pk_device PRIMARY KEY (id), CONSTRAINT fk_device_users_user_id FOREIGN KEY (user_id) REFERENCES dodo.user_account (id) ON DELETE CASCADE ); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE TABLE dodo.team_membership ( id uuid NOT NULL, team_id uuid NOT NULL, user_id uuid NOT NULL, role integer NOT NULL, status integer NOT NULL, invited_by_user_id uuid, joined_at_utc timestamp with time zone, created_at_utc timestamp with time zone NOT NULL, deleted_at_utc timestamp with time zone, CONSTRAINT pk_team_membership PRIMARY KEY (id), CONSTRAINT fk_team_membership_team_team_id FOREIGN KEY (team_id) REFERENCES dodo.team (id) ON DELETE CASCADE, CONSTRAINT fk_team_membership_users_user_id FOREIGN KEY (user_id) REFERENCES dodo.user_account (id) ON DELETE CASCADE ); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE TABLE dodo.user_key ( id uuid NOT NULL, user_id uuid NOT NULL, generation integer NOT NULL, encryption_public_key bytea NOT NULL, signing_public_key bytea NOT NULL, fingerprint_sha256 bytea NOT NULL, statement jsonb NOT NULL, statement_signature bytea NOT NULL, identity_provider_binding jsonb, is_current boolean NOT NULL, created_at_utc timestamp with time zone NOT NULL, revoked_at_utc timestamp with time zone, CONSTRAINT pk_user_key PRIMARY KEY (id), CONSTRAINT fk_user_key_user_account_user_id FOREIGN KEY (user_id) REFERENCES dodo.user_account (id) ON DELETE CASCADE ); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE TABLE dodo.vault ( id uuid NOT NULL, name character varying(256) NOT NULL, owner_kind integer NOT NULL, owner_user_id uuid, team_id uuid, key_generation integer NOT NULL, rekey_required boolean NOT NULL, rekey_reason integer NOT NULL, created_at_utc timestamp with time zone NOT NULL, updated_at_utc timestamp with time zone NOT NULL, deleted_at_utc timestamp with time zone, CONSTRAINT pk_vault PRIMARY KEY (id), CONSTRAINT ck_vault_key_generation CHECK (key_generation >= 1), CONSTRAINT ck_vault_owner CHECK ((owner_kind = 1 AND owner_user_id IS NOT NULL AND team_id IS NULL) OR (owner_kind = 2 AND team_id IS NOT NULL AND owner_user_id IS NULL)), CONSTRAINT fk_vault_team_team_id FOREIGN KEY (team_id) REFERENCES dodo.team (id) ON DELETE RESTRICT, CONSTRAINT fk_vault_user_account_owner_user_id FOREIGN KEY (owner_user_id) REFERENCES dodo.user_account (id) ON DELETE RESTRICT ); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE TABLE dodo.user_key_wrap ( id uuid NOT NULL, user_id uuid NOT NULL, kind integer NOT NULL, device_id uuid, wrap bytea NOT NULL, wrap_version integer NOT NULL, kdf_algorithm character varying(64), kdf_salt bytea, kdf_memory_kibibytes integer, kdf_passes integer, kdf_parallelism integer, created_at_utc timestamp with time zone NOT NULL, last_used_at_utc timestamp with time zone, CONSTRAINT pk_user_key_wrap PRIMARY KEY (id), CONSTRAINT ck_user_key_wrap_device CHECK ((kind = 2 AND device_id IS NOT NULL) OR (kind <> 2 AND device_id IS NULL)), CONSTRAINT ck_user_key_wrap_kdf CHECK ((kind IN (1, 3) AND kdf_algorithm IS NOT NULL AND kdf_salt IS NOT NULL AND kdf_memory_kibibytes IS NOT NULL AND kdf_passes IS NOT NULL AND kdf_parallelism IS NOT NULL) OR (kind IN (2, 4) AND kdf_algorithm IS NULL AND kdf_salt IS NULL)), CONSTRAINT fk_user_key_wrap_device_device_id FOREIGN KEY (device_id) REFERENCES dodo.device (id) ON DELETE CASCADE, CONSTRAINT fk_user_key_wrap_user_account_user_id FOREIGN KEY (user_id) REFERENCES dodo.user_account (id) ON DELETE CASCADE ); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE TABLE dodo.host ( id uuid NOT NULL, vault_id uuid NOT NULL, payload bytea NOT NULL, data_key_wrap bytea, content_key_id uuid, key_generation integer NOT NULL, payload_aad_version smallint NOT NULL, relay_enabled boolean NOT NULL, hostname character varying(255), port integer, group_id uuid, version integer NOT NULL, change_sequence bigint NOT NULL, created_at_utc timestamp with time zone NOT NULL, updated_at_utc timestamp with time zone NOT NULL, deleted_at_utc timestamp with time zone, created_by_user_id uuid NOT NULL, updated_by_user_id uuid NOT NULL, CONSTRAINT pk_host PRIMARY KEY (id), CONSTRAINT ck_host_port_range CHECK (port IS NULL OR (port BETWEEN 1 AND 65535)), CONSTRAINT ck_host_relay_target CHECK ((relay_enabled AND hostname IS NOT NULL AND port IS NOT NULL) OR (NOT relay_enabled AND hostname IS NULL AND port IS NULL)), CONSTRAINT ck_host_version CHECK (version >= 1), CONSTRAINT fk_host_vaults_vault_id FOREIGN KEY (vault_id) REFERENCES dodo.vault (id) ON DELETE CASCADE ); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE TABLE dodo.vault_key_grant ( id uuid NOT NULL, vault_id uuid NOT NULL, key_generation integer NOT NULL, kind integer NOT NULL, recipient_user_id uuid, recipient_key_fingerprint bytea NOT NULL, wrapped_key bytea NOT NULL, granter_user_id uuid NOT NULL, granter_key_fingerprint bytea NOT NULL, key_log_head bytea, signature bytea NOT NULL, state integer NOT NULL, created_at_utc timestamp with time zone NOT NULL, revoked_at_utc timestamp with time zone, CONSTRAINT pk_vault_key_grant PRIMARY KEY (id), CONSTRAINT ck_vault_key_grant_recipient CHECK ((kind = 1 AND recipient_user_id IS NOT NULL) OR (kind <> 1 AND recipient_user_id IS NULL)), CONSTRAINT fk_vault_key_grant_user_account_recipient_user_id FOREIGN KEY (recipient_user_id) REFERENCES dodo.user_account (id) ON DELETE CASCADE, CONSTRAINT fk_vault_key_grant_vault_vault_id FOREIGN KEY (vault_id) REFERENCES dodo.vault (id) ON DELETE CASCADE ); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE INDEX ix_device_user_id ON dodo.device (user_id); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE INDEX ix_host_vault_id_change_sequence ON dodo.host (vault_id, change_sequence); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE INDEX ix_host_vault_live ON dodo.host (vault_id) WHERE deleted_at_utc IS NULL; END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE UNIQUE INDEX ix_key_log_hash ON dodo.key_log (hash); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE INDEX ix_key_log_user_id ON dodo.key_log (user_id); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE INDEX ix_sync_change_vault_id_entity_id_sequence ON dodo.sync_change (vault_id, entity_id, sequence DESC); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE INDEX ix_sync_change_vault_id_sequence ON dodo.sync_change (vault_id, sequence); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE INDEX ix_sync_operation_receipt_vault_id_created_at_utc ON dodo.sync_operation_receipt (vault_id, created_at_utc); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE UNIQUE INDEX ix_team_slug ON dodo.team (slug) WHERE deleted_at_utc IS NULL; END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE UNIQUE INDEX ix_team_membership_team_id_user_id ON dodo.team_membership (team_id, user_id) WHERE deleted_at_utc IS NULL; END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE INDEX ix_team_membership_user_id ON dodo.team_membership (user_id); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE UNIQUE INDEX ix_user_account_email ON dodo.user_account (email) WHERE email IS NOT NULL AND deleted_at_utc IS NULL; END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE UNIQUE INDEX ix_user_account_issuer_subject ON dodo.user_account (issuer, subject); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE UNIQUE INDEX ix_user_key_current ON dodo.user_key (user_id) WHERE is_current; END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE UNIQUE INDEX ix_user_key_fingerprint_sha256 ON dodo.user_key (fingerprint_sha256); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE UNIQUE INDEX ix_user_key_user_id_generation ON dodo.user_key (user_id, generation); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE INDEX ix_user_key_wrap_device_id ON dodo.user_key_wrap (device_id); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE UNIQUE INDEX ix_user_key_wrap_user_device ON dodo.user_key_wrap (user_id, device_id) WHERE device_id IS NOT NULL; END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE UNIQUE INDEX ix_user_key_wrap_user_kind ON dodo.user_key_wrap (user_id, kind) WHERE device_id IS NULL; END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE INDEX ix_vault_owner_user_id ON dodo.vault (owner_user_id); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE INDEX ix_vault_team_id ON dodo.vault (team_id); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE INDEX ix_vault_key_grant_recipient_user_id ON dodo.vault_key_grant (recipient_user_id); END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN CREATE UNIQUE INDEX ix_vault_key_grant_vault_id_key_generation_recipient_user_id ON dodo.vault_key_grant (vault_id, key_generation, recipient_user_id) WHERE revoked_at_utc IS NULL AND recipient_user_id IS NOT NULL; END IF; END $EF$; DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM dodo."__EFMigrationsHistory" WHERE "migration_id" = '20260728125751_InitialSchema') THEN INSERT INTO dodo."__EFMigrationsHistory" (migration_id, product_version) VALUES ('20260728125751_InitialSchema', '10.0.10'); END IF; END $EF$; COMMIT;