Files
buzz/migrations/0028_private_managed_agent_foundation.sql
Wes 3451593743 Add relay authority for private managed agents
Introduce an authenticated aggregate endpoint and serializable authority
transaction for encrypted private managed-agent heads and their exact public
projections. Preserve immutable signed recovery bytes, enforce generation and
definition revision CAS, reactivate submitted projections, retire prior heads,
and return a writer-consistent signed aggregate snapshot.

Keep kind 30179 author-only and out of full-text search, reject generic writes,
and advertise the aggregate capability through NIP-11.

Co-authored-by: Princess Donut <68157ebd23b3897c1991015c3038658ea916200c67d3a54620b0754d1b92f6e0@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:53 -06:00

94 lines
4.5 KiB
SQL

-- Relay-canonical authority for NIP-PMA kind:30179. The head retains the
-- generation floor after deletion; immutable revisions provide the audit/CAS
-- chain. Public projections are bound by exact signed event IDs and hashes.
CREATE TABLE managed_agent_definition_heads (
community_id UUID NOT NULL REFERENCES communities(id) ON DELETE CASCADE,
owner_pubkey BYTEA NOT NULL CHECK (length(owner_pubkey) = 32),
definition_d TEXT NOT NULL,
revision BIGINT NOT NULL CHECK (revision > 0),
event_id BYTEA NOT NULL CHECK (length(event_id) = 32),
content_sha256 BYTEA NOT NULL CHECK (length(content_sha256) = 32),
PRIMARY KEY (community_id, owner_pubkey, definition_d)
);
CREATE TABLE managed_agent_heads (
community_id UUID NOT NULL REFERENCES communities(id) ON DELETE CASCADE,
owner_pubkey BYTEA NOT NULL CHECK (length(owner_pubkey) = 32),
agent_pubkey BYTEA NOT NULL CHECK (length(agent_pubkey) = 32),
generation BIGINT NOT NULL CHECK (generation > 0 AND generation <= 9007199254740991),
event_id BYTEA NOT NULL CHECK (length(event_id) = 32),
state TEXT NOT NULL CHECK (state IN ('active', 'deleted')),
definition_d TEXT,
definition_revision BIGINT,
definition_event_id BYTEA,
definition_content_sha256 BYTEA,
instance_event_id BYTEA,
instance_content_sha256 BYTEA,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (community_id, owner_pubkey, agent_pubkey),
UNIQUE (community_id, event_id),
CHECK ((state = 'active') =
(definition_d IS NOT NULL AND definition_revision IS NOT NULL AND
definition_event_id IS NOT NULL AND definition_content_sha256 IS NOT NULL AND
instance_event_id IS NOT NULL AND instance_content_sha256 IS NOT NULL)),
CHECK (definition_event_id IS NULL OR length(definition_event_id) = 32),
CHECK (definition_content_sha256 IS NULL OR length(definition_content_sha256) = 32),
CHECK (instance_event_id IS NULL OR length(instance_event_id) = 32),
CHECK (instance_content_sha256 IS NULL OR length(instance_content_sha256) = 32)
);
CREATE INDEX managed_agent_heads_definition_binding
ON managed_agent_heads (community_id, owner_pubkey, definition_d);
CREATE TABLE managed_agent_revisions (
community_id UUID NOT NULL,
owner_pubkey BYTEA NOT NULL,
agent_pubkey BYTEA NOT NULL,
generation BIGINT NOT NULL CHECK (generation > 0 AND generation <= 9007199254740991),
event_id BYTEA NOT NULL CHECK (length(event_id) = 32),
previous_event_id BYTEA CHECK (previous_event_id IS NULL OR length(previous_event_id) = 32),
state TEXT NOT NULL CHECK (state IN ('active', 'deleted')),
definition_d TEXT,
definition_revision BIGINT,
definition_event_id BYTEA CHECK (definition_event_id IS NULL OR length(definition_event_id) = 32),
definition_content_sha256 BYTEA CHECK (definition_content_sha256 IS NULL OR length(definition_content_sha256) = 32),
instance_event_id BYTEA CHECK (instance_event_id IS NULL OR length(instance_event_id) = 32),
instance_content_sha256 BYTEA CHECK (instance_content_sha256 IS NULL OR length(instance_content_sha256) = 32),
private_event JSONB NOT NULL,
definition_event JSONB,
instance_event JSONB,
committed_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (community_id, owner_pubkey, agent_pubkey, generation),
UNIQUE (community_id, event_id),
FOREIGN KEY (community_id, owner_pubkey, agent_pubkey)
REFERENCES managed_agent_heads (community_id, owner_pubkey, agent_pubkey)
DEFERRABLE INITIALLY DEFERRED
);
-- NIP-PMA kind:30179 contains owner-encrypted runnable agent configuration and
-- is author-only. Preserve every installation's current FTS expression while
-- making existing and future private aggregate rows storage-level unsearchable.
DO $$
DECLARE
existing_expression TEXT;
BEGIN
SELECT pg_get_expr(d.adbin, d.adrelid)
INTO existing_expression
FROM pg_attrdef d
JOIN pg_attribute a
ON a.attrelid = d.adrelid
AND a.attnum = d.adnum
WHERE d.adrelid = 'events'::regclass
AND a.attname = 'search_tsv';
IF existing_expression IS NULL THEN
RAISE EXCEPTION 'events.search_tsv generated expression not found';
END IF;
ALTER TABLE events DROP COLUMN search_tsv;
EXECUTE format(
'ALTER TABLE events ADD COLUMN search_tsv TSVECTOR GENERATED ALWAYS AS (CASE WHEN kind = 30179 THEN NULL::tsvector ELSE (%s) END) STORED',
existing_expression
);
CREATE INDEX idx_events_search_tsv ON events USING GIN (search_tsv);
END $$;