diff --git a/pmoves/env.tier-agent b/pmoves/env.tier-agent index 916e5d9188..cc12535183 100644 --- a/pmoves/env.tier-agent +++ b/pmoves/env.tier-agent @@ -1,9 +1,8 @@ -# ============================================================================= # PMOVES Agent Tier Environment Variables # Services: agent-zero, archon, mesh-agent, supaserch, deepresearch, # consciousness-service, archon-agent-work-orders, botz-gateway, -# publisher-discord, messaging-gateway, jellyfin-bridge, chat-relay -# ============================================================================= +# publisher-discord, messaging-gateway, jellyfin-bridge, chat-relay, +# github-runner-ctl # Agent tier services handle orchestration and coordination # They connect to internal services (Hi-RAG, NATS, Supabase) @@ -17,9 +16,9 @@ NATS_URL=nats://nats:4222 # --------------------------------------------------------------------------- # Supabase (internal) # --------------------------------------------------------------------------- -SUPABASE_URL=http://postgrest:3000 +SUPABASE_URL=http://supabase_kong_PMOVES.AI:8000 SUPABASE_SERVICE_ROLE_KEY= # REQUIRED: From Supabase CLI -SUPA_REST_URL=http://postgrest:3000/rest/v1 +SUPA_REST_URL=http://supabase_kong_PMOVES.AI:8000/rest/v1 # --------------------------------------------------------------------------- # Hi-RAG Gateway (internal) @@ -50,7 +49,7 @@ AGENTZERO_JETSTREAM=true # --------------------------------------------------------------------------- ARCHON_SERVER_PORT=8091 ARCHON_MCP_PORT=8051 -ARCHON_SUPABASE_BASE_URL=http://postgrest:3000 +ARCHON_SUPABASE_BASE_URL=http://supabase_kong_PMOVES.AI:8000 # --------------------------------------------------------------------------- # DeepResearch Settings @@ -65,3 +64,12 @@ DEEPRESEARCH_NOTEBOOK_EMBED=true # --------------------------------------------------------------------------- DISCORD_WEBHOOK_URL= # Optional: Discord webhook for notifications +# --------------------------------------------------------------------------- +# GitHub Runner Controller (CI/CD orchestration) +# --------------------------------------------------------------------------- +GITHUB_RUNNER_CTL_PORT=8100 +GITHUB_RUNNER_CTL_WEB_PORT=8104 +GITHUB_RUNNER_CTL_LOG_LEVEL=INFO +GITHUB_REPOSITORIES=POWERFULMOVES/PMOVES.AI +GITHUB_RUNNER_REFRESH_INTERVAL=60 + diff --git a/pmoves/env.tier-api b/pmoves/env.tier-api index 87e666b636..bb5a8a2dae 100644 --- a/pmoves/env.tier-api +++ b/pmoves/env.tier-api @@ -9,24 +9,25 @@ # PostgreSQL Connection (for PostgREST) POSTGRES_USER=pmoves -# REQUIRED: Must match env.tier-data POSTGRES_PASSWORD -POSTGRES_PASSWORD=A7sX9kL2mN4pQ8rT6vW3yZ5bC1dE8fG0hJ2 +# REQUIRED: Generate with `openssl rand -base64 24` for production deployments +POSTGRES_PASSWORD= POSTGRES_DB=pmoves PGRST_DB_SCHEMA=public,pmoves_core PGRST_DB_ANON_ROLE=anon PGRST_SERVER_PORT=3000 -# REQUIRED: Generate with `openssl rand -base64 32` -SUPABASE_JWT_SECRET=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJwbW92ZXMtYXBpIn0.fake_jwt_secret_replace_with_real +# REQUIRED: Generate with `openssl rand -base64 32` for production deployments +SUPABASE_JWT_SECRET= # MinIO Connection (for Presign) MINIO_ENDPOINT=minio:9000 -# REQUIRED: Must match env.tier-data MINIO credentials +# REQUIRED: Set unique access key for production deployments MINIO_ACCESS_KEY= +# REQUIRED: Generate with `openssl rand -base64 32` for production deployments MINIO_SECRET_KEY= MINIO_SECURE=false AWS_DEFAULT_REGION=us-east-1 ALLOWED_BUCKETS=assets,outputs -# REQUIRED: Generate with `openssl rand -hex 32` +# REQUIRED: Generate with `openssl rand -hex 32` for production deployments PRESIGN_SHARED_SECRET= # Hi-RAG Gateway (for retrieval-eval) @@ -38,5 +39,13 @@ QDRANT_URL=http://qdrant:6333 QDRANT_COLLECTION=pmoves_chunks_qwen3 # Meilisearch Connection (for Hi-RAG) -# REQUIRED: Must match env.tier-data MEILI_MASTER_KEY -MEILI_MASTER_KEY=JOoQIIAp9sedrTMd0XXL6cdtTRpUYlmh +# REQUIRED: Generate with `openssl rand -hex 32` for production deployments +MEILI_MASTER_KEY= + +# Neo4j Connection (for Hi-RAG Knowledge Graph) +NEO4J_URI=bolt://neo4j:7687 +NEO4J_USER=neo4j +# REQUIRED: Generate with `openssl rand -base64 24` for production deployments +NEO4J_PASSWORD= +# REQUIRED: Generate with `openssl rand -base64 24` for production deployments (format: neo4j/password) +NEO4J_AUTH= diff --git a/pmoves/env.tier-data b/pmoves/env.tier-data index 2f49ab8179..c7f6dfa1ab 100644 --- a/pmoves/env.tier-data +++ b/pmoves/env.tier-data @@ -4,28 +4,31 @@ # Services: postgres, qdrant, neo4j, meilisearch, minio, nats # Scope: Infrastructure credentials ONLY (no external API keys) # -# Copy to `env.tier-data` and fill in real values. The file is gitignored. +# Fill in real values below. This file is gitignored. # ============================================================================= # PostgreSQL (pgvector) # Used by: postgres service, all services that query Supabase/PostgREST POSTGRES_DB=pmoves POSTGRES_USER=pmoves -# REQUIRED: Generate with `openssl rand -base64 24` -POSTGRES_PASSWORD=A7sX9kL2mN4pQ8rT6vW3yZ5bC1dE8fG0hJ2 +# REQUIRED: Generate with `openssl rand -base64 24` for production deployments +# For development, a safe default is provided +POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-pmoves_dev} # Neo4j (Knowledge Graph) # Format: neo4j/ -# REQUIRED: Generate password with `openssl rand -base64 24` -NEO4J_AUTH=neo4j/7kJtbpJ1n1xzMX4F2EAu60K5 +# REQUIRED: Generate password with `openssl rand -base64 24` for production deployments +# For development, use "neo4j/none" to disable auth (healthcheck compatibility) +NEO4J_AUTH=neo4j/none # Meilisearch (Full-text Search) -# REQUIRED: Generate with `openssl rand -hex 32` +# REQUIRED: Generate with `openssl rand -hex 32` for production deployments MEILI_MASTER_KEY= # MinIO (S3-compatible Object Storage) -# REQUIRED: Generate with `openssl rand -base64 32` +# REQUIRED: Set unique username for production deployments MINIO_ROOT_USER= +# REQUIRED: Generate with `openssl rand -base64 32` for production deployments MINIO_ROOT_PASSWORD= # Qdrant (Vector Database) diff --git a/pmoves/env.tier-llm b/pmoves/env.tier-llm index 011732c6be..cdc755dec0 100644 --- a/pmoves/env.tier-llm +++ b/pmoves/env.tier-llm @@ -92,3 +92,19 @@ OLLAMA_URL=http://ollama:11434 # Cloudflare Workers AI CLOUDFLARE_ACCOUNT_ID= CLOUDFLARE_API_TOKEN= + +# ============================================ +# Additional Provider Keys (Optional) +# ============================================ + +# Moonshot AI (Kimi) +# Optional: Add your Moonshot API key +MOONSHOT_API_KEY= + +# Venice AI +# Optional: Add your Venice API key +VENICE_API_KEY= + +# Z.AI (GLM) +# Optional: Add your Z.AI API key +Z_AI_API_KEY= diff --git a/pmoves/env.tier-media b/pmoves/env.tier-media index 004a96b7f2..c1364a5e74 100644 --- a/pmoves/env.tier-media +++ b/pmoves/env.tier-media @@ -7,15 +7,17 @@ NATS_URL=nats://nats:4222 # ----- MinIO (Media Storage) ----- MINIO_ENDPOINT=minio:9000 -MINIO_ACCESS_KEY=minioadmin -MINIO_SECRET_KEY=minioadmin +# REQUIRED: Set unique access key for production deployments +MINIO_ACCESS_KEY= +# REQUIRED: Generate with `openssl rand -base64 32` for production deployments +MINIO_SECRET_KEY= MINIO_SECURE=false MINIO_BUCKET=assets MINIO_OUTPUT_BUCKET=outputs # ----- Supabase (Metadata) ----- SUPA_REST_URL=http://host.docker.internal:65421/rest/v1 -SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.fake_key_replace_with_real +SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key_here # ----- PMOVES.YT ----- YT_CHANNEL_CHECK_INTERVAL=3600 @@ -32,7 +34,3 @@ FRAME_SAMPLE_RATE=5 # ----- Indexer Namespace ----- INDEXER_NAMESPACE=pmoves - -# ----- Invidious (from GitHub Secrets in production) ----- -INVIDIOUS_HMAC_KEY=temp_test_key_please_replace_from_gh_secrets -INVIDIOUS_COMPANION_KEY=temp_test_key_please_replace_from_gh_secrets diff --git a/pmoves/env.tier-worker b/pmoves/env.tier-worker index a040067202..d39655f899 100644 --- a/pmoves/env.tier-worker +++ b/pmoves/env.tier-worker @@ -21,14 +21,15 @@ MEILI_API_KEY=${MEILI_MASTER_KEY} # ----- MinIO (Object Storage for Documents) ----- MINIO_ENDPOINT=minio:9000 -MINIO_ACCESS_KEY=minioadmin -MINIO_SECRET_KEY=minioadmin +# REQUIRED: Set unique access key for production deployments +MINIO_ACCESS_KEY= +# REQUIRED: Generate with `openssl rand -base64 32` for production deployments +MINIO_SECRET_KEY= MINIO_SECURE=false # ----- Supabase (Metadata Storage) ----- SUPA_REST_URL=http://host.docker.internal:65421/rest/v1 -# Generated for fresh start (replace with your real key from Supabase) -SUPABASE_SERVICE_ROLE_KEY=1e5ac31b15768d88851879d4a6ed7b3fd64056b4f72eadca8ae62b4dafd0a6e7 +SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key_here # ----- Open Notebook (for notebook-sync) ----- OPEN_NOTEBOOK_API_URL=http://host.docker.internal:5055 diff --git a/pmoves/supabase/initdb/06_media_analysis.sql b/pmoves/supabase/initdb/06_media_analysis.sql new file mode 100644 index 0000000000..c3c4dfb817 --- /dev/null +++ b/pmoves/supabase/initdb/06_media_analysis.sql @@ -0,0 +1,105 @@ +CREATE TABLE IF NOT EXISTS public.detections ( + id bigserial PRIMARY KEY, + namespace text DEFAULT 'pmoves', + video_id text, + ts_seconds double precision, + label text, + score double precision, + frame_uri text, + meta jsonb NOT NULL DEFAULT '{}'::jsonb, + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS idx_detections_namespace_video_ts + ON public.detections (namespace, video_id, ts_seconds); +CREATE INDEX IF NOT EXISTS idx_detections_label + ON public.detections (label); + +CREATE TABLE IF NOT EXISTS public.segments ( + id bigserial PRIMARY KEY, + namespace text DEFAULT 'pmoves', + video_id text, + label text, + score double precision, + ts_start double precision, + ts_end double precision, + uri text, + meta jsonb NOT NULL DEFAULT '{}'::jsonb, + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS idx_segments_namespace_video_start + ON public.segments (namespace, video_id, ts_start); +CREATE INDEX IF NOT EXISTS idx_segments_label + ON public.segments (label); + +CREATE TABLE IF NOT EXISTS public.emotions ( + id bigserial PRIMARY KEY, + namespace text DEFAULT 'pmoves', + video_id text, + ts_seconds double precision, + label text, + score double precision, + speaker text, + frame_uri text, + meta jsonb NOT NULL DEFAULT '{}'::jsonb, + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS idx_emotions_namespace_video_ts + ON public.emotions (namespace, video_id, ts_seconds); +CREATE INDEX IF NOT EXISTS idx_emotions_label + ON public.emotions (label); + +-- HARDENED: Remove anonymous grants - access via authenticated JWT only +-- GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE public.detections TO anon; +-- GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE public.segments TO anon; +-- GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE public.emotions TO anon; + +ALTER TABLE public.detections ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.segments ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.emotions ENABLE ROW LEVEL SECURITY; + +-- SECURITY: Tenant-scoped RLS policies with namespace isolation (HARDENED) +-- Uses app.current_tenant setting to isolate data by namespace column +-- Set tenant with: SET LOCAL app.current_tenant = 'tenant_name'; +-- HARDENED: Requires authentication (TO authenticated) and no 'pmoves' fallback +DO $$ BEGIN + CREATE POLICY detections_tenant_isolation ON public.detections FOR ALL + TO authenticated + USING (namespace = current_setting('app.current_tenant', true)) + WITH CHECK (namespace = current_setting('app.current_tenant', true)); +EXCEPTION WHEN duplicate_object THEN + -- Drop old policy if exists + DROP POLICY IF EXISTS detections_tenant_isolation ON public.detections; + CREATE POLICY detections_tenant_isolation ON public.detections FOR ALL + TO authenticated + USING (namespace = current_setting('app.current_tenant', true)) + WITH CHECK (namespace = current_setting('app.current_tenant', true)); +END $$; + +DO $$ BEGIN + CREATE POLICY segments_tenant_isolation ON public.segments FOR ALL + TO authenticated + USING (namespace = current_setting('app.current_tenant', true)) + WITH CHECK (namespace = current_setting('app.current_tenant', true)); +EXCEPTION WHEN duplicate_object THEN + DROP POLICY IF EXISTS segments_tenant_isolation ON public.segments; + CREATE POLICY segments_tenant_isolation ON public.segments FOR ALL + TO authenticated + USING (namespace = current_setting('app.current_tenant', true)) + WITH CHECK (namespace = current_setting('app.current_tenant', true)); +END $$; + +DO $$ BEGIN + CREATE POLICY emotions_tenant_isolation ON public.emotions FOR ALL + TO authenticated + USING (namespace = current_setting('app.current_tenant', true)) + WITH CHECK (namespace = current_setting('app.current_tenant', true)); +EXCEPTION WHEN duplicate_object THEN + DROP POLICY IF EXISTS emotions_tenant_isolation ON public.emotions; + CREATE POLICY emotions_tenant_isolation ON public.emotions FOR ALL + TO authenticated + USING (namespace = current_setting('app.current_tenant', true)) + WITH CHECK (namespace = current_setting('app.current_tenant', true)); +END $$; diff --git a/pmoves/supabase/initdb/07_geometry_bus.sql b/pmoves/supabase/initdb/07_geometry_bus.sql new file mode 100644 index 0000000000..3b7c2afd04 --- /dev/null +++ b/pmoves/supabase/initdb/07_geometry_bus.sql @@ -0,0 +1,60 @@ +-- CHIT Geometry Bus core schema +-- Shared by initdb and migrations to keep geometry tables first-class. + +CREATE TABLE IF NOT EXISTS public.anchors ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + kind text NOT NULL CHECK (kind IN ('text','audio','video','image','latent','multi')), + dim integer NOT NULL CHECK (dim > 0), + anchor float4[] NULL, + anchor_enc jsonb NULL, + meta jsonb NOT NULL DEFAULT '{}'::jsonb, + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.constellations ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + anchor_id uuid NOT NULL REFERENCES public.anchors(id) ON DELETE CASCADE, + summary text NULL, + radial_min double precision NULL, + radial_max double precision NULL, + spectrum float4[] NULL, + meta jsonb NOT NULL DEFAULT '{}'::jsonb, + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.shape_points ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + constellation_id uuid NOT NULL REFERENCES public.constellations(id) ON DELETE CASCADE, + modality text NOT NULL CHECK (modality IN ('text','audio','video','image','latent')), + ref_id text NOT NULL, + t_start double precision NULL, + t_end double precision NULL, + frame_idx integer NULL, + token_start integer NULL, + token_end integer NULL, + proj double precision NULL, + conf double precision NULL, + meta jsonb NOT NULL DEFAULT '{}'::jsonb, + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.shape_index ( + shape_id uuid NOT NULL, + modality text NOT NULL, + ref_id text NOT NULL, + loc_hash text NOT NULL, + meta jsonb NOT NULL DEFAULT '{}'::jsonb, + created_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (shape_id, modality, ref_id, loc_hash) +); + +-- Helpful indexes for lookups and metadata filters. +CREATE INDEX IF NOT EXISTS idx_shape_points_lookup ON public.shape_points (modality, ref_id); +CREATE INDEX IF NOT EXISTS idx_shape_points_time ON public.shape_points (t_start, t_end); +CREATE INDEX IF NOT EXISTS idx_constellations_anchor ON public.constellations (anchor_id); +CREATE INDEX IF NOT EXISTS idx_shape_index_ref ON public.shape_index (modality, ref_id); +CREATE INDEX IF NOT EXISTS idx_anchors_kind ON public.anchors (kind); +CREATE INDEX IF NOT EXISTS idx_json_meta_anchors ON public.anchors USING GIN (meta); +CREATE INDEX IF NOT EXISTS idx_json_meta_constellations ON public.constellations USING GIN (meta); +CREATE INDEX IF NOT EXISTS idx_json_meta_points ON public.shape_points USING GIN (meta); +CREATE INDEX IF NOT EXISTS idx_json_meta_shape_index ON public.shape_index USING GIN (meta); diff --git a/pmoves/supabase/initdb/09_geometry_rls.sql b/pmoves/supabase/initdb/09_geometry_rls.sql new file mode 100644 index 0000000000..3d13adee49 --- /dev/null +++ b/pmoves/supabase/initdb/09_geometry_rls.sql @@ -0,0 +1,50 @@ +-- Enable RLS and add permissive read policies for Geometry Bus tables (authenticated only) +-- Date: 2025-09-08 + +-- Optional read-only role for direct DB access (non-Supabase JWT) +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'pmoves_ui') THEN + CREATE ROLE pmoves_ui NOLOGIN; + END IF; +END$$; + +GRANT USAGE ON SCHEMA public TO pmoves_ui; +GRANT SELECT ON public.anchors, public.constellations, public.shape_points, public.shape_index TO pmoves_ui; + +-- Enable RLS +ALTER TABLE public.anchors ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.constellations ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.shape_points ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.shape_index ENABLE ROW LEVEL SECURITY; + +-- Tenant-scoped read policies for Geometry Bus tables +-- SECURITY: Uses namespace-based tenant isolation via app.current_tenant setting +-- NOTE: Set 'app.current_tenant' with SET LOCAL app.current_tenant = 'tenant_name'; +-- HARDENED: Removed 'pmoves' fallback - requires explicit tenant for all access +-- HARDENED: Requires authentication (TO authenticated) for JWT-based access +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='anchors' AND policyname='read_anchors_tenant' + ) THEN + EXECUTE 'CREATE POLICY read_anchors_tenant ON public.anchors FOR SELECT TO authenticated USING (namespace = current_setting(''app.current_tenant'', true))'; + END IF; + IF NOT EXISTS ( + SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='constellations' AND policyname='read_constellations_tenant' + ) THEN + EXECUTE 'CREATE POLICY read_constellations_tenant ON public.constellations FOR SELECT TO authenticated USING (namespace = current_setting(''app.current_tenant'', true))'; + END IF; + IF NOT EXISTS ( + SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='shape_points' AND policyname='read_shape_points_tenant' + ) THEN + EXECUTE 'CREATE POLICY read_shape_points_tenant ON public.shape_points FOR SELECT TO authenticated USING (namespace = current_setting(''app.current_tenant'', true))'; + END IF; + IF NOT EXISTS ( + SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='shape_index' AND policyname='read_shape_index_tenant' + ) THEN + EXECUTE 'CREATE POLICY read_shape_index_tenant ON public.shape_index FOR SELECT TO authenticated USING (namespace = current_setting(''app.current_tenant'', true))'; + END IF; +END$$; + +-- No write policies: inserts/updates/deletes require service role (bypass RLS) diff --git a/pmoves/supabase/sql/001_init.sql b/pmoves/supabase/sql/001_init.sql new file mode 100644 index 0000000000..2935a632cd --- /dev/null +++ b/pmoves/supabase/sql/001_init.sql @@ -0,0 +1,3 @@ +create table if not exists agent_memory(id bigserial primary key); +create table if not exists extractions(id bigserial primary key); +create table if not exists studio_board(id bigserial primary key); diff --git a/pmoves/supabase/sql/006_media_analysis.sql b/pmoves/supabase/sql/006_media_analysis.sql new file mode 100644 index 0000000000..e178d1d198 --- /dev/null +++ b/pmoves/supabase/sql/006_media_analysis.sql @@ -0,0 +1,71 @@ +-- Media analysis tables (dev RLS) +CREATE TABLE IF NOT EXISTS public.detections ( + id bigserial PRIMARY KEY, + namespace text DEFAULT 'pmoves', + video_id text, + ts_seconds double precision, + label text, + score double precision, + frame_uri text, + meta jsonb NOT NULL DEFAULT '{}'::jsonb, + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS idx_detections_namespace_video_ts + ON public.detections (namespace, video_id, ts_seconds); +CREATE INDEX IF NOT EXISTS idx_detections_label + ON public.detections (label); + +CREATE TABLE IF NOT EXISTS public.segments ( + id bigserial PRIMARY KEY, + namespace text DEFAULT 'pmoves', + video_id text, + label text, + score double precision, + ts_start double precision, + ts_end double precision, + uri text, + meta jsonb NOT NULL DEFAULT '{}'::jsonb, + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS idx_segments_namespace_video_start + ON public.segments (namespace, video_id, ts_start); +CREATE INDEX IF NOT EXISTS idx_segments_label + ON public.segments (label); + +CREATE TABLE IF NOT EXISTS public.emotions ( + id bigserial PRIMARY KEY, + namespace text DEFAULT 'pmoves', + video_id text, + ts_seconds double precision, + label text, + score double precision, + speaker text, + frame_uri text, + meta jsonb NOT NULL DEFAULT '{}'::jsonb, + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS idx_emotions_namespace_video_ts + ON public.emotions (namespace, video_id, ts_seconds); +CREATE INDEX IF NOT EXISTS idx_emotions_label + ON public.emotions (label); + +GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE public.detections TO anon; +GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE public.segments TO anon; +GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE public.emotions TO anon; + +ALTER TABLE public.detections ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.segments ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.emotions ENABLE ROW LEVEL SECURITY; + +DO $$ BEGIN + CREATE POLICY detections_anon_all ON public.detections FOR ALL TO anon USING (true) WITH CHECK (true); +EXCEPTION WHEN duplicate_object THEN NULL; END $$; +DO $$ BEGIN + CREATE POLICY segments_anon_all ON public.segments FOR ALL TO anon USING (true) WITH CHECK (true); +EXCEPTION WHEN duplicate_object THEN NULL; END $$; +DO $$ BEGIN + CREATE POLICY emotions_anon_all ON public.emotions FOR ALL TO anon USING (true) WITH CHECK (true); +EXCEPTION WHEN duplicate_object THEN NULL; END $$; diff --git a/pmoves/supabase/sql/007_geometry_bus.sql b/pmoves/supabase/sql/007_geometry_bus.sql new file mode 100644 index 0000000000..3b7c2afd04 --- /dev/null +++ b/pmoves/supabase/sql/007_geometry_bus.sql @@ -0,0 +1,60 @@ +-- CHIT Geometry Bus core schema +-- Shared by initdb and migrations to keep geometry tables first-class. + +CREATE TABLE IF NOT EXISTS public.anchors ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + kind text NOT NULL CHECK (kind IN ('text','audio','video','image','latent','multi')), + dim integer NOT NULL CHECK (dim > 0), + anchor float4[] NULL, + anchor_enc jsonb NULL, + meta jsonb NOT NULL DEFAULT '{}'::jsonb, + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.constellations ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + anchor_id uuid NOT NULL REFERENCES public.anchors(id) ON DELETE CASCADE, + summary text NULL, + radial_min double precision NULL, + radial_max double precision NULL, + spectrum float4[] NULL, + meta jsonb NOT NULL DEFAULT '{}'::jsonb, + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.shape_points ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + constellation_id uuid NOT NULL REFERENCES public.constellations(id) ON DELETE CASCADE, + modality text NOT NULL CHECK (modality IN ('text','audio','video','image','latent')), + ref_id text NOT NULL, + t_start double precision NULL, + t_end double precision NULL, + frame_idx integer NULL, + token_start integer NULL, + token_end integer NULL, + proj double precision NULL, + conf double precision NULL, + meta jsonb NOT NULL DEFAULT '{}'::jsonb, + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.shape_index ( + shape_id uuid NOT NULL, + modality text NOT NULL, + ref_id text NOT NULL, + loc_hash text NOT NULL, + meta jsonb NOT NULL DEFAULT '{}'::jsonb, + created_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (shape_id, modality, ref_id, loc_hash) +); + +-- Helpful indexes for lookups and metadata filters. +CREATE INDEX IF NOT EXISTS idx_shape_points_lookup ON public.shape_points (modality, ref_id); +CREATE INDEX IF NOT EXISTS idx_shape_points_time ON public.shape_points (t_start, t_end); +CREATE INDEX IF NOT EXISTS idx_constellations_anchor ON public.constellations (anchor_id); +CREATE INDEX IF NOT EXISTS idx_shape_index_ref ON public.shape_index (modality, ref_id); +CREATE INDEX IF NOT EXISTS idx_anchors_kind ON public.anchors (kind); +CREATE INDEX IF NOT EXISTS idx_json_meta_anchors ON public.anchors USING GIN (meta); +CREATE INDEX IF NOT EXISTS idx_json_meta_constellations ON public.constellations USING GIN (meta); +CREATE INDEX IF NOT EXISTS idx_json_meta_points ON public.shape_points USING GIN (meta); +CREATE INDEX IF NOT EXISTS idx_json_meta_shape_index ON public.shape_index USING GIN (meta);