⚠️ Disclaimer: This software has not been security audited. It handles Discord bot tokens and interacts with the Discord API. Use at your own risk. Review the code before deploying in any environment where security is a concern. Do not exposeaudit-config.jsonor any file containing your bot token.
Stream every Clawdbot action — incoming messages, assistant reasoning, tool calls, and outgoing replies — into a dedicated Discord audit channel in real time.
Three scripts work together to give you full visibility into what your Clawdbot agent is doing, across all sessions, without touching the main conversation flow.
┌─────────────────────────────────────────────────────────────────────┐
│ Clawdbot Runtime │
│ │
│ session-abc123.jsonl ──┐ │
│ session-def456.jsonl ──┼── .jsonl session files (append-only) │
│ session-ghi789.jsonl ──┘ │
└───────────────────────────┬─────────────────────────────────────────┘
│
┌─────────────▼──────────────┐
│ audit-log-pusher.sh │
│ (main orchestrator) │
│ │
│ tail -F *.jsonl │
│ │ │
│ ▼ │
│ format-log.py │
│ (per-line formatting) │
└─────────────┬──────────────┘
│
Formatted messages
│
▼
┌─────────────────────────────┐
│ Discord Audit Channel │ ◄──── #audit
│ (all activity lands │
│ here) │
└─────────────────────────────┘
▲
│
Forwarded bot replies
│
┌─────────────┴──────────────┐
│ forward-outgoing.py │
│ (polls watched channels │
│ for bot messages) │
│ │
│ channel #general ────┐ │
│ channel #dev ────────┤ │
│ channel #help ───────┘ │
└─────────────────────────────┘
-
Session events — Clawdbot writes every interaction to
.jsonlsession files.audit-log-pusher.shtails all of them simultaneously and pipes each line throughformat-log.py, which formats it and posts to the audit channel via the Discord API. -
Outgoing replies — Bot replies in watched Discord channels are picked up by
forward-outgoing.py, which polls those channels every N seconds and uses Discord's native message forwarding (message_referencetype 1) to mirror them into the audit channel.
| Script | Language | Role |
|---|---|---|
audit-log-pusher.sh |
Bash | Main orchestrator. Tails all .jsonl session files, pipes through the formatter, and spawns the outgoing forwarder as a background process. |
format-log.py |
Python 3 | Formats raw session JSON events into compact, icon-coded Discord messages. |
forward-outgoing.py |
Python 3 | Polls watched Discord channels for bot-authored messages and forwards them to the audit channel. Maintains a state file to avoid duplicates. |
| Event Type | Icon | Example Output |
|---|---|---|
| Incoming Discord message | (native forward) | Forwarded via message_reference type 1 |
| Assistant thinking | 💭 | 💭 Let me check the configuration files first… |
| File read | 📂 | 📂 READ → /home/debian/clawd/config.json |
| File write | 💾 | 💾 WRITE → /tmp/output.txt |
| File edit | ✏️ | ✏️ EDIT → /home/debian/clawd/scripts/main.py |
| Shell command | ⚡ | ⚡ $ git status |
| Web search | 🔎 | 🔎 SEARCH → python requests timeout |
| Web fetch | 🌐 | 🌐 FETCH → docs.python.org |
| Message action | 💬 | 💬 MSG.SEND |
| Browser action | 🖥️ | 🖥️ BROWSER.snapshot |
| Memory operation | 🧠 | 🧠 MEMORY_SEARCH |
| Session operation | 📡 | 📡 sessions_list |
| Other tool call | 🔧 | 🔧 cron |
| Tool result (success) | ✓ | ✓ done or ✓ (42 lines) |
| Tool result (error) | ❌ | ❌ FileNotFoundError: No such file |
| Text output | 💬 | 💬 Here's what I found… |
- Python 3.6+ with the
requestslibrary - curl (used by
format-log.pyfor Discord API calls) - jq (used by
audit-log-pusher.shto parse config) - Clawdbot installed and running (session
.jsonlfiles must exist) - A Discord bot token with permissions to read messages and send messages in the audit channel and any watched channels
pip3 install requestsAll three scripts read from a shared audit-config.json file located in the same directory as the scripts.
cp audit-config.sample.json audit-config.json| Field | Type | Description |
|---|---|---|
discord_token |
string | Your Discord bot token (with Bot prefix handled automatically). |
bot_user_id |
string | The bot's Discord user ID. Used by the outgoing forwarder to identify which messages are from the bot. |
audit_channel_id |
string | The Discord channel ID where all audit messages will be posted. |
sessions_dir |
string | Path to Clawdbot's session files directory. Default: /home/debian/.clawdbot/agents/main/sessions |
watch_channels |
string[] | Array of Discord channel IDs to poll for outgoing bot messages. |
state_file |
string | Path to the JSON state file that tracks which messages have already been forwarded. Default: /home/debian/.clawdbot/audit-forwarded.json |
poll_interval |
number | Seconds between polling cycles for the outgoing forwarder. Default: 3 |
{
"discord_token": "YOUR_DISCORD_BOT_TOKEN",
"bot_user_id": "YOUR_BOT_USER_ID",
"audit_channel_id": "DISCORD_CHANNEL_ID_FOR_AUDIT_LOGS",
"sessions_dir": "/home/debian/.clawdbot/agents/main/sessions",
"watch_channels": ["CHANNEL_ID_1", "CHANNEL_ID_2"],
"state_file": "/home/debian/.clawdbot/audit-forwarded.json",
"poll_interval": 3
}-
Clone or copy the scripts into a directory:
mkdir -p ~/clawd/scripts cp audit-log-pusher.sh format-log.py forward-outgoing.py ~/clawd/scripts/ cp audit-config.sample.json ~/clawd/scripts/audit-config.json
-
Make the orchestrator executable:
chmod +x ~/clawd/scripts/audit-log-pusher.sh -
Edit the config:
nano ~/clawd/scripts/audit-config.jsonFill in your Discord bot token, bot user ID, audit channel ID, and any channels you want to watch.
-
Verify prerequisites:
python3 -c "import requests; print('requests OK')" which curl jq -
Ensure session files exist:
ls /home/debian/.clawdbot/agents/main/sessions/*.jsonl
cd ~/clawd/scripts
./audit-log-pusher.shThe script will:
- Start
forward-outgoing.pyin the background - Tail all
.jsonlfiles in the sessions directory - Pipe each new event through
format-log.py - Post formatted messages to the audit channel
Press Ctrl+C to stop (both the tail and the outgoing forwarder will be cleaned up).
nohup ~/clawd/scripts/audit-log-pusher.sh >> /var/log/audit-pusher.log 2>&1 &The tool ships with a clawdbot-audit.service unit file for running as a user-level systemd service (no sudo required).
-
Create the user systemd directory (if it doesn't exist):
mkdir -p ~/.config/systemd/user -
Copy the service file:
cp clawdbot-audit.service ~/.config/systemd/user/ -
Edit paths if needed — the unit file uses
%h(home directory) by default, so it resolves to~/clawd/scripts/. If your scripts live elsewhere, update the paths:nano ~/.config/systemd/user/clawdbot-audit.serviceThe default unit file:
[Unit] Description=Clawdbot Audit Log Pusher After=network-online.target Wants=network-online.target [Service] Type=simple WorkingDirectory=%h/clawd/scripts ExecStart=%h/clawd/scripts/audit-log-pusher.sh Restart=on-failure RestartSec=5 StandardOutput=journal StandardError=journal [Install] WantedBy=default.target
-
Enable and start:
systemctl --user daemon-reload systemctl --user enable clawdbot-audit systemctl --user start clawdbot-audit -
Check status and logs:
systemctl --user status clawdbot-audit journalctl --user -u clawdbot-audit -f
Tip: User-level services only run while you're logged in, unless you enable lingering:
loginctl enable-linger $USER. With lingering enabled, the service starts at boot and persists after logout — ideal for a headless server.
Here's what the audit channel looks like during a typical interaction:
┌──────────────────────────────────────────────────────┐
│ #audit │
├──────────────────────────────────────────────────────┤
│ │
│ 📨 [Forwarded message from #general] │
│ User: "Can you check if nginx is running?" │
│ │
│ 💭 The user wants me to check the nginx service │
│ status. I'll run systemctl… │
│ │
│ ⚡ $ systemctl status nginx │
│ ✓ `● nginx.service - A high performance…` (12 lines) │
│ │
│ 📂 READ → /etc/nginx/nginx.conf │
│ ✓ `worker_processes auto;` (48 lines) │
│ │
│ 💬 Nginx is running. The config shows 4 worker… │
│ │
│ 📨 [Forwarded message from #general] │
│ Bot: "✅ Nginx is running with 4 worker processes…" │
│ │
└──────────────────────────────────────────────────────┘
Incoming user messages are forwarded natively (Discord's forward feature, preserving the original message context). Bot replies are forwarded from the watched channels by the outgoing forwarder. Everything in between — thinking, tool calls, results — is formatted inline with contextual icons.
- Check the bot token — Ensure
discord_tokeninaudit-config.jsonis valid and the bot hasSend Messagespermission in the audit channel. - Check channel ID — Verify
audit_channel_idis correct (enable Developer Mode in Discord → right-click channel → Copy ID). - Check session files exist — Run
ls /home/debian/.clawdbot/agents/main/sessions/*.jsonlto confirm there are active session files. - Check for errors — Run
audit-log-pusher.shin the foreground and look for error messages on stderr.
- Check
watch_channels— Make sure the channel IDs in the array match the channels where the bot posts. - Check
bot_user_id— The forwarder filters messages by author ID; if this is wrong, no messages will match. - Check
requestsis installed — Runpython3 -c "import requests". - Check the state file — If the state file (
audit-forwarded.json) has grown stale, delete it and restart.
- The state file (
audit-forwarded.json) tracks forwarded message IDs. If you delete it, recently forwarded messages may be re-forwarded on the next poll cycle. - The state file is automatically pruned to the last 500 entries to prevent unbounded growth.
- Increase
poll_intervalin the config (default is 3 seconds). - Reduce the number of channels in
watch_channels. - Discord rate limits are handled silently; messages may be delayed but won't be lost from the session log side (they're posted via individual
curlcalls).
sudo apt install jq-
The formatter silently drops malformed JSON lines and events it doesn't recognize. Check that session files contain valid JSONL by running:
tail -1 /home/debian/.clawdbot/agents/main/sessions/*.jsonl | python3 -m json.tool
scripts/
├── audit-log-pusher.sh # Main orchestrator (bash)
├── format-log.py # Event formatter (python)
├── forward-outgoing.py # Outgoing message forwarder (python)
├── clawdbot-audit.service # systemd user service unit file
├── audit-config.json # Your config (gitignored)
├── audit-config.sample.json # Config template
└── AUDIT-README.md # This file
MIT License
Copyright (c) 2025
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.