Traul is a personal intelligence engine that syncs messages from Slack, Telegram, Discord, Linear, Gmail, WhatsApp, Claude Code sessions, and local Markdown files into a single searchable SQLite database.
For AI agents: If you are an AI assistant setting up Traul for a user, follow this doc step by step. Present the list of available connectors (Section 4), let the user choose which ones they want, then walk through each selected connector one at a time. After setup, start the daemon and verify with a search query.
Traul uses Bun as its runtime and package manager.
curl -fsSL https://bun.sh/install | bashRestart your terminal, then verify:
bun --version # should be 1.0+Apple's bundled SQLite does not support loadable extensions. Traul needs Homebrew's build for the sqlite-vec vector extension.
brew install sqliteTraul automatically loads the library from /opt/homebrew/opt/sqlite/lib/libsqlite3.dylib — no PATH changes needed.
Ollama runs embedding models locally. Without it, you can still use keyword search (--fts).
brew install ollama
ollama pull snowflake-arctic-embed2
ollama serveKeep ollama serve running in a background terminal or configure it as a launch agent.
Only needed if you want to sync Telegram messages.
python3 --version # macOS ships with Python 3
pip3 install telethonYou will also need a Telegram API app — see the Telegram section below.
git clone <repo-url> && cd traul
bun install
bun linkbun link registers traul as a global CLI command. Verify:
traul --helpCreate ~/.config/traul/config.json with your desired settings. You can start with an empty object and add connector config as you go:
{}All connector credentials can be set via environment variables (shown in each section below) or in this config file. Environment variables take precedence over config file values.
Each connector syncs independently. Pick the ones you want and set them up one at a time.
| Connector | Difficulty | What you need |
|---|---|---|
| Claude Code | Zero config | Nothing — reads ~/.claude/projects/ |
| Markdown | Minimal | List of directories in config |
| Linear | Easy | API key |
| Discord | Easy | Bot token |
| Slack | Moderate | Token (bot or user) |
| Telegram | Moderate | API ID/hash + one-time auth |
| Gmail | Moderate | OAuth2 credentials |
| Advanced | WAHA server + QR auth |
Reads session files from ~/.claude/projects/ — nothing to configure.
traul sync claudecodeAdd a list of directories to your config file:
{
"markdown": {
"dirs": ["~/notes", "~/docs"]
}
}traul sync markdownGet an API key from Linear Settings > API.
export LINEAR_API_KEY="lin_api_..."For multiple workspaces, use suffixed variables:
export LINEAR_API_KEY_WORK="lin_api_..."
export LINEAR_API_KEY_PERSONAL="lin_api_..."traul sync linearCreate a Discord bot at discord.com/developers/applications:
- Create a New Application
- Go to Bot > Reset Token — copy the token
- Under Privileged Gateway Intents, enable Message Content Intent
- Go to OAuth2 > URL Generator, select
botscope withRead Message Historypermission - Open the generated URL to invite the bot to your server
export DISCORD_TOKEN="your-bot-token"Optional — filter by server or channel in config:
{
"discord": {
"servers": {
"allowlist": ["server_id_1"]
},
"channels": {
"stoplist": ["channel_id_to_skip"]
}
}
}traul sync discordYou need a Slack token. Two approaches:
Bot token (xoxb) — create a Slack app at api.slack.com/apps, add OAuth scopes (channels:history, channels:read, users:read), install to workspace, copy the Bot User OAuth Token.
User token (xoxc) — extract from your browser's Slack session. Open Slack in a browser, find the xoxc- token and d= cookie value in DevTools (Application > Cookies and Network requests). This gives access to all channels you can see.
export SLACK_TOKEN="xoxb-..."
# or for xoxc tokens:
export SLACK_TOKEN="xoxc-..."
export SLACK_COOKIE="xoxd=..."For multiple workspaces, use suffixed variables:
export SLACK_TOKEN_MYTEAM="xoxb-..."
export SLACK_TOKEN_WORK="xoxc-..."
export SLACK_COOKIE_WORK="xoxd=..."traul sync slack-
Go to my.telegram.org, log in, and create an app under "API development tools". Note the
api_idandapi_hash. -
Set environment variables:
export TELEGRAM_API_ID="12345678"
export TELEGRAM_API_HASH="abc123..."- Run the one-time interactive auth (enters your phone number, sends a code):
python3 scripts/tg_sync.py setupThis creates a session file at ~/.config/telegram-telethon/session. You only need to do this once.
- Sync:
traul sync telegramGmail requires OAuth2 credentials. You need a Google Cloud project with the Gmail API enabled.
- Go to Google Cloud Console
- Create a project (or use an existing one)
- Enable the Gmail API under APIs & Services > Library
- Create OAuth2 credentials under APIs & Services > Credentials > Create Credentials > OAuth client ID
- Set application type to "Desktop app"
- Note the
client_idandclient_secret - Obtain a refresh token by completing the OAuth2 flow (use the Google OAuth Playground or a local script)
export GMAIL_CLIENT_ID="your-client-id"
export GMAIL_CLIENT_SECRET="your-client-secret"
export GMAIL_REFRESH_TOKEN="your-refresh-token"Or as a single JSON variable:
export GMAIL_CREDS_JSON='{"client_id":"...","client_secret":"...","refresh_token":"..."}'For multiple accounts, use config:
{
"gmail": {
"accounts": [
{
"name": "personal",
"client_id": "...",
"client_secret": "...",
"refresh_token": "...",
"labels": ["INBOX"]
}
]
}
}traul sync gmailWhatsApp sync uses WAHA (WhatsApp HTTP API), a self-hosted service that bridges WhatsApp Web.
- Start a WAHA instance:
docker compose -f docker-compose.waha.yml up -d- Add the instance to your config:
{
"whatsapp": {
"instances": [
{
"name": "personal",
"url": "http://localhost:3000",
"api_key": "your-waha-api-key",
"session": "default"
}
]
}
}- Authenticate by scanning a QR code:
traul whatsapp auth personalThis displays a QR code in your terminal. Scan it with WhatsApp on your phone within 2 minutes.
- Optional — filter to specific chats:
{
"whatsapp": {
"instances": [
{
"name": "personal",
"url": "http://localhost:3000",
"api_key": "your-waha-api-key",
"session": "default",
"chats": ["chat_id_1", "chat_id_2"]
}
]
}
}traul sync whatsappInstead of running traul sync manually, start the daemon for continuous background sync:
traul daemon start --detachThis runs the daemon in the background, syncing all configured connectors at regular intervals and generating embeddings automatically. Logs are written to ~/.local/share/traul/daemon.log.
Check daemon status:
traul daemon statusStop the daemon:
traul daemon stopTo start the daemon automatically when you log in, create a launchd plist:
cat > ~/Library/LaunchAgents/com.traul.daemon.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.traul.daemon</string>
<key>ProgramArguments</key>
<array>
<string>/Users/YOUR_USERNAME/.bun/bin/traul</string>
<string>daemon</string>
<string>start</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/YOUR_USERNAME/.local/share/traul/daemon-launchd.log</string>
<key>StandardErrorPath</key>
<string>/Users/YOUR_USERNAME/.local/share/traul/daemon-launchd.log</string>
</dict>
</plist>
EOFReplace YOUR_USERNAME with your macOS username, then load it:
launchctl load ~/Library/LaunchAgents/com.traul.daemon.plistTo disable auto-start:
launchctl unload ~/Library/LaunchAgents/com.traul.daemon.plistAfter your first sync, verify everything is working:
traul statsThis shows the number of messages synced per source.
Run a search to confirm:
traul search "hello" --ftsIf you have Ollama running and have generated embeddings (traul embed), you can use hybrid search:
traul search "recent discussions about deployments"Browse your synced channels:
traul channels
traul messages general --limit 20Traul stores everything in a single SQLite file:
~/.local/share/traul/traul.db
Override with TRAUL_DB_PATH:
export TRAUL_DB_PATH="~/my-data/traul.db""unable to load extension" or SQLite errors
Homebrew SQLite is not installed. Run brew install sqlite and try again.
"connect ECONNREFUSED" on search
Ollama is not running. Start it with ollama serve, or use --fts for keyword-only search.
Telegram "session expired" or auth errors
Re-run python3 scripts/tg_sync.py setup to create a fresh session.
Slack "not_authed" or 403 Your token has expired. For xoxc tokens, re-extract the token and cookie from your browser — they rotate periodically.
"model not found" on embed/search
Pull the model first: ollama pull snowflake-arctic-embed2. If you use a different model, set TRAUL_EMBED_MODEL.
No search results with hybrid mode
Embeddings may not be generated yet. Run traul embed first, or use --fts for keyword search.
Daemon won't start or port conflict
Check if another daemon is running: traul daemon status. The default port is 3847 — change it in config under daemon.port if needed.
WhatsApp QR code expired
Run traul whatsapp auth <account> again — you have 2 minutes to scan before it expires.