mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
feat(push): add public APNs gateway (#1770)
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
@@ -206,9 +206,9 @@ CREATE TABLE events (
|
||||
-- Privacy: encrypted/private routing wrappers and p-gated membership notices
|
||||
-- must never be discoverable through NIP-50 full-text search. NULL tsvector
|
||||
-- never matches `@@`.
|
||||
-- Keep in sync with migrations (final state: 0001 + 0005_agent_turn_metric_fts).
|
||||
-- Keep in sync with migrations (final state: 0001 + 0005 + 0009).
|
||||
search_tsv TSVECTOR GENERATED ALWAYS AS (
|
||||
CASE WHEN kind IN (1059, 30300, 30622, 44100, 44101, 44200) THEN NULL::tsvector
|
||||
CASE WHEN kind IN (1059, 30300, 30350, 30622, 44100, 44101, 44200) THEN NULL::tsvector
|
||||
ELSE to_tsvector('simple', content)
|
||||
END
|
||||
) STORED,
|
||||
@@ -744,3 +744,130 @@ INSERT INTO _operator_global_tables (table_name, reason) VALUES
|
||||
('communities', 'the tenant registry itself; id IS the community key'),
|
||||
('rate_limit_violations', 'deployment abuse/health; never tenant-observable; community_id is an attribution label only'),
|
||||
('_operator_global_tables', 'the registry table itself');
|
||||
-- NIP-PL effective lease state and durable wake outbox. Every key is led by
|
||||
-- community_id: client-provided origin is confirmation only, never routing.
|
||||
CREATE TABLE push_leases (
|
||||
community_id UUID NOT NULL REFERENCES communities(id),
|
||||
author BYTEA NOT NULL CHECK (length(author) = 32),
|
||||
installation_id TEXT NOT NULL CHECK (octet_length(installation_id) BETWEEN 1 AND 64),
|
||||
source_event_id BYTEA NOT NULL CHECK (length(source_event_id) = 32),
|
||||
source_created_at BIGINT NOT NULL,
|
||||
generation BIGINT NOT NULL CHECK (generation > 0),
|
||||
active BOOLEAN NOT NULL,
|
||||
endpoint_enabled BOOLEAN NOT NULL DEFAULT true,
|
||||
app_profile TEXT,
|
||||
endpoint_hash BYTEA CHECK (endpoint_hash IS NULL OR length(endpoint_hash) = 32),
|
||||
endpoint_grant TEXT,
|
||||
max_class TEXT CHECK (max_class IS NULL OR max_class IN ('silent','default','time_sensitive','urgent')),
|
||||
subscriptions JSONB,
|
||||
expires_at BIGINT NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
PRIMARY KEY (community_id, author, installation_id),
|
||||
UNIQUE (community_id, source_event_id),
|
||||
CHECK ((active AND app_profile IS NOT NULL AND endpoint_hash IS NOT NULL AND endpoint_grant IS NOT NULL AND max_class IS NOT NULL AND subscriptions IS NOT NULL)
|
||||
OR (NOT active AND app_profile IS NULL AND endpoint_hash IS NULL AND endpoint_grant IS NULL AND max_class IS NULL AND subscriptions IS NULL))
|
||||
);
|
||||
CREATE UNIQUE INDEX push_leases_endpoint_unique
|
||||
ON push_leases (community_id, author, app_profile, endpoint_hash)
|
||||
WHERE active;
|
||||
CREATE INDEX push_leases_expiry ON push_leases (community_id, expires_at) WHERE active;
|
||||
|
||||
CREATE TABLE push_wake_outbox (
|
||||
community_id UUID NOT NULL REFERENCES communities(id),
|
||||
id UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
author BYTEA NOT NULL CHECK (length(author) = 32),
|
||||
installation_id TEXT NOT NULL,
|
||||
lease_generation BIGINT NOT NULL CHECK (lease_generation > 0),
|
||||
endpoint_hash BYTEA NOT NULL CHECK (length(endpoint_hash) = 32),
|
||||
event_id BYTEA NOT NULL CHECK (length(event_id) = 32),
|
||||
class TEXT NOT NULL CHECK (class IN ('silent','default','time_sensitive','urgent')),
|
||||
expires_at BIGINT NOT NULL,
|
||||
state TEXT NOT NULL DEFAULT 'pending' CHECK (state IN ('pending','sending','delivered','failed')),
|
||||
attempts INTEGER NOT NULL DEFAULT 0 CHECK (attempts >= 0),
|
||||
next_attempt_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
lease_until TIMESTAMPTZ,
|
||||
claim_id UUID,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
PRIMARY KEY (community_id, id),
|
||||
FOREIGN KEY (community_id, author, installation_id)
|
||||
REFERENCES push_leases (community_id, author, installation_id),
|
||||
UNIQUE (community_id, endpoint_hash, event_id)
|
||||
);
|
||||
CREATE INDEX push_wake_outbox_due
|
||||
ON push_wake_outbox (community_id, next_attempt_at) WHERE state = 'pending';
|
||||
CREATE INDEX push_wake_outbox_recovery
|
||||
ON push_wake_outbox (community_id, lease_until) WHERE state = 'sending';
|
||||
-- Durable, deployment-global authority for the public NIP-PL push gateway.
|
||||
-- This state is intentionally outside relay community tenancy: installations
|
||||
-- delegate to relay signing keys and may authorize multiple relay deployments.
|
||||
CREATE TABLE push_gateway_challenges (
|
||||
id UUID PRIMARY KEY,
|
||||
challenge_hash BYTEA NOT NULL CHECK (length(challenge_hash) = 32),
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX push_gateway_challenges_expiry ON push_gateway_challenges (expires_at);
|
||||
|
||||
CREATE TABLE push_gateway_installations (
|
||||
id UUID PRIMARY KEY,
|
||||
app_attest_key_id BYTEA NOT NULL UNIQUE CHECK (octet_length(app_attest_key_id) BETWEEN 1 AND 128),
|
||||
app_attest_public_key BYTEA NOT NULL CHECK (octet_length(app_attest_public_key) BETWEEN 33 AND 256),
|
||||
assertion_counter BIGINT NOT NULL CHECK (assertion_counter BETWEEN 0 AND 4294967295),
|
||||
app_profile TEXT NOT NULL CHECK (app_profile IN ('buzz-ios-production','buzz-ios-sandbox')),
|
||||
token_ciphertext BYTEA NOT NULL CHECK (octet_length(token_ciphertext) BETWEEN 1 AND 2048),
|
||||
token_fingerprint BYTEA NOT NULL CHECK (length(token_fingerprint) = 32),
|
||||
endpoint_epoch BIGINT NOT NULL CHECK (endpoint_epoch > 0),
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
revoked_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
UNIQUE (app_profile, token_fingerprint)
|
||||
);
|
||||
CREATE INDEX push_gateway_installations_expiry ON push_gateway_installations (expires_at) WHERE revoked_at IS NULL;
|
||||
|
||||
CREATE TABLE push_gateway_delegations (
|
||||
id UUID PRIMARY KEY,
|
||||
installation_id UUID NOT NULL REFERENCES push_gateway_installations(id),
|
||||
relay_pubkey BYTEA NOT NULL CHECK (length(relay_pubkey) = 32),
|
||||
endpoint_epoch BIGINT NOT NULL CHECK (endpoint_epoch > 0),
|
||||
generation BIGINT NOT NULL CHECK (generation > 0),
|
||||
not_before TIMESTAMPTZ NOT NULL,
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
revoked_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
UNIQUE (installation_id, relay_pubkey),
|
||||
CHECK (not_before < expires_at)
|
||||
);
|
||||
CREATE INDEX push_gateway_delegations_expiry ON push_gateway_delegations (expires_at) WHERE revoked_at IS NULL;
|
||||
|
||||
CREATE TABLE push_gateway_endpoint_quotas (
|
||||
token_fingerprint BYTEA PRIMARY KEY CHECK (length(token_fingerprint) = 32),
|
||||
window_started_at TIMESTAMPTZ NOT NULL,
|
||||
admitted BIGINT NOT NULL CHECK (admitted >= 0),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX push_gateway_endpoint_quotas_updated ON push_gateway_endpoint_quotas (updated_at);
|
||||
|
||||
CREATE TABLE push_gateway_delivery_auth_replays (
|
||||
relay_pubkey BYTEA NOT NULL CHECK (length(relay_pubkey) = 32),
|
||||
auth_event_id BYTEA NOT NULL CHECK (length(auth_event_id) = 32),
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
PRIMARY KEY (relay_pubkey, auth_event_id)
|
||||
);
|
||||
CREATE INDEX push_gateway_delivery_auth_replays_expiry ON push_gateway_delivery_auth_replays (expires_at);
|
||||
|
||||
CREATE TABLE push_gateway_delivery_request_replays (
|
||||
relay_pubkey BYTEA NOT NULL CHECK (length(relay_pubkey) = 32),
|
||||
request_id UUID NOT NULL,
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
PRIMARY KEY (relay_pubkey, request_id)
|
||||
);
|
||||
CREATE INDEX push_gateway_delivery_request_replays_expiry ON push_gateway_delivery_request_replays (expires_at);
|
||||
|
||||
INSERT INTO _operator_global_tables (table_name, reason) VALUES
|
||||
('push_gateway_challenges', 'public gateway one-time challenges span relay communities'),
|
||||
('push_gateway_installations', 'public gateway installation authority spans relay communities'),
|
||||
('push_gateway_delegations', 'public gateway relay delegations span relay communities'),
|
||||
('push_gateway_endpoint_quotas', 'public gateway endpoint abuse ceilings span relay communities'),
|
||||
('push_gateway_delivery_auth_replays', 'public gateway signed-event replay admission spans relay communities'),
|
||||
('push_gateway_delivery_request_replays', 'public gateway stable request-id admission spans relay communities');
|
||||
|
||||
Reference in New Issue
Block a user