# wx-cli: reading your own WeChat database from the command line

> wx-cli decrypts the local WeChat 4.x SQLCipher databases on your own machine, so that conversations, contacts, favourites and statistics can be queried or exported from a terminal. The interesting engineering is in how it obtains the database keys without disabling system integrity protection, and the interesting operational detail is that every result carries a meta block telling you how much to trust it.

**jackwener/wx-cli-again** — WeChat local data CLI (query/decrypt/export) — fresh start from wx-cli

- Repository: https://github.com/jackwener/wx-cli-again
- Stars: 866 · Forks: 1,782
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/jackwener-wx-cli-again

## A local reader for a database that is designed not to be read

wx-cli is described as a WeChat local data CLI for querying, decrypting and exporting, and the README puts it in Chinese as a tool for querying local WeChat data from the command line. The scope is sessions, chat history, search, contacts, group members, group nicknames, favourites, statistics and export.

The data it reads is the local WeChat 4.x database, which is encrypted with SQLCipher. The Cargo.toml description says it plainly, that it decrypts the SQLCipher databases, queries the chat history and watches for new messages, and the key words in the manifest are wechat, sqlcipher, decrypt and cli.

The design choice that makes it fast is that it does not decrypt everything in advance. Instead it opens the encrypted database online, in process, and decrypts as it reads. A background daemon holds that state, and the README says that a daemon caches the decrypted database and reuses it when the modification time has not changed, which is where the millisecond response time comes from. The daemon starts automatically on the first query, and the README notes that after initialisation the daily commands need no root privileges and do not require WeChat to keep running.

Two properties are worth stating up front, because they shape everything else. The data never leaves the machine, and there is no pre-decryption step that would write plaintext copies of your conversations to disk.

The tool is also built to be driven by an agent rather than a person. The commands return a wrapper with a meta block, so a script can read the freshness and source information of a result instead of guessing.

## Key extraction needs a graphical terminal and root, but not SIP

The hard part of the project is not the querying, it is getting the database key in the first place, and the README is unusually detailed about it.

The key extraction runs in two phases, and neither of them requires disabling system integrity protection. The first phase scans the process memory for the key and salt pattern, using the salt adjacency. The second phase attaches an LLDB hook to the crypto creation call, which fills in the per-database AES keys that have not been loaded into memory yet, for example because that conversation has not been opened recently.

The constraints on how you run it are the part to plan for. On macOS you need WeChat 4.x installed and logged in, and you need to run the initialisation from the machine's own graphical terminal rather than over SSH, because the process inspection primitives that are needed are not available in a remote session. You need sudo, and the system may prompt you to grant developer tools permission. You do not need to disable SIP, and you do not need to re-sign WeChat with an ad-hoc signature, because the default path does not modify the application at all.

The README also explains the cases where it still fails, which is where the details matter. An official package with the hardened runtime enabled needs the local terminal and sudo, and if that fails you enable your terminal under developer tools in the system settings. Some official 4.x builds are already ad-hoc signed, in which case a user-space LLDB attach often works without elevation. And re-signing WeChat is explicitly discouraged, because it disturbs the permissions model and can break official account features and screenshots, so it is only worth considering in a headless situation such as SSH where the process inspection call keeps failing.

On Linux the initialisation is a single sudo command, and on Windows it is the same command run from an administrator PowerShell. The Windows path is different in kind rather than in degree, because the manifest carries a Windows dependency set built around process and thread inspection.

## doctor is the command that tells you whether to trust the output

The diagnostic commands are the centre of the workflow, and the README is explicit that the key extraction is the main path rather than an emergency measure.

The main diagnostic is a check of the environment, the keys, the shards and the SQLCipher layer, and it comes with a variant that produces JSON plus repair suggestions, and a key listing that shows which keys exist and which coverage is missing:

```bash
wx doctor
wx doctor --fix --json
wx key list
```

A separate command writes a key by hand for a specific database and validates it, and the recommended way to fill a gap is the extraction command with a hook duration:

```bash
sudo wx key extract --hook-seconds 90
```

The duration is not arbitrary. Some databases are cold, meaning their key has never been loaded into memory in this session, and the hook can only capture a key while the application is using it. So the workflow is to run the extraction with a hook window, open the relevant conversation in WeChat while it waits so the key gets loaded, and then check again.

The verification sequence at the end of the setup is short:

```bash
wx --version
wx doctor
wx sessions
```

Seeing recent sessions with the important checks passing in the diagnostic is the definition of a working installation, and the README notes that the daemon starts on the first query, so there is nothing extra to launch.

For an upgrade, the same three commands reappear in a different order. You pull, rebuild in release mode, copy the binary over the old one, and then check the version, which is expected to match the version in the project file.

## The everyday commands, and the chat type that decides what you are looking at

Once the keys are in place, the command set is conventional and quick to learn.

```bash
wx sessions
wx unread
wx new-messages
```

The first lists recent sessions, the second lists sessions with unread messages, with a filter for looking only at real people and excluding official accounts and folded entries, and the third returns whatever arrived since the last check, which is the incremental one a script would poll.

The history and search commands take a term and a limit, and the limit is described as a default that exists to avoid printing too much at once rather than a hard cap. History also filters by date range, and search can be restricted to a particular conversation, so a query can be narrowed to one group and one window before it runs.

The output carries a chat type field with the values private, group, official account and folded, which matters because the official account value covers public accounts, subscription accounts, service accounts and system notifications, and folded covers the two aggregated entries that the WeChat client shows for folded subscriptions and folded group chats. The distinction is what lets you filter a chat list down to actual conversations.

