Repáralo ahora
- Suelta el archivo
- La reparación ocurre en local
- Descarga el resultado
Signal Desktop almacena todo tu historial de mensajes en una única base de datos SQLite llamada db.sqlite, pero no es SQLite sin más: Signal usa SQLCipher, una bifurcación que cifra de forma transparente todo el archivo con AES-256, cabecera incluida. Eso significa que un motor de rescate de páginas (este incluido) solo ve ruido de alta entropía y responde file is not a database hasta que el archivo se descifra. Por eso recuperar una base de datos de Signal rota son dos pasos: primero descifrarla localmente con la clave que ya está en tu equipo, produciendo un archivo SQLite en texto plano; luego suelta ese archivo arriba y la herramienta recorre sus páginas en tu navegador y reconstruye una base de datos limpia con cada fila que aún pueda decodificar. Tanto la clave como los datos se quedan en tu dispositivo.
El db.sqlite de Signal está cifrado: descífralo antes de cualquier rescate
La base de datos de Signal Desktop reside en ~/Library/Application Support/Signal/sql/db.sqlite en macOS, en %AppData%\Signal\sql\db.sqlite en Windows y en ~/.config/Signal/sql/db.sqlite en Linux. Como SQLCipher cifra el archivo entero, ejecutar file db.sqlite muestra bytes aleatorios en lugar de la habitual firma SQLite format 3\0. Cualquier herramienta SQLite normal —el intérprete sqlite3, DB Browser o el motor de rescate de esta página— ve texto cifrado, no páginas, y no puede ni va a adivinar la clave.
La clave SQLCipher de 64 caracteres hexadecimales está en config.json, junto a la base de datos. En las versiones antiguas se guarda en claro bajo un campo key; desde 2024 Signal la envuelve bajo encryptedKey usando la API safeStorage de Electron, respaldada por el elemento del Llavero de macOS llamado "Signal Safe Storage", por DPAPI en Windows y por gnome-libsecret / kwallet en Linux. Como la envoltura está atada a tu cuenta del sistema operativo, la clave se desenvuelve en la misma máquina y el mismo usuario que la crearon.
Con la clave en bruto ya disponible, abre la base de datos en cualquier herramienta compatible con SQLCipher (el CLI sqlcipher, o una compilación de DB Browser for SQLite construida con SQLCipher) y ejecuta PRAGMA key = "x'<clave-64-hex>'"; seguido de PRAGMA cipher_compatibility = 4; — Signal incorpora SQLCipher 4. Luego exporta una copia en texto plano con .recover o con VACUUM INTO 'plain.sqlite'. Ese archivo en texto plano es el que sueltas aquí. Trabaja siempre sobre una copia byte a byte, nunca sobre tu único original.
Qué significa "malformed" dentro de una base de datos de Signal
Una vez descifrada, una base de datos de Signal es un archivo SQLite corriente: un array plano de páginas de tamaño fijo (4096 bytes cada una). La página 1 contiene la cabecera y el esquema; cada tabla e índice es un árbol B de páginas interiores que apuntan hacia abajo, a páginas hoja que guardan las filas. Los datos de Signal viven en tablas como messages (cada fila identificada por id, con conversationId, sent_at, received_at, type, una columna body y una columna json que lleva el registro completo del mensaje), conversations, reactions, sessions, items y un índice de texto completo messages_fts (FTS5) usado para las búsquedas.
La corrupción casi siempre es localizada. Un corte de corriente a mitad de una escritura, una sincronización interrumpida, una página rota en un almacenamiento que mintió sobre el vaciado: un solo puntero de un árbol B deja de concordar con los datos y SQLite lanza SQLITE_CORRUPT: Error: database disk image is malformed (11). Rechaza el archivo entero en cuanto no puede fiarse de la estructura, aunque tus demás tablas sigan intactas en sus páginas hoja. El archivo parece mucho peor de lo que está.
El error relacionado file is not a database (SQLITE_NOTADB, código 26) tiene aquí dos significados: o bien la firma de 16 bytes de la cabecera se dañó (las páginas que hay detrás suelen estar intactas), o —mucho más habitual con Signal— apuntaste una herramienta normal al archivo todavía cifrado. Si lo ves antes de descifrar, es el cifrado hablando, no un daño.
Qué recupera el rescate de páginas de una BD de Signal, y qué no
El rescate de páginas no arregla el archivo roto en su sitio; hace lo mismo que el propio .recover de SQLite. Recorre el archivo descifrado página por página y, por cada página que parece una hoja de tabla, lee cada celda directamente —rowid, tipos seriales, valores— y vuelve a insertar cada fila decodificable en una base de datos nueva y vacía. Las páginas dañadas se omiten; las páginas legibles entregan sus filas sobreviva o no el árbol B que las indexaba. Las filas que la cadena de punteros ya no alcanza se encuentran mediante un barrido en bruto, y las filas cuya tabla de origen no se puede identificar van a una tabla lost_and_found indexada por página y celda, en lugar de descartarse. En la práctica, tus filas de messages vuelven con su body y su json intactos aunque lo que se rompiera fuera el índice FTS o el árbol de una sola conversación.
Si el fallo dejó un archivo hermano -wal (el modo WAL prepara las transacciones confirmadas más recientes antes de volcarlas —checkpoint— a db.sqlite), ahí están tus mensajes más nuevos, pero también está cifrado con SQLCipher, así que descífralo junto con el archivo principal. Dada una base de datos en texto plano y su WAL en texto plano, el motor superpone los fotogramas confirmados antes del escaneo para que la recuperación refleje la última transacción confirmada.
Los límites honestos son reales. Los adjuntos no están en la base de datos: Signal guarda las fotos, los vídeos y los archivos como blobs separados y cifrados individualmente bajo attachments.noindex/, así que una fila de messages recuperada conserva el puntero y los metadatos, pero rescatar la BD no descifra el propio archivo multimedia. Todo lo que se haya sobrescrito o truncado físicamente al final del archivo se ha perdido: un archivo SQLite no tiene redundancia con la que reconstruirlo. Y la salida recuperada es siempre sospechosa: como el escaneo lee celdas en bruto, incluidas las páginas de la freelist, pueden reaparecer mensajes borrados y un valor puede volver con la clase de almacenamiento equivocada, así que verifícala contra una fuente conocida y fiable antes de depender de ella.
Por qué esto pertenece a tu propia máquina
La base de datos de un mensajero es el único archivo que menos querrías en un servidor que no controlas: es cada conversación, cada contacto y cada grupo que has conservado. Los servicios de "reparación" basados en subidas copian ese archivo a una infraestructura que no puedes inspeccionar, bajo unas condiciones de retención que tú no redactaste; y con una base de datos de Signal, eso desmonta en silencio todo el sentido de usar Signal.
Aquí el rescate se ejecuta por completo en la pestaña de tu navegador: el archivo en texto plano se lee del disco, se reconstruye localmente y se descarga, y puedes abrir la pestaña Red (Network) y ver cómo salen 0 bytes. Igual de importante: el paso de descifrado también se queda en local —tu clave SQLCipher sale de tu propio Llavero (o de config.json) y la usa una herramienta de tu máquina, nunca se transmite—. Recuperar una base de datos de Signal así significa no confiar a ningún tercero ni los datos ni la clave.
Qué puede y qué no puede reparar
Puede reparar
- Una base de datos de Signal que has descifrado a texto plano (con sqlcipher o una compilación de DB Browser con SQLCipher) y que luego indica "database disk image is malformed"
- Filas de messages —la columna body y el registro json completo— extraídas de las páginas hoja del árbol B que sobrevivieron
- conversations, reactions y otras tablas cuando una sola página o puntero defectuoso tumbó todo el archivo
- Filas que la cadena de punteros no alcanza, mediante un escaneo de páginas en bruto, con las filas no atribuibles colocadas en una tabla lost_and_found
- Los mensajes más nuevos desde un archivo hermano -wal descifrado, superpuestos antes del escaneo
No puede reparar
- El propio db.sqlite todavía cifrado: descífralo primero en local; el motor rescata SQLite en texto plano, no texto cifrado de SQLCipher
- Cualquier cosa sin la clave: un encryptedKey que no puedes desenvolver (Llavero perdido, otra máquina u otro usuario) nunca se convierte en SQLite legible
- Los adjuntos de los mensajes: viven como archivos cifrados por separado bajo attachments.noindex/, no dentro de la base de datos
- Mensajes sobrescritos o truncados físicamente al final del archivo: ninguna herramienta puede inventarlos
- Una garantía de exactitud: pueden reaparecer filas borradas y cambiar los tipos; verifica los datos recuperados antes de depender de ellos
Si una reparación falla, te decimos por qué (datos que faltan frente a estructura dañada), y nunca se te cobra por una reparación fallida.