A lightweight, POSIX shell–based Matrix bot for remote router management over the Matrix protocol. Runs natively on OpenWrt without any runtime dependencies beyond what the standard image provides.
Features:
- Remote control via Matrix chat: services, interfaces, Wi-Fi, WOL, clients, system info
- E2EE support via
matrix-commander-rsover SSH (primary transport) - HTTP fallback using the Matrix Client-Server API directly (no E2EE)
- Auto mode: E2EE listener primary, HTTP listener as fallback/parallel
- Security alerts forwarded to a dedicated admin room on unauthorized access attempts
- Managed by
procd— automatic restart on crash, logs vialogread
Compatibility: Successfully tested on OpenWrt 25.12.0 (Xiaomi Mi Router 3G).
OpenWrt Router
│
├── /etc/config/bot.conf ← single config file (credentials, room IDs, etc.)
├── /etc/init.d/matrixbot ← procd init script (start/stop/enable)
├── /etc/matrix_bot_known_hosts ← SSH host key store (created during setup)
│
└── /usr/lib/matrix/
├── matrix_bot ← main bot process (listener + command handler)
└── matrix_send ← standalone message sender (called by bot + usable directly)
Transport priority (sending messages):
matrix_send → [1] SSH → matrix-commander-rs (E2EE)
↓ on failure
[2] HTTP → Matrix Client API (plaintext)
Listening priority (receiving commands):
auto mode → listen_e2ee (SSH/matrix-commander-rs) — primary
→ listen_http (Matrix /sync polling) — fallback / parallel
Install on the router:
apk update
apk add curl jq openssh-client| Package | Purpose |
|---|---|
curl |
HTTP transport for sending/receiving Matrix API calls |
jq |
JSON parsing (fallback: built-in jsonfilter) |
openssh-client |
SSH transport to the E2EE host running matrix-commander-rs |
jsonfilteris included in OpenWrt by default and is used automatically ifjqis not installed.jqis strongly recommended for correctness with complex sync payloads.
For Wake-on-LAN support:
apk add etherwakeFor Nginx reload support — Nginx must already be installed:
apk add nginxYou need a separate machine (Linux server, VPS, Raspberry Pi, etc.) with:
matrix-commander-rsinstalled and already logged in to your Matrix account- SSH server running and accessible from the router
- A dedicated SSH key pair for the bot (see SSH Key Setup)
If you do not need E2EE, you can run the bot in --no-e2ee (HTTP-only) mode and skip the external host entirely.
Register a dedicated Matrix account for the bot on your homeserver (e.g. @mybot:matrix.org). Do not use your personal account.
Log in to the bot account and retrieve its access token:
curl -s -X POST "https://matrix.example.com/_matrix/client/v3/login" \
-H "Content-Type: application/json" \
-d '{"type":"m.login.password","user":"mybot","password":"yourpassword"}'Copy the access_token field from the response.
Create (or reuse) the following rooms in your Matrix client:
| Room | Purpose | Variable |
|---|---|---|
| Command room | Where you send commands to the bot. Can be E2EE-encrypted. | MATRIX_ROOM_E2EE_ID |
| Plaintext room | Optional secondary room without encryption. | MATRIX_ROOM_ID |
| Admin/alert room | Where the bot sends security alerts (unauthorized access). Does not accept commands. | MATRIX_ROOM_ADMIN |
Invite the bot account to all rooms. Get room IDs from your client (usually under Room Settings → Advanced) — they look like !opaque:server.tld.
Generate a dedicated ED25519 key pair on the router:
mkdir -p /etc/matrix_bot
ssh-keygen -t ed25519 -f /etc/matrix_bot/id_ed25519 -N "" -C "matrix-bot@openwrt"
chmod 600 /etc/matrix_bot/id_ed25519Copy the public key to the external E2EE host:
cat /etc/matrix_bot/id_ed25519.pub
# Append the output to ~/.ssh/authorized_keys on the remote hostRegister the remote host key (run once — uses your config values):
. /etc/config/bot.conf && \
ssh -i "$SSH_KEY" -p "$SSH_PORT" \
-o StrictHostKeyChecking=accept-new \
-o UserKnownHostsFile=/etc/matrix_bot_known_hosts \
-o BatchMode=yes \
-o ConnectTimeout=10 \
"$SSH_USER@$SSH_HOST" exit 2>&1 && \
echo "Host key saved." && cat /etc/matrix_bot_known_hostsThis stores the host key in /etc/matrix_bot_known_hosts. The bot uses StrictHostKeyChecking=yes against this file — MITM attacks will be rejected.
mkdir -p /usr/lib/matrixcurl -sSL "https://raw.githubusercontent.com/webstudiobond/matrix-bot-openwrt/refs/heads/v1.0.0/usr/lib/matrix/matrix_bot" -o /usr/lib/matrix/matrix_bot
curl -sSL "https://raw.githubusercontent.com/webstudiobond/matrix-bot-openwrt/refs/heads/v1.0.0/usr/lib/matrix/matrix_send" -o /usr/lib/matrix/matrix_send
curl -sSL "https://raw.githubusercontent.com/webstudiobond/matrix-bot-openwrt/refs/heads/v1.0.0/etc/init.d/matrixbot" -o /etc/init.d/matrixbotchmod 700 /usr/lib/matrix/matrix_bot
chmod 700 /usr/lib/matrix/matrix_send
chmod 755 /etc/init.d/matrixbotThe scripts must be owned by root and not writable by anyone else — they execute as root and handle credentials.
touch /etc/config/bot.conf
chmod 600 /etc/config/bot.confEdit /etc/config/bot.conf — see Configuration below.
/etc/init.d/matrixbot enable
/etc/init.d/matrixbot startCheck logs:
logread -e matrixAll settings live in /etc/config/bot.conf. This file is sourced as a shell script.
# /etc/config/bot.conf
# Permissions must be 600 (chmod 600 /etc/config/bot.conf)
# =====================
# Matrix
# =====================
# Base URL of your Matrix homeserver (no trailing slash)
MATRIX_URL='https://matrix.example.com'
# Room IDs — get from your Matrix client (Room Settings → Advanced)
# Primary room: E2EE-encrypted, used for sending commands
MATRIX_ROOM_E2EE_ID='!AbCdEfGhIj:matrix.example.com'
# Secondary room: plaintext (optional, can be same as above if no E2EE needed)
MATRIX_ROOM_ID='!KlMnOpQrSt:matrix.example.com'
# Admin/alert room: bot sends security warnings here. NOT a command room.
MATRIX_ROOM_ADMIN='!UvWxYzAbCd:matrix.example.com'
# Full Matrix user ID of the bot account (must be invited to all rooms above)
MATRIX_BOT_USER='@mybot:matrix.example.com'
# Full Matrix user ID of the admin (the only user whose commands are accepted)
MATRIX_ADMIN_USER='@me:matrix.example.com'
# Access token of the bot account (from login response)
MATRIX_ACCESS_TOKEN='syt_XXXXXXXXXXXXXXXXXXXXXXXX'
# =====================
# SSH (E2EE transport)
# =====================
# Leave blank to disable E2EE and use HTTP-only mode.
# Hostname or IP of the machine running matrix-commander-rs
SSH_HOST='192.168.1.100'
SSH_PORT='22'
SSH_USER='myuser'
# Path to the private key generated during setup
SSH_KEY='/etc/matrix_bot/id_ed25519'
# =====================
# Wake-on-LAN
# =====================
# MAC address of a specific machine to wake with the 'wol_pc' command (optional)
MAC_PC='AA:BB:CC:DD:EE:FF'
# =====================
# Wi-Fi display
# =====================
# Set to 1 for detailed Wi-Fi info (hardware, BSSID, signal, TX power, etc.)
# Set to 0 or leave empty for simple mode (SSID, channel, rate, key)
WIFI_DETAILED='0'
# =====================
# Service control
# =====================
# Space-separated whitelist of services allowed to be restarted via 'restart' command.
# Only services in this list AND present in /etc/init.d/ can be restarted.
# Default (used if this variable is absent): dnsmasq firewall network odhcpd cron uhttpd
SVC_WANTED='dnsmasq firewall network odhcpd cron uhttpd nginx'Security note:
/etc/config/bot.confcontains your Matrix access token and SSH key path. It must be readable only by root (chmod 600). Never commit this file to version control.
Once the bot is running and invited to your rooms, send commands as MATRIX_ADMIN_USER in the command room.
Send help or start to get the full command list from the bot itself.
| Command | Description |
|---|---|
uptime |
Router uptime and load average |
memory |
RAM usage in MB (total / used / free) |
meminfo |
Detailed /proc/meminfo (first 5 entries) |
wan_ip |
Public WAN IP address (queries external services with local fallback) |
| Command | Description |
|---|---|
clients |
Full network report: all Wi-Fi + wired clients |
wifi_clients |
Wi-Fi associated clients (IP, IPv6, MAC, signal, hostname) |
wired_clients |
Wired LAN clients from ARP table (excluding Wi-Fi MACs) |
| Command | Description |
|---|---|
restart <service> |
Restart a service from the whitelist (e.g. restart dnsmasq) |
reload nginx |
Test Nginx config, then reload if valid |
Only services listed in SVC_WANTED (or the default list) and present in /etc/init.d/ can be restarted.
| Command | Description |
|---|---|
ifup <iface> |
Bring up a UCI-defined network interface (e.g. ifup wan) |
ifdown <iface> |
Bring down a UCI-defined network interface |
| Command | Description |
|---|---|
wifi / wifi_info |
Wi-Fi status (SSID, encryption, channel, rate, key) |
wifi_up_2_4 |
Enable 2.4 GHz radio (radio0) |
wifi_down_2_4 |
Disable 2.4 GHz radio |
wifi_reload_2_4 |
Reload 2.4 GHz radio config |
wifi_up_5 |
Enable 5 GHz radio (radio1) |
wifi_down_5 |
Disable 5 GHz radio |
wifi_reload_5 |
Reload 5 GHz radio config |
Set WIFI_DETAILED='1' in config for extended output (hardware chip, BSSID, country, TX power, signal/noise, OCV, client count).
| Command | Description |
|---|---|
wol <MAC> |
Send WOL magic packet to any MAC (format: AA:BB:CC:DD:EE:FF) |
wol_pc |
Send WOL magic packet to MAC_PC from config |
Requires etherwake package. The LAN interface is detected automatically from UCI (network.lan.device), falling back to br-lan.
The bot can be started in three modes. The procd init script uses --e2ee by default.
| Flag | Mode | Description |
|---|---|---|
| (none / auto) | auto |
E2EE listener (SSH) as primary, HTTP /sync polling as parallel fallback |
--e2ee |
E2EE only | SSH + matrix-commander-rs listener only. No HTTP polling. |
--no-e2ee |
HTTP only | Matrix /sync long-polling only. No SSH. No E2EE. |
To change the mode, edit /etc/init.d/matrixbot and modify the procd_set_param command line:
# E2EE only (default):
procd_set_param command /bin/sh "$SCRIPT" --e2ee
# HTTP only (no external host needed):
procd_set_param command /bin/sh "$SCRIPT" --no-e2eeThen restart the service:
/etc/init.d/matrixbot restartStop the procd service first to avoid conflicts:
/etc/init.d/matrixbot stopRun in foreground with debug output:
/usr/lib/matrix/matrix_bot -d --no-e2eeFlags can be combined:
/usr/lib/matrix/matrix_bot -d --e2ee
/usr/lib/matrix/matrix_bot -d # auto mode with debugSend a test message directly:
/usr/lib/matrix/matrix_send --room-id '!RoomID:server.tld' 'Hello from router'With debug output:
/usr/lib/matrix/matrix_send -d --room-id '!RoomID:server.tld' 'Test message'Force HTTP transport (skip SSH attempt):
/usr/lib/matrix/matrix_send --no-e2ee --room-id '!RoomID:server.tld' 'HTTP only test'View live logs:
logread -f -e matrix- Single admin: only
MATRIX_ADMIN_USERcan issue commands. Any other sender triggers an alert toMATRIX_ROOM_ADMIN. - Room whitelist: events from any room not listed in
MATRIX_ROOM_E2EE_ID/MATRIX_ROOM_IDare silently dropped. - Service whitelist: only services in
SVC_WANTEDcan be restarted. Service names are validated against[a-zA-Z0-9_-]before use. - Input sanitization: all command arguments are stripped to a safe character set before use. Shell metacharacters are blocked.
- SSH host verification:
StrictHostKeyChecking=yeswith a dedicatedknown_hostsfile. Run the host registration step before first use. - Credentials not exposed in process list: the Matrix access token is written to a
chmod 600temporary file and passed tocurl -Krather than on the command line. - E2EE context awareness: the bot tracks which rooms have encryption enabled. In HTTP mode, encrypted messages arrive as opaque
m.room.encryptedevents — the bot detects this and warns rather than silently failing.
| Path | Owner | Mode | Notes |
|---|---|---|---|
/usr/lib/matrix/matrix_bot |
root | 700 |
Main bot process |
/usr/lib/matrix/matrix_send |
root | 700 |
Message sender |
/etc/init.d/matrixbot |
root | 755 |
procd init script |
/etc/config/bot.conf |
root | 600 |
Credentials — never world-readable |
/etc/matrix_bot/id_ed25519 |
root | 600 |
SSH private key |
/etc/matrix_bot_known_hosts |
root | 600 |
SSH host key store |
Bot does not respond to commands
- Confirm the bot account is invited to and has joined the room.
- Confirm
MATRIX_ADMIN_USERexactly matches the full Matrix user ID (including@and:server). - Check
logread -e matrixfor authentication or network errors.
Host key verification failed
- The SSH host key has not been registered. Run the SSH Key Setup registration command.
Could not determine WAN IP
- All external IP services timed out and
ifstatus wanreturned no address. Check WAN connectivity on the router.
Service restart returns Access Denied
- The service is not in
SVC_WANTED. Add it to the config and restart the bot.
E2EE messages not received
- The SSH connection to the
matrix-commander-rshost failed. Run the bot with-dflag and check theRUST LOG:lines for the reason. - Verify
matrix-commander-rsis running and logged in on the remote host.
No wireless interfaces found
iwinforeturned no results. Ensurekmod-mac80211and the appropriate driver are installed, and that Wi-Fi is enabled.