feat(MAT-107): memory encryption & user isolation

- Per-user Fernet encryption for fact/chunk_text/summary fields
- Postgres RLS with memory_app restricted role
- SSL for memory-db connections
- Data migration script (migrate_encrypt.py)
- DB migration (migrate_rls.sql)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Christian Gick
2026-03-06 15:56:14 +00:00
parent 0d83d3177e
commit 108144696b
7 changed files with 263 additions and 17 deletions

40
migrate_rls.sql Normal file
View File

@@ -0,0 +1,40 @@
-- MAT-107: Row-Level Security for memory tables
-- Run as superuser (memory) which owns the tables
-- Create restricted role for memory-service
DO $$
BEGIN
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'memory_app') THEN
CREATE ROLE memory_app LOGIN PASSWORD 'OhugBZP4g4d7rk3OszOq1Xe3yo7hQwEn';
END IF;
END
$$;
-- Grant permissions to memory_app
GRANT CONNECT ON DATABASE memories TO memory_app;
GRANT USAGE ON SCHEMA public TO memory_app;
GRANT SELECT, INSERT, DELETE ON memories, conversation_chunks TO memory_app;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO memory_app;
-- Ensure tables are owned by memory (superuser) so RLS doesn't apply to owner
ALTER TABLE memories OWNER TO memory;
ALTER TABLE conversation_chunks OWNER TO memory;
-- Enable RLS
ALTER TABLE memories ENABLE ROW LEVEL SECURITY;
ALTER TABLE conversation_chunks ENABLE ROW LEVEL SECURITY;
-- Drop existing policies if re-running
DROP POLICY IF EXISTS user_isolation_memories ON memories;
DROP POLICY IF EXISTS user_isolation_chunks ON conversation_chunks;
-- RLS policies: rows visible only when session var matches user_id
-- current_setting with missing_ok=true returns empty string if not set
CREATE POLICY user_isolation_memories ON memories
USING (user_id = current_setting('app.current_user_id', true));
CREATE POLICY user_isolation_chunks ON conversation_chunks
USING (user_id = current_setting('app.current_user_id', true));
-- Verify
SELECT tablename, rowsecurity FROM pg_tables WHERE tablename IN ('memories', 'conversation_chunks');