chatmux
Local-first personal chat data layer daemon. Connects IM platforms via child-process adapters, stores to JSONL + SQLite/FTS5, exposes MCP tools for AI clients.
Documentation
chatmux
Local-first personal chat data layer daemon. Connects IM platforms (v0.1: LINE) via child-process adapters, stores messages to JSONL + SQLite/FTS5, exposes MCP tools for AI clients.
The three repos
chatmux is the core. Platforms plug in below it, consumers sit above it, and both sides of that
boundary live in their own repos:
| Repo | Role |
|---|---|
| chatmux (this one) | Core daemon: storage, safety rail, MCP server, LINE adapter |
| chatmux-adapter-telegram | Second platform adapter (Telegram, MTProto user session) |
| chat.nvim | Reference consumer: read and reply to chats inside Neovim |
Adapters speak the adapter protocol; consumers speak
MCP. Either side can be replaced without touching the other.
Quickstart
1. Install
git clone https://github.com/echoedinvoker/chatmux.git
cd chatmux
bun install2. Decide whether to connect an account yet
With no `adapters.json`, `bun run start` launches the LINE adapter, which means step 3 puts
your LINE account on the line — read Account Risk Warning before you
run it. If you would rather look around first, start with no adapter at all:
mkdir -p ~/.local/share/chatmux
cat > ~/.local/share/chatmux/adapters.json ⚠️ **If you are on an older unit with `Restart=on-failure`, `kill -TERM` will not bring
> it back — and that is not a missing restart policy.** systemd counts SIGTERM, SIGHUP,
> SIGINT and SIGPIPE as an intended stop, so `on-failure` leaves the service sitting in
> `inactive` after any of them. Only `kill -9` (SIGKILL) counts as a failure there.
>
> With the `Restart=always` this unit now ships, **TERM comes back too** — measured
> 2026-08-02: `kill -TERM $MainPID` moved `NRestarts` 1 → 2 and produced a new `MainPID`
> within the 10s `RestartSec`. That makes TERM the useful test: `kill -9` restarts under
> *either* setting, so it cannot tell you which one is in effect. If you want to confirm
> `always` is live, send TERM and watch `systemctl --user show chatmux -p MainPID,NRestarts`
> change.
`StartLimitIntervalSec=300` / `StartLimitBurst=5` cap a crash loop: five starts inside five
minutes and systemd stops trying, leaving the unit `failed` for you to look at rather than
restarting into the same wall forever. Clear it with `systemctl --user reset-failed chatmux`.
## Containers
A systemd user service is the intended way to run chatmux. If you want it in a
container instead, [`deploy/container/`](deploy/container/) is a reference that builds
and answers — not an official image, and it runs **zero adapters**, because adapters
hold logged-in sessions and a container you rebuild is the wrong home for those.
The one thing you cannot skip is `CHATMUX_MCP_HOST`. The daemon binds `127.0.0.1` by
default, which inside a container is the container's own loopback — a published port
then maps to a socket nobody is listening on, and every connection is refused while the
logs look perfectly healthy. Read `deploy/container/README.md` before assuming your
port mapping is broken.
## Developmentbun run dev # Start with --watch (auto-reload)
bun test # Run all tests
bun run start # Start daemon
See `docs/` for detailed architecture and protocol documentation.
## Limitations
Known and accepted, with what would make each worth revisiting.
- **The chat list caps at 1000, silently.** `chat://chats` is hard-coded to that limit. Consumers
can detect an overflow by comparing the `total` field against what arrived, so it will not bite
you without saying so. Worth raising once a vault approaches ~500 chats, or the first time that
completeness check fires.
- **The JSONL log holds duplicate history.** Backfill re-ingested some messages many times over,
leaving the event log several times larger than the messages in it. This has stopped: recent
growth is almost entirely new distinct messages, and the worst-case duplicate count has been
frozen across repeated measurements. It is not a correctness problem — ingestion is idempotent
and the SQLite projection is unaffected — so the fix, if ever needed, is a one-off compaction
rather than a code change. Worth doing if the log passes ~500 MB, if the duplicate count starts
climbing again, or if cold start slows noticeably.
- **Retractions in Telegram one-to-one chats are missed.** Group retractions land; direct ones do
not, because the adapter cannot recover the chat id for those events from its entity cache, and
core will not match a message on id alone — that ambiguity is exactly what the storage key was
widened to remove. So a message you retracted on your phone can stay visible here. Worth fixing
once the adapter can resolve the chat id itself, or as soon as retraction accuracy matters to a
consumer.
- **Reactions are not stored at all.** The platforms send them; no layer reads them. Nothing in
core, the schema, or the MCP surface represents a reaction, so a consumer cannot show what a
phone shows. Worth building when reactions carry meaning you would otherwise miss — it is new
storage, not a display tweak.
- **`read_receipt` is declared but never emitted.** The LINE adapter advertises the capability and
core is ready to ingest it; nothing constructs the event. Whether read state should reach a UI
at all is an open product question, not a pending bug — but the declaration is wrong today, so
do not branch on `supported_events` for this one. Worth fixing as soon as any consumer does
branch on it, or once that product question gets an answer.
## ⚠️ Account Risk Warning
This project uses **@evex/linejs**, an unofficial LINE client library. Using unofficial APIs may violate LINE's Terms of Service. Your LINE account may be restricted, suspended, or permanently banned. **Use at your own risk.**
The IOSIPAD device slot is used to avoid interfering with your phone's LINE app, but LINE may change their multi-device policy at any time.
## ⚠️ Legal Disclaimer
This software is provided "as is", without warranty of any kind. The author is not responsible for any consequences of using this software, including but not limited to account restrictions, data loss, or violations of third-party terms of service.
This is a personal tool for personal use. Do not use it for spam, harassment, unauthorized access to others' messages, or any illegal activity.
## 🔒 Privacy Disclosure
chatmux stores **decrypted message content in plaintext** on your local machine:
- `~/.local/share/chatmux/events.jsonl` — all events (append-only)
- `~/.local/share/chatmux/chatmux.db` — SQLite database with messages, contacts, chats
- `~/.local/share/chatmux/adapters/line/auth.json` — LINE auth token
- `~/.local/share/chatmux/adapters/line/storage.json` — E2EE key storage
These files are protected by filesystem permissions (owner-only). **Do not share these files.** The auth token grants full access to your LINE account. The E2EE keys can decrypt your messages.
v0.1 does not encrypt the database. SQLCipher encryption is planned for v0.2.
## License
MITFrequently asked questions
What is chatmux?
chatmux is Local-first personal chat data layer daemon. Connects IM platforms via child-process adapters, stores to JSONL + SQLite/FTS5, exposes MCP tools for AI clients.
How do I install chatmux?
Open the GitHub repository and follow its README. Most MCP servers are added to your client's MCP config, then called by your agent.
Is chatmux open source?
Yes — it is hosted on GitHub at https://github.com/echoedinvoker/chatmux and has 1 stars.
Related MCP tools
The go-to web for your AI coding agent — local-first search, fetch, crawl & research over MCP. No API keys, no cloud, $0/query. Public beta.
Open-source cross-agent memory layer for coding agents via MCP. Compatible with Claude Code, Codex, Cursor, Windsurf, Gemini CLI, Antigravity, OpenClaw, Hermes Agent, Oh-my-Pi, Pi, Copilot, Kiro, OpenCode, and Trae.
Open-source coding agent memory. Records issues, attempts, fixes and decisions, then warns your agent before it repeats an approach that already failed. Native MCP server for Claude Code, Cursor, Antigravity and Codex. 100% local, no cloud, no telemetry. MIT.
Cut AI token costs 95%+ on code exploration. The leading MCP server for precise, symbol-level GitHub code retrieval via tree-sitter AST. Works with Claude Code, Cursor & any MCP client. 313B+ tokens saved.
MCP Aggregator, Orchestrator, Middleware, Gateway in one docker
MCP server that enables AI assistants to interact with Google Gemini CLI, leveraging Gemini's massive token window for large file analysis and codebase understanding
Run your own MCP server? See who uses it and what to fix.
Measure it with TrackMCP