In group chats the display name comes from the group nickname when there is one, and otherwise falls back to the contact remark, then the WeChat nickname, then the username. On top of that, the output includes a stable identity set, a sender username that is the identifier which distinguishes two members with the same nickname, a contact display name and the group nickname itself. When the identifier cannot be resolved, those fields are simply not emitted, so a downstream filter is never polluted with empty values.

## meta is how the tool admits what it does not know

Every session and message command attaches a meta block, and this is the most interesting design decision in the project.

The meta block carries a status, with the values ok, possibly stale, possibly stale with unknown shards, and windowed. It lists the unknown shards, meaning the shard files that exist on disk but whose keys the daemon does not currently hold, and it is non-empty when you should run the key extraction command before trusting the output. It reports the timestamp of the newest message in the data that was hit, and which shard it came from, and separately the newest timestamp that WeChat itself recorded in the session database. If that second value is clearly ahead of the first, messages are missing from the result.

That comparison is the point. A tool that decrypts part of a database and cannot tell you which part is an uncomfortable tool, and this one tells you.

The split between streams is deliberate. A human running the command sees actionable warnings on standard error, while an agent or a script reads the machine-readable meta on standard output. There are two hidden flags for deeper inspection, one that adds per-shard timestamps and the cache mode per shard, and one that adds the real shard paths.

Quoted messages are rendered as well, with the current reply and the quoted text beneath it, and the type filters for links, files, merged conversation records and quoted messages are also applied during search, matching the decompressed quoted text.

The Moments, or SNS, side has its own three commands, separating notifications from posts, with the notification command showing likes and comments, unread by default, the feed command showing recent posts with filters for author and date, and a search command for the full text of posts.

## A Rust binary with a bundled SQLCipher, and an upstream that is private

The packaging story has a wrinkle that the README is upfront about. The current source lives at a different organisation and that repository is private, so an anonymous download or the old public npm package will not give you the latest binary, and the recommended route is to build from source with read access to the repository.

The build itself is a standard Cargo release build, and the manifest is configured for a single binary named wx:

```bash
cargo build --release
mkdir -p ~/.local/bin
cp target/release/wx ~/.local/bin/wx
wx --version
```

The dependency list explains what the tool does. The command line is clap with derive, the async runtime is tokio with the full feature set, the serialisation is serde with JSON and YAML support, and the database access is rusqlite with the vendored SQLCipher and OpenSSL build, which is the comment that says the encrypted database is opened online to avoid a full decryption. The cryptography crates, aes, cbc, hmac, sha2 and pbkdf2, cover the key handling, zstd handles decompression, md5 exists because the contact table is named with an md5 of the identifier, base64 is for the attachment identifier encoding, and roxmltree and regex do the XML and text work. The interprocess crate provides the named pipe IPC on Windows while Unix uses a socket from the async runtime directly, and the release profile is tuned hard, with link time optimisation, a single codegen unit and symbols stripped.

There is also an agent install path. The skills CLI installs the package as a skill for coding agents, either locally or globally, and the agent then reads the skill file in the repository to learn how to install and call the tool:

```bash
npx skills add botiverse/wx-cli
```

The repository also carries a Chinese README, an agent instruction file, a separate Claude instruction file, an example configuration file and the install scripts for both shells, so the packaging covers both a human terminal and an agent runtime.

## Conclusion

Adopt wx-cli if you want to query, search or export the WeChat history on your own machine, for a personal archive, a migration or a script that an agent can call, because the daemon keeps the decrypted database open and cached and every command returns machine-readable output. Do not adopt it on a machine where the WeChat account is not yours, and do not plan on it working over SSH, since the key extraction needs a local graphical terminal and elevated privileges. Before you rely on a result, read the meta block, because a status of possibly_stale or a non-empty unknown_shards means part of your history was not decrypted, and the fix is the key extraction command with a hook duration rather than a reinstall.

## FAQ

### What does wx-cli do with local WeChat data?

It decrypts the local WeChat 4.x SQLCipher databases on your own machine so that sessions, chat history, contacts, favourites and statistics can be queried, searched and exported from the command line. The data stays local, and a background daemon caches the decrypted database and reuses it while the modification time is unchanged.

### Do I need to disable SIP or re-sign WeChat to use wx-cli?

No. The README states that key extraction does not depend on disabling SIP, that it needs the local graphical terminal with sudo, and that the default path does not modify the WeChat application. It advises against ad-hoc re-signing because it disturbs the permission model, and suggests it only as a last resort for headless cases.

### How do I initialise wx-cli and check it worked?

With WeChat 4.x installed and logged in, run sudo wx init on macOS and Linux, or wx init from an administrator PowerShell on Windows, then verify with wx --version, wx doctor for the key, shard and SQLCipher health check, and wx sessions. The daemon starts on the first query.

### What does the meta block in the output tell me?

It carries a status of ok, possibly stale, possibly stale with unknown shards, or windowed, the list of shards present on disk whose keys the daemon does not have, the newest message timestamp and the shard it came from, and the newest timestamp WeChat itself recorded, so you can tell when messages are missing. Warnings go to standard error and the meta goes to standard output.

### How do I build or update wx-cli?

The README recommends building from source, since the upstream repository is private and the old public npm package does not carry the latest binary. You clone with git, run cargo build --release, copy target/release/wx into a directory on your path, and check wx --version. To update an existing clone you pull, rebuild and copy the binary again.

## Sources

- [Issues](https://github.com/jackwener/wx-cli-again/issues)
- [jackwener/wx-cli-again on GitHub](https://github.com/jackwener/wx-cli-again)
- [License: Apache-2.0](https://github.com/jackwener/wx-cli-again/blob/main/LICENSE)
- [README](https://github.com/jackwener/wx-cli-again/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jackwener-wx-cli-again
