No description
  • Python 89.9%
  • Shell 7.4%
  • CSS 2.7%
Find a file
gnomy fd290387fc feat: add signal-query skill for asking about message history
"How old are the earliest and latest messages from <number>" took six tool calls
to work out: locate the database, pull the DPAPI-wrapped key through
sigexport.crypto, apply four non-default cipher pragmas, then learn that sent_at
is epoch milliseconds and that key-changes and call records inflate any naive
count. None of that is discoverable from the repo, and all of it recurs for any
question of the form "when did I last talk to X".

scripts/query.py carries the boilerplate so a query is one call, formats epoch
milliseconds as dates, and refuses anything that is not SELECT/WITH -- this
database is the only copy of years of history.

Timestamps are detected by value range rather than column name: aggregate queries
alias them (MIN(sent_at) AS first), so a name-based check silently misses exactly
the queries that most need formatting.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 15:05:53 +02:00
.claude/skills/signal-query feat: add signal-query skill for asking about message history 2026-09-22 15:05:53 +02:00
.github chore: refresh dependencies, drop rye for uv, require Python 3.10 2026-09-22 12:48:40 +02:00
docs docs: add AGENTS.md, move backup reference into docs/ 2026-09-22 14:54:53 +02:00
scripts feat: report what each backup run added 2026-09-22 14:47:47 +02:00
sigexport chore: refresh dependencies, drop rye for uv, require Python 3.10 2026-09-22 12:48:40 +02:00
tests chore: fmt, lint, remove broken test 2025-01-27 09:54:24 +00:00
.gitattributes chore: add .gitattributes to normalize line endings to LF 2026-06-24 14:51:21 +02:00
.gitignore chore: gitignore backup/ and document Signal store snapshots 2026-09-22 13:28:56 +02:00
AGENTS.md feat: add signal-query skill for asking about message history 2026-09-22 15:05:53 +02:00
backup.sh feat: report what each backup run added 2026-09-22 14:47:47 +02:00
export.sh fix: point export.sh at sigexport.exe in Windows venv 2026-06-24 14:42:52 +02:00
INSTALLATION.md update instructions 2024-08-02 07:22:21 +01:00
LICENSE Update license 2022-07-27 13:06:15 -04:00
pyproject.toml chore: refresh dependencies, drop rye for uv, require Python 3.10 2026-09-22 12:48:40 +02:00
README.md docs: add AGENTS.md, move backup reference into docs/ 2026-09-22 14:54:53 +02:00
requirements-dev.lock chore: refresh dependencies, drop rye for uv, require Python 3.10 2026-09-22 12:48:40 +02:00
requirements.lock chore: refresh dependencies, drop rye for uv, require Python 3.10 2026-09-22 12:48:40 +02:00

signal-export

PyPI version

⚠️ NB: Because the latest versions of Signal Desktop protect the database encryption key, so decrypting involves some extra steps. Good luck.

Export chats from the Signal Desktop app to Markdown and HTML files with attachments. Each chat is exported as an individual .md/.html file and the attachments for each are stored in a separate folder. Attachments are linked from the Markdown files and displayed in the HTML (pictures, videos, voice notes).

Currently this seems to be the only way to get chat history out of Signal!

Adapted from mattsta/signal-backup, which I suspect will be hard to get working now.

Example

An export for a group conversation looks as follows:

[2019-05-29, 15:04] Me: How is everyone?
[2019-05-29, 15:10] Aya: We're great!
[2019-05-29, 15:20] Jim: I'm not.

Images are attached inline with ![name](path) while other attachments (voice notes, videos, documents) are included as links like [name](path) so a click will take you to the file.

This is converted to HTML at the end so it can be opened with any web browser. The stylesheet .css is still very basic but I'll get to it sooner or later.

🐧 Installation

  1. Make sure you have Python 3.10 or later installed.

  2. Install this package:

pip install signal-export

# ...if you have the "pipx" command available, you're probably better off installing with "pipx install signal-export"
  1. Then run the script!
sigexport ~/signal-chats

# or for Windows:
python -m sigexport C:\Temp\SignalExport

🪟 Installation: Windows

If you need step-by-step instructions on things like enabling WSL2, please see the dedicated Windows Installation instructions.

Installation nix/nixOS

signal-export is packaged in nixpkgs, so you can run

nix-shell -I nixpkgs=channel:nixpkgs-unstable --packages signal-export --command 'sigexport ~/signal-chats'

If you get an error message about secret-tool not being found, you probably need to install libsecret-tools via your Linux package manager. If you get this on NixOS then just add libsecret in the previous command

nix-shell -I nixpkgs=channel:nixpkgs-unstable --packages signal-export libsecret --command 'sigexport ~/signal-chats'

🚀 Usage

Please fully exit your Signal app before proceeding, otherwise you will likely encounter an I/O disk error, due to the message database being made read-only, as it was being accessed by the app.

See the full help info:

sigexport --help

Disable pagination on HTML:

sigexport --paginate=0 ~/signal-chats

List available chats and exit:

sigexport --list-chats

Export only the selected chats:

sigexport --chats=Jim,Aya ~/signal-chats

You can add --source /path/to/source/dir/ if the script doesn't manage to find the Signal config location. Default locations per OS are below. The directory should contain a folder called sql with db.sqlite inside it.

  • Linux: ~/.config/Signal/
  • Linux Flatpak: ~/.var/app/org.signal.Signal/config/Signal
  • macOS: ~/Library/Application Support/Signal/
  • Windows: ~/AppData/Roaming/Signal/

You can also use --old /previously/exported/dir/ to merge the new export with a previous one. Nothing will be overwritten! It will put the combined results in whatever output directory you specified and leave your previos export untouched. Exercise is left to the reader to verify that all went well before deleting the previous one.

This fork

This copy lives at git.mrpeu.com/gnomy/signal-export and tracks carderne/signal-export. On top of upstream it adds two wrappers:

./export.sh    # export every chat into ./data
./backup.sh    # snapshot the whole Signal Desktop store into ./backup

Both are bash and must be run from WSL, not PowerShell — Windows cannot execute them, and they use rsync and wslpath. They call out to the Windows venv at .venv/Scripts/, so that venv has to exist and be built with a Windows Python 3.10+ (currently 3.14), not the WSL interpreter.

data/ and backup/ are both gitignored. export.sh passes --overwrite, so each run replaces the previous export. For backup.sh — the vaulted key, how a snapshot is taken while Signal is running, restoring, and verifying — see docs/backup.md.

Development

git clone https://git.mrpeu.com/gnomy/signal-export.git
cd signal-export
uv venv
uv pip install -r requirements-dev.lock

Various dev commands:

uv run ruff format          # format
uv run ruff check --fix     # lint
uv run pyright              # typecheck
uv run pytest               # test
uv run sigexport            # run signal-export

To re-resolve the lockfiles after changing pyproject.toml, run the uv pip compile command recorded in the header of each .lock file.

Similar things