Skip to content

Persistencia

Postgres (DB bridge)

bridge_instance              -- config + state machine timestamps
├── name (PK)
├── jid                      -- WhatsApp JID (NULL hasta pareado)
├── paired_at, ready_at, last_event_at
├── inbox_id                 -- arbitrario, lo decide el integrador
├── events_webhook_url       -- POST destino de lifecycle events
├── spamguard_enabled, spamguard_window_ms, spamguard_min_chars
├── last_qr_msg_id
└── owner_tag                -- string libre para correlación tenant→instancia

bridge_dedup                 -- idempotencia incoming + LID-twin
├── instance_name, remote_jid, content_hash (composite PK)
└── seen_at

bridge_usage_daily           -- counters diarios por instancia
├── instance, day (PK)
├── messages_in, messages_out
├── spamguard_blocks, lifecycle_events
└── updated_at

bridge_outgoing_queue        -- outbox persistido para reconnect
├── id (BIGSERIAL PK)
├── instance, remote_jid
├── payload (JSONB)          -- WebhookPayload completo
├── enqueued_at, expires_at  -- TTL default 5 min
├── attempts, last_error
├── status                   -- pending | sent | expired | failed
└── sent_at

bridge_audit_log             -- append-only, triggers rechazan UPDATE/DELETE
├── id (BIGSERIAL PK)
├── ts, actor, action
├── instance, target
└── metadata (JSONB)

whatsmeow_*                  -- internas de whatsmeow (sessions, keys, etc.)

State in-memory (Go)

Estructura Granularidad Persiste en restart
SpamguardTracker last-2 history + block counter (instance, jid_user) no
manager.disconnectNotified instance no
manager.pendingReconnected instance no
manager.pendingUnreachable instance no
banwatch ring buffer de eventos send instance no
usage.Tracker deltas no-flushed (instance, day) no (DB se actualiza c/60s)

Todo el state in-memory es transitorio por diseño — los efectos importantes (usage counters, audit log, outbox payloads, spamguard config) viven en Postgres y sobreviven restarts.

Glosario

Primary key (PK): columna(s) que identifican unívocamente una fila. qrsgen las usa para idempotencia en upserts y para JOINs rápidos.

Composite PK: clave primaria formada por varias columnas. Ejemplo: (instance_name, remote_jid, content_hash) en bridge_dedup — la combinación de las tres identifica una entrada única.

JSONB: tipo de Postgres que guarda JSON en formato binario indexable. qrsgen lo usa para payload (outbox) y metadata (audit log).

BIGSERIAL: secuencia auto-incrementable de 64 bits que Postgres genera automáticamente para columnas id. Sobrevive a TRUNCATE y restarts.

TIMESTAMPTZ: timestamp con zona horaria. Se almacena en UTC internamente y Postgres lo convierte al timezone del cliente al leer.

Schema migrations: cambios a la estructura de las tablas (nuevas columnas, nuevas tablas, índices). qrsgen las ejecuta cada boot vía EnsureSchema() con IF NOT EXISTS.

Ring buffer: estructura circular de tamaño fijo que sobrescribe elementos viejos cuando se llena. BanWatcher la usa para conservar solo los últimos N minutos de eventos por instancia.

State machine: modelo donde un sistema solo puede estar en uno de varios estados predefinidos y transiciona entre ellos según eventos. bridge_instance modela el ciclo de una sesión WhatsApp con timestamps (paired_at, ready_at, etc.).

LID-twin dedup: técnica para detectar y descartar mensajes duplicados que WhatsApp Multi-Device emite por ambas direcciones (JID PN y JID LID) del mismo cliente.