From b50192e562f5db06a0105a0aca0b6ef70f483abb Mon Sep 17 00:00:00 2001 From: ARCHITECT Date: Wed, 31 Dec 2025 20:05:07 +0000 Subject: [PATCH] Add log schema and documentation --- docs/LOG.md | 59 +++++++++++++++++++++++++++++++++++ schemas/04_log.sql | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 docs/LOG.md create mode 100644 schemas/04_log.sql diff --git a/docs/LOG.md b/docs/LOG.md new file mode 100644 index 0000000..db34dbe --- /dev/null +++ b/docs/LOG.md @@ -0,0 +1,59 @@ +# Schema LOG + +Log inmutable de mensajes del sistema TZZR. + +## Tabla: log.messages + +Registra todos los mensajes entre usuarios y agentes IA. + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | BIGSERIAL | Índice incremental | +| hash | CHAR(64) | SHA256 único del mensaje | +| session_hash | CHAR(64) | SHA256 de la sesión | +| sender_type | ENUM | Tipo de emisor: user, agent, orchestrator, system, tool | +| sender_id | CHAR(64) | Hash del emisor (referencia a players) | +| receiver_type | ENUM | Tipo de receptor: user, agent, orchestrator, system, tool | +| receiver_id | CHAR(64) | Hash del receptor (referencia a players) | +| leader_id | CHAR(64) | Hash del coordinador en multiagente (nullable) | +| role | TEXT | Rol del emisor en el contexto | +| content | TEXT | Contenido del mensaje | +| attachments | JSONB | Adjuntos (archivos, datos) | +| prev_hash | CHAR(64) | Hash del mensaje anterior (cadena de integridad) | +| context_hashes | CHAR(64)[] | Hashes de mensajes incluidos como contexto | +| hashtags | CHAR(64)[] | Hashes de etiquetas asociadas | +| created_at | TIMESTAMPTZ | Timestamp de creación | + +## Inmutabilidad + +- **INSERT**: Permitido +- **UPDATE**: Bloqueado por trigger +- **DELETE**: Bloqueado por trigger + +## Cadena de integridad + +Cada mensaje referencia al anterior via `prev_hash`: + +``` +msg1.hash = SHA256(msg1) +msg2.prev_hash = msg1.hash +msg2.hash = SHA256(msg2) +msg3.prev_hash = msg2.hash +... +``` + +Primer mensaje de sesión: `prev_hash = NULL` + +## Índices + +- `session_hash` - Buscar mensajes de una sesión +- `sender_id` - Mensajes enviados por un actor +- `receiver_id` - Mensajes recibidos por un actor +- `prev_hash` - Seguir cadena de integridad +- `created_at` - Ordenar cronológicamente +- `hashtags` (GIN) - Buscar por etiquetas + +## Relaciones externas + +- `sender_id` y `receiver_id` referencian hashes de `core.players` +- `hashtags` referencian hashes de `core.hashtags` diff --git a/schemas/04_log.sql b/schemas/04_log.sql new file mode 100644 index 0000000..56ad54d --- /dev/null +++ b/schemas/04_log.sql @@ -0,0 +1,76 @@ +-- ============================================ +-- SCHEMA LOG - Sistema TZZR +-- Log inmutable de mensajes +-- ============================================ + +DROP SCHEMA IF EXISTS log CASCADE; +CREATE SCHEMA log; + +-- Extensiones +CREATE EXTENSION IF NOT EXISTS pgcrypto; + +-- Tipos +CREATE TYPE log.actor_type AS ENUM ('user', 'agent', 'orchestrator', 'system', 'tool'); + +-- Tabla principal +CREATE TABLE log.messages ( + id BIGSERIAL PRIMARY KEY, + hash CHAR(64) UNIQUE NOT NULL, + session_hash CHAR(64) NOT NULL, + sender_type log.actor_type NOT NULL, + sender_id CHAR(64) NOT NULL, + receiver_type log.actor_type NOT NULL, + receiver_id CHAR(64) NOT NULL, + leader_id CHAR(64), + role TEXT, + content TEXT NOT NULL, + attachments JSONB DEFAULT '{}', + prev_hash CHAR(64), + context_hashes CHAR(64)[] DEFAULT '{}', + hashtags CHAR(64)[] DEFAULT '{}', + created_at TIMESTAMPTZ DEFAULT NOW() +); + +-- Índices +CREATE INDEX idx_messages_session ON log.messages(session_hash); +CREATE INDEX idx_messages_sender ON log.messages(sender_id); +CREATE INDEX idx_messages_receiver ON log.messages(receiver_id); +CREATE INDEX idx_messages_prev ON log.messages(prev_hash); +CREATE INDEX idx_messages_created ON log.messages(created_at); +CREATE INDEX idx_messages_hashtags ON log.messages USING gin(hashtags); + +-- Función SHA256 +CREATE OR REPLACE FUNCTION log.sha256(data TEXT) RETURNS CHAR(64) AS $$ +BEGIN + RETURN encode(digest(data, 'sha256'), 'hex'); +END; +$$ LANGUAGE plpgsql IMMUTABLE; + +-- Protección: no UPDATE +CREATE OR REPLACE FUNCTION log.prevent_update() RETURNS TRIGGER AS $$ +BEGIN + RAISE EXCEPTION 'UPDATE no permitido en log.messages'; +END; +$$ LANGUAGE plpgsql; + +-- Protección: no DELETE +CREATE OR REPLACE FUNCTION log.prevent_delete() RETURNS TRIGGER AS $$ +BEGIN + RAISE EXCEPTION 'DELETE no permitido en log.messages'; +END; +$$ LANGUAGE plpgsql; + +-- Triggers de protección +CREATE TRIGGER protect_update BEFORE UPDATE ON log.messages + FOR EACH ROW EXECUTE FUNCTION log.prevent_update(); + +CREATE TRIGGER protect_delete BEFORE DELETE ON log.messages + FOR EACH ROW EXECUTE FUNCTION log.prevent_delete(); + +-- Permisos +GRANT USAGE ON SCHEMA log TO tzzr; +GRANT SELECT, INSERT ON log.messages TO tzzr; +GRANT USAGE ON SEQUENCE log.messages_id_seq TO tzzr; + +-- Verificación +SELECT 'Schema log creado' as status;