mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
## Summary Adds the relay and database admission boundary for owner-requested deletion of archived hosted communities. The operator endpoint records accepted owner intent with stable UUID idempotency and immutable owner/operator provenance. Admission serializes with unarchive and owner transfer, and accepted requests disappear from actionable owner listings. This keeps destructive execution out of the request path. The existing durable deletion engine remains the only executor, so this change adds no lifecycle state or second deletion mechanism. Complexity stays bounded: one durable request is both the idempotency record and the mutation fence. ### Related issue None found. ### Testing - `REDIS_URL=redis://127.0.0.1:6379 BUZZ_POSTGRES_ADMIN_URL=postgres://buzz:buzz_dev@localhost:5432/postgres PGHOST=localhost PGPORT=5432 PGUSER=buzz PGPASSWORD=buzz_dev PG_BIN_DIR=/tmp/buzz-pg-bin ./scripts/postgres-test-run.sh -p buzz-db -p buzz-relay --lib --tests` — 385 passed. - `cargo test -p buzz-db` — passed. - `cargo fmt --all -- --check` and `cargo clippy -p buzz-db -p buzz-relay --all-targets -- -D warnings` — passed. - `just desktop-test desktop-build desktop-tauri-check desktop-tauri-test web-build` — passed; Desktop ran 6,589 tests. - Full `just test` could not start uncached Keycloak dependencies because `quay.io` reset connections. Full `just ci` reached Flutter dependency resolution, then `pub.dev` reset connections. Generated with Codex --------- Signed-off-by: tornquist <tornquist@squareup.com> Signed-off-by: Codex <noreply@openai.com> Signed-off-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz> Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz>
82 lines
3.8 KiB
PL/PgSQL
82 lines
3.8 KiB
PL/PgSQL
-- Structured provenance for owner-origin whole-community deletion requests.
|
|
--
|
|
-- The existing request UUID is the stable correlation/idempotency identity.
|
|
-- Owner admission supplies that UUID instead of creating a second identity
|
|
-- column, while these bounded columns distinguish owner intent from the
|
|
-- deployment operator that mediated it.
|
|
--
|
|
-- The owner branch rejects a NULL in every provenance column before testing
|
|
-- its shape. A bare `col ~ '...'` on a NULL column yields NULL, and a CHECK is
|
|
-- satisfied by NULL, so `FALSE OR NULL` would silently admit an owner-origin
|
|
-- row with no owner key, no mediating operator, or no acknowledgement version.
|
|
--
|
|
-- The null rejection is spelled `NOT (col IS NULL)` rather than
|
|
-- `col IS NOT NULL`: pgschema drops a named CHECK whose body contains
|
|
-- `IS NOT NULL` and still exits 0, which would leave the desired-state
|
|
-- bootstrap in `schema/schema.sql` silently unguarded while the migration path
|
|
-- stayed correct. `store::deletion::owner_provenance_contract` asserts both
|
|
-- schema sources against one case table so that divergence fails loudly.
|
|
SET LOCAL lock_timeout = '5s';
|
|
|
|
ALTER TABLE community_deletion_requests
|
|
ADD COLUMN request_origin TEXT NOT NULL DEFAULT 'operator'
|
|
CHECK (request_origin IN ('operator', 'owner')),
|
|
ADD COLUMN owner_pubkey TEXT,
|
|
ADD COLUMN mediating_operator_pubkey TEXT,
|
|
ADD COLUMN acknowledgement_version INTEGER,
|
|
ADD CONSTRAINT community_deletion_owner_provenance CHECK (
|
|
(request_origin = 'operator'
|
|
AND owner_pubkey IS NULL
|
|
AND mediating_operator_pubkey IS NULL
|
|
AND acknowledgement_version IS NULL)
|
|
OR
|
|
(request_origin = 'owner'
|
|
AND NOT (owner_pubkey IS NULL)
|
|
AND NOT (mediating_operator_pubkey IS NULL)
|
|
AND NOT (acknowledgement_version IS NULL)
|
|
AND owner_pubkey ~ '^[0-9a-f]{64}$'
|
|
AND mediating_operator_pubkey ~ '^[0-9a-f]{64}$'
|
|
AND acknowledgement_version BETWEEN 1 AND 32767
|
|
AND requested_by = owner_pubkey)
|
|
);
|
|
|
|
CREATE OR REPLACE FUNCTION prevent_community_deletion_request_retargeting()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
AS $$
|
|
BEGIN
|
|
IF NEW.community_id IS DISTINCT FROM OLD.community_id
|
|
OR NEW.community_host IS DISTINCT FROM OLD.community_host
|
|
THEN
|
|
RAISE EXCEPTION 'community deletion target identity is immutable'
|
|
USING ERRCODE = 'integrity_constraint_violation';
|
|
END IF;
|
|
IF NEW.request_origin IS DISTINCT FROM OLD.request_origin
|
|
OR NEW.owner_pubkey IS DISTINCT FROM OLD.owner_pubkey
|
|
OR NEW.mediating_operator_pubkey IS DISTINCT FROM OLD.mediating_operator_pubkey
|
|
OR NEW.acknowledgement_version IS DISTINCT FROM OLD.acknowledgement_version
|
|
THEN
|
|
RAISE EXCEPTION 'community deletion request provenance is immutable'
|
|
USING ERRCODE = 'integrity_constraint_violation';
|
|
END IF;
|
|
IF OLD.inventory_frozen_at IS NOT NULL AND (
|
|
NEW.schema_manifest IS DISTINCT FROM OLD.schema_manifest
|
|
OR NEW.storage_manifest IS DISTINCT FROM OLD.storage_manifest
|
|
OR NEW.inventory_manifest IS DISTINCT FROM OLD.inventory_manifest
|
|
OR NEW.inventory_digest IS DISTINCT FROM OLD.inventory_digest
|
|
OR NEW.inventory_frozen_at IS DISTINCT FROM OLD.inventory_frozen_at
|
|
) THEN
|
|
RAISE EXCEPTION 'frozen community deletion inventory is immutable'
|
|
USING ERRCODE = 'integrity_constraint_violation';
|
|
END IF;
|
|
IF OLD.destructive_storage_frozen_at IS NOT NULL AND (
|
|
NEW.destructive_storage_manifest IS DISTINCT FROM OLD.destructive_storage_manifest
|
|
OR NEW.destructive_storage_frozen_at IS DISTINCT FROM OLD.destructive_storage_frozen_at
|
|
) THEN
|
|
RAISE EXCEPTION 'frozen destructive storage manifest is immutable'
|
|
USING ERRCODE = 'integrity_constraint_violation';
|
|
END IF;
|
|
RETURN NEW;
|
|
END;
|
|
$$;
|