Installation Guide
July 12, 2026 · View on GitHub
Requirements
- Python 3.11+
pipx(recommended) orpip- At least one authenticated provider CLI:
- Claude Code CLI:
npm install -g @anthropic-ai/claude-code && claude auth - Codex CLI:
npm install -g @openai/codex && codex auth - Gemini CLI:
npm install -g @google/gemini-cliand authenticate ingemini
- Claude Code CLI:
- One of these messaging transports:
- Telegram: Bot token from @BotFather + user ID from @userinfobot
- Matrix: install Matrix support first (
ductor install matrixorpip install \"ductor[matrix]\"), then provide homeserver URL, user ID, and password/access token - Slack: install Slack support first (
pip install "ductor[slack]"), then create a Slack app with Socket Mode, the bot/app scopes below, and provide bot/app tokens plus Slack member/channel IDs for the allowlist
- Docker optional (recommended for sandboxing)
Install
pipx (recommended)
pipx install ductor
pip
pip install ductor
from source
git clone https://github.com/PleasePrompto/ductor.git
cd ductor
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
First run
ductor
On first run, onboarding does:
- checks Claude/Codex/Gemini/Antigravity auth status,
- asks which transport to use (Telegram, Matrix, or Slack),
- collects transport credentials,
- asks timezone,
- offers Docker sandboxing (with optional AI/ML package selection),
- offers service install,
- writes config and seeds
~/.ductor/.
Multiple transports can run in parallel (e.g. Telegram + Slack
simultaneously). After initial setup, configure the transports array
in config.json. See config.md for details.
If service install succeeds, onboarding returns without starting foreground bot.
Slack setup
ductor's Slack transport follows the same modern pattern Hermes uses: Slack Bolt + Socket Mode. That means no public webhook URL is needed.
1. Install the Slack extra
pip install "ductor[slack]"
2. Create a Slack app
- Go to https://api.slack.com/apps
- Click Create New App
- Choose From scratch
- Pick a name and workspace
3. Add bot token scopes
In OAuth & Permissions → Scopes → Bot Token Scopes, add:
| Scope | Required | Purpose |
|---|---|---|
chat:write | yes | send bot replies |
app_mentions:read | yes | detect @bot in channels |
channels:history | yes | read public-channel messages and thread history |
channels:read | yes | resolve public channel metadata |
groups:history | recommended | read private-channel messages and thread history |
im:history | yes | read DMs |
im:read | yes | access DM metadata |
im:write | yes | open/manage DMs |
users:read | yes | resolve Slack user names |
files:read | yes | download attached files |
files:write | yes | upload files back to Slack |
groups:read | optional | resolve private-channel metadata |
Without channels:history / message.channels, the bot will work in DMs but not in public channels. Without groups:history / message.groups, it will not work in private channels.
4. Enable Socket Mode
In Settings → Socket Mode:
- Turn Socket Mode on
- Create an app-level token
- Grant it the
connections:writescope - Copy the resulting
xapp-...token
This token goes into slack.app_token.
5. Subscribe to Slack events
In Event Subscriptions → Subscribe to bot events, add:
| Event | Required | Purpose |
|---|---|---|
message.im | yes | direct messages |
message.channels | yes | public-channel messages |
message.groups | recommended | private-channel messages |
app_mention | yes | mention handling in channels |
6. Enable direct messages
In App Home:
- Turn on Messages Tab
- Enable Allow users to send Slash commands and messages from the messages tab
Without this, users cannot DM the bot even if the tokens and scopes are correct.
ductor does not register native Slack slash commands. Instead, its command keywords work in Slack as normal messages (for example help, status, or model) and also accept a leading /.
7. Install or reinstall the app to the workspace
In Install App, click Install to Workspace and authorize the app. Copy the Bot User OAuth Token (xoxb-...) into slack.bot_token.
If you change scopes or event subscriptions later, reinstall the app so Slack applies the new permissions.
8. Collect Slack IDs for the allowlist
- User IDs (
U...) go intoslack.allowed_users - Channel IDs (
C.../G...) go intoslack.allowed_channels
You can get them from Slack's profile/channel details UI.
9. Configure ductor
{
"transport": "slack",
"group_mention_only": true,
"slack": {
"bot_token": "xoxb-your-slack-bot-token",
"app_token": "xapp-your-slack-app-token",
"allowed_channels": ["C0123456789"],
"allowed_users": ["U0123456789"]
}
}
Then invite the app into each target channel:
/invite @your-bot-name
Behavior summary:
- DMs: the bot responds to every allowed user message
- Channels: with
group_mention_only=true, a channel conversation starts from a top-level@botmention or an@botinside an existing thread - Activated threads: once a thread is activated, follow-up replies in that thread continue the same session without another mention
Platform notes
Linux
sudo apt update && sudo apt install python3 python3-pip python3-venv nodejs npm
pip install pipx
pipx ensurepath
pipx install ductor
ductor
Optional Docker:
sudo apt install docker.io
sudo usermod -aG docker $USER
macOS
brew install python@3.11 node pipx
pipx ensurepath
pipx install ductor
ductor
Windows (native)
winget install Python.Python.3.11
winget install OpenJS.NodeJS
pip install pipx
pipx ensurepath
pipx install ductor
ductor
Native Windows is fully supported, including service management via Task Scheduler.
Windows (WSL)
WSL works too. Install like Linux inside WSL.
sudo apt update && sudo apt install python3 python3-pip python3-venv nodejs npm
pip install pipx
pipx ensurepath
pipx install ductor
ductor
Docker sandboxing
Enable in config:
{
"docker": {
"enabled": true
}
}
Notes:
- Docker image is built on first use when missing.
- Container is reused between calls.
- On Linux, ductor maps UID/GID to avoid root-owned files.
- If Docker setup fails at startup, ductor logs warning and falls back to host execution.
Docker CLI shortcuts:
ductor docker enable
ductor docker disable
ductor docker rebuild
ductor docker mount /path/to/project
ductor docker unmount /path/to/project
ductor docker mounts
ductor docker extras
ductor docker extras-add <id>
ductor docker extras-remove <id>
enable/disabletogglesdocker.enabledinconfig.json(restart bot afterwards).rebuildstops the bot, removes container + image, and forces fresh build on next start.mount/unmountmanagedocker.mountsentries.- mounts are available in-container under
/mnt/<name>(basename-based mapping with collision suffixes). - run
ductor docker mountsto inspect effective mapping and broken paths. extraslists all optional packages with their selection status.extras-add/extras-removemanage optional AI/ML packages (Whisper, PyTorch, OpenCV, etc.) inconfig.json. Transitive dependencies are resolved automatically.- after changing extras, run
ductor docker rebuildto apply. Build output is streamed live to the terminal.
Direct API server (optional)
Preferred enable path:
ductor api enable
This writes/updates the api block in config.json and generates a token if missing.
ductor api enable requires PyNaCl (used for E2E encryption against the direct API). PyNaCl is only needed when the direct WebSocket API is enabled — the core bot, Telegram, and Matrix transports run without it. If it is missing:
# pipx install
pipx inject ductor PyNaCl
# pip install
pip install "ductor[api]"
Manual config equivalent:
{
"api": {
"enabled": true,
"host": "0.0.0.0",
"port": 8741,
"token": "",
"chat_id": 0,
"allow_public": false
}
}
Notes:
- token is generated and persisted by
ductor api enable(runtime also generates it on API start if still empty). - WebSocket auth frame must include
type="auth",token, ande2e_pk(client ephemeral public key). - endpoints:
- WebSocket:
ws://<host>:8741/ws - health:
GET /health - file download:
GET /files?path=...(Bearer token) - file upload:
POST /upload(Bearer token, multipart)
- WebSocket:
- default API session uses
api.chat_idby truthiness (0falls back), else firstallowed_user_idsentry (fallback1); clients can overridechat_idin auth payload. - recommended deployment is a private network (for example Tailscale).
Background service
Install:
ductor service install
Manage:
ductor service status
ductor service start
ductor service stop
ductor service logs
ductor service uninstall
Backend details and platform quirks: Service Management
Backends:
- Linux:
systemd --userservice~/.config/systemd/user/ductor.service - macOS: Launch Agent
~/Library/LaunchAgents/dev.ductor.plist - Windows: Task Scheduler task
ductor
Linux note:
- user services survive logout/start on boot only when user linger is enabled (
sudo loginctl enable-linger <user>). Installer attempts this and prints a hint when it cannot set linger.
Windows note:
- service install prefers
pythonw.exe -m ductor_bot(no visible console window), - installed Task Scheduler service uses logon trigger + restart-on-failure retries,
- some systems require elevated terminal permissions for Task Scheduler operations.
Log command behavior:
- Linux: live
journalctl --user -u ductor -f - macOS/Windows: recent lines from
~/.ductor/logs/agent.log(fallback newest*.log)
VPS notes
Small Linux VPS is enough. Typical path:
ssh user@host
sudo apt update && sudo apt install python3 python3-pip python3-venv nodejs npm docker.io
pip install pipx
pipx ensurepath
pipx install ductor
ductor
Security basics:
- keep SSH key-only auth
- enable Docker sandboxing for unattended automation
- keep
allowed_user_idsrestricted - use
/upgradeorpipx upgrade ductor
Troubleshooting
Bot not responding
- check transport credentials (
telegram_token/matrixblock) + allowlists - run
ductor status - inspect
~/.ductor/logs/agent.log - run
/diagnosein chat
CLI installed but not authenticated
Authenticate at least one provider and restart:
claude auth
# or
codex auth
# or
# authenticate in gemini CLI
Docker enabled but not running
docker info
Then validate docker.enabled + image/container names in config.
Webhooks not arriving
- set
webhooks.enabled: true - expose
127.0.0.1:8742through tunnel/proxy when external sender is used - verify auth settings and hook ID
Upgrade and uninstall
Upgrade:
pipx upgrade ductor
Uninstall:
pipx uninstall ductor
rm -rf ~/.ductor # optional data removal