You need SSH access, a personal Matrix account, a bot Matrix account, and a server (if self-hosting via Ollama, at least 32 GB of RAM)
Sign into your bot account in Element, go to Settings → Sessions, and delete any stale sessions to avoid E2EE key conflicts.
SSH into the server and create a dedicated nanobot user with passwordless sudo and lingering enabled (so its systemd user services survive logout):
ssh root@<SERVER_IP>
useradd -m nanobot
echo "nanobot ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/nanobot
passwd -d nanobot
loginctl enable-linger nanobot
su - nanobotsudo dnf install -y nodejs npm python3-pip python3-devel cmake gcc-c++ @development-tools jq moreutilspip install nanobot-ai[matrix]
nanobot onboardYou must use a new
device_idevery time you re-register the bot.
Create a temporary login payload and authenticate against the Matrix homeserver:
cat > /tmp/login.json << 'EOF'
{
"type": "m.login.password",
"identifier": {
"type": "m.id.user",
"user": "<BOT_MATRIX_ID>"
},
"password": "<BOT_PASSWORD>",
"device_id": "<DEVICE_ID>",
"initial_device_display_name": "<DEVICE_DISPLAY_NAME>"
}
EOF
curl -s -X POST "https://matrix.org/_matrix/client/v3/login" \
-H "Content-Type: application/json" \
-d @/tmp/login.json | jq .Inject the Matrix channel config into Nanobot and clean up the login payload and any stale sync state:
rm -f /tmp/login.json && jq '.channels.matrix = {
"enabled": true,
"homeserver": "https://matrix.org",
"accessToken": "<ACCESS_TOKEN>",
"userId": "<BOT_MATRIX_ID>",
"deviceId": "<DEVICE_ID>",
"e2eeEnabled": true,
"syncStopGraceSeconds": 2,
"maxMediaBytes": 20971520,
"allowFrom": ["<YOUR_MATRIX_ID>"],
"groupPolicy": "allowlist",
"groupAllowFrom": ["<YOUR_MATRIX_ID>"],
"allowRoomMentions": false
}' ~/.nanobot/config.json | sponge ~/.nanobot/config.json
rm -f ~/.nanobot/matrix-store/<BOT_MATRIX_ID>_*.dbSet the OpenRouter API key and select a model:
jq '.providers.openrouter.apiKey = "<OPENROUTER_API_KEY>" | .agents.defaults.provider = "openrouter"' \
~/.nanobot/config.json | sponge ~/.nanobot/config.json
jq '.agents.defaults.model = "qwen/qwen3.6-plus:free"' \
~/.nanobot/config.json | sponge ~/.nanobot/config.jsonModels that work well on OpenRouter:
| Model | Self-hostable | Price |
|---|---|---|
qwen/qwen3.6-plus |
No | $0.33 / 1M tokens |
xiaomi/mimo-v2-pro |
No | $1.00 / 1M tokens |
minimax/minimax-m2.7 |
No | $0.30 / 1M tokens |
qwen/qwen3.5-27b |
Yes (qwen3.5:27b) |
$0.19 / 1M tokens |
google/gemma-4-26b-a4b-it |
Yes (gemma4:26b) |
$0.13 / 1M tokens |
Install Ollama, point Nanobot at it, and select a model:
curl -fsSL https://ollama.com/install.sh | sh
jq '.providers.ollama.apiBase = "http://localhost:11434/v1" | .agents.defaults.provider = "ollama"' \
~/.nanobot/config.json | sponge ~/.nanobot/config.json
jq '.agents.defaults.model = "gemma4:26b"' \
~/.nanobot/config.json | sponge ~/.nanobot/config.jsonStart the gateway temporarily so the device registers its keys:
nanobot gatewayFrom another terminal, query the device's public keys:
ssh root@<SERVER_IP>
curl -s -X POST "https://matrix.org/_matrix/client/v3/keys/query" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"device_keys":{"<BOT_MATRIX_ID>":["<DEVICE_ID>"]}}' \
| jq '{keys: .device_keys["<BOT_MATRIX_ID>"]["<DEVICE_ID>"].keys, sigs: .device_keys["<BOT_MATRIX_ID>"]["<DEVICE_ID>"].signatures}'Note down the ed25519 fingerprint from the output.
Next, add your bot account on your Matrix client and send "Hi" - the bot won't respond yet because the device is unverified.
Sign into your bot account in Element, open the new chat containing "Hi", and run:
/verify <DEVICE_ID> <ED25519_FINGERPRINT>
Go to Settings → Sessions and confirm the bot session is now verified.
Back in your Matrix client, send "Hi" again — the bot should now respond.
Stop the temporary nanobot gateway process, then create a persistent user service:
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/nanobot-gateway.service << 'EOF'
[Unit]
Description=Nanobot Gateway
After=network.target
[Service]
Type=simple
ExecStart=%h/.local/bin/nanobot gateway
Restart=always
RestartSec=10
Environment=PATH=%h/.local/bin:/usr/local/bin:/usr/bin:/bin
[Install]
WantedBy=default.target
EOF
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export DBUS_SESSION_BUS_ADDRESS=unix:path=$XDG_RUNTIME_DIR/bus
systemctl --user daemon-reload
systemctl --user enable --now nanobot-gatewayVerify it's running:
systemctl --user status nanobot-gateway
journalctl --user -u nanobot-gateway -f