CREATE TABLE collaboration_federation_identity ( singleton boolean PRIMARY KEY DEFAULT true CHECK(singleton), instance_id uuid NOT NULL UNIQUE, key_id uuid NOT NULL, public_key text NOT NULL, private_key_cipher jsonb NOT NULL, created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE collaboration_partners ( tenant_id bigint NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, origin text NOT NULL, instance_id uuid NOT NULL, key_id uuid NOT NULL, public_key text NOT NULL, active boolean NOT NULL DEFAULT true, updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY(tenant_id,origin) ); CREATE TABLE collaboration_federation_nonces ( origin text NOT NULL, nonce uuid NOT NULL, expires_at timestamptz NOT NULL, PRIMARY KEY(origin,nonce) ); CREATE TABLE collaboration_remote_bindings ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id bigint NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, principal_id uuid NOT NULL REFERENCES collaboration_principals(id) ON DELETE CASCADE, origin text NOT NULL, room_id uuid NOT NULL, invitation_id uuid NOT NULL, status text NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','active','revoked','declined')), invitation jsonb NOT NULL, snapshot jsonb, revision bigint NOT NULL DEFAULT 0, last_read_seq bigint NOT NULL DEFAULT 0, sync_seq bigint NOT NULL DEFAULT 0, sync_at timestamptz, last_error text, UNIQUE(principal_id,origin,room_id) ); CREATE TABLE collaboration_remote_messages ( binding_id uuid NOT NULL REFERENCES collaboration_remote_bindings(id) ON DELETE CASCADE, id uuid NOT NULL, seq bigint NOT NULL, payload jsonb NOT NULL, PRIMARY KEY(binding_id,id), UNIQUE(binding_id,seq) ); CREATE TABLE collaboration_outbox ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), dedup_key text NOT NULL UNIQUE, tenant_id bigint NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, principal_id uuid NOT NULL REFERENCES collaboration_principals(id), origin text NOT NULL, binding_id uuid REFERENCES collaboration_remote_bindings(id) ON DELETE CASCADE, room_id uuid REFERENCES collaboration_rooms(id) ON DELETE CASCADE, action text NOT NULL, payload jsonb NOT NULL, status text NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','sent','failed')), attempts integer NOT NULL DEFAULT 0, next_attempt_at timestamptz NOT NULL DEFAULT now(), lease_until timestamptz, last_error text, created_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX collaboration_outbox_pending ON collaboration_outbox(next_attempt_at) WHERE status='pending';