LocalControl is a minimal UNIX-socket control interface for Limnoria bots. It provides a local administrative command channel without exposing a separate remote control interface over IRC.
This project includes:
- A Limnoria plugin (
LocalControl) - A generic command‑line client (
botctl) - Optional wrapper scripts for multi‑bot setups
LocalControl is designed to be self-contained, predictable, and portable across multiple bot instances.
The socket is intended as a local owner-control channel. Local filesystem access
to .localcontrol.sock is therefore equivalent to owner-level bot access.
The optional TCP listener has the same owner-level control semantics and is
disabled by default.
Clone the repository into your Limnoria plugin directory, usually
~/runbot/plugins:
cd ~/runbot/plugins
git clone https://github.com/Alcheri/LocalControl.gitLoad the plugin into your bot:
/msg yourbot load LocalControl
The plugin creates this UNIX domain socket beside plugin.py:
~/runbot/plugins/LocalControl/.localcontrol.sock
LocalControl restricts the socket file to owner-only permissions (0600) after
binding it. The parent directory must still be protected so that only trusted
local users can reach the socket path.
If you install the plugin somewhere else, adjust the examples below to match that path.
LocalControl generates a synthetic IRC-style prefix for each local request, for example:
LocalControl123!local123@localcontrol.invalid
To allow this synthetic user to execute owner-level commands, you must add a matching hostmask to your Limnoria user account.
In your Limnoria console (or via IRC as the bot owner):
/msg <bot> user hostmask add <your-account> LocalControl*!local*@localcontrol.invalid
Without this step, the bot will reject requests from botctl because they will
not map to an authorized owner identity.
The botctl script communicates with the LocalControl socket.
It is located in the plugin directory:
~/runbot/plugins/LocalControl
Make it executable if needed:
chmod +x ~/runbot/plugins/LocalControl/botctlYou can then run it directly or add that directory to your PATH.
Basic usage:
botctl bot sysinfo
botctl bot say '#channel' Hello from LocalControl
botctl bot config <name> [<value>]
botctl exec "reload LocalControl"exec sends a raw command line. bot accepts command tokens directly, which
makes it convenient for bot commands such as sysinfo or say.
The LocalControl request flow:
flowchart LR
A[botctl CLI] --> B[(UNIX Socket)]
B --> C[LocalControl Plugin]
C --> D[Limnoria Core]
D --> E[Bot Commands / Owner Actions]
The CLI resolves the socket path in this order:
--socketcommand‑line flagBOT_CONTROL_SOCKETenvironment variable- Default path:
~/runbot/plugins/LocalControl/.localcontrol.sock
Example:
botctl --socket /tmp/test.sock bot sysinfoLocalControl can also expose the same line-based command protocol on TCP for local testing tools. This listener is disabled by default:
botctl bot config plugins.LocalControl.tcpListenerEnabledThe default TCP endpoint is loopback-only:
127.0.0.1:8023
Enable it for local testing:
botctl bot config plugins.LocalControl.tcpListenHost 127.0.0.1
botctl bot config plugins.LocalControl.tcpListenPort 8023
botctl bot config plugins.LocalControl.tcpListenerEnabled true
botctl exec "reload LocalControl"The listener is created when the plugin starts, so reload LocalControl after
changing tcpListenerEnabled, tcpListenHost, or tcpListenPort.
Binding to a non-loopback address is blocked unless
plugins.LocalControl.tcpAllowRemote is set to true. Do not enable remote TCP
binding unless the host firewall and network exposure are deliberately
controlled. Anyone who can connect can issue owner-level bot commands.
This usually means the synthetic LocalControl identity is not mapped to your owner account.
Verify that your Limnoria account has this hostmask:
LocalControl*!local*@localcontrol.invalid
If needed, add it again:
/msg <bot> user hostmask add <your-account> LocalControl*!local*@localcontrol.invalid
Check that the plugin is loaded and that the socket file exists at the expected path:
~/runbot/plugins/LocalControl/.localcontrol.sock
If your installation uses a different location, pass it explicitly:
botctl --socket /path/to/.localcontrol.sock bot sysinfoYou can also set it once with an environment variable:
export BOT_CONTROL_SOCKET=/path/to/.localcontrol.sockIf the socket file is missing entirely, reload the plugin and check the bot log for bind or startup errors.
Check the configured host, port, and enable flag:
botctl bot config plugins.LocalControl.tcpListenerEnabled
botctl bot config plugins.LocalControl.tcpListenHost
botctl bot config plugins.LocalControl.tcpListenPortAfter changing those values, reload the plugin:
botctl exec "reload LocalControl"If tcpListenHost is not a loopback address, either change it back to
127.0.0.1 or explicitly enable plugins.LocalControl.tcpAllowRemote.
The user running botctl must be able to access the socket file and its parent
directory.
Make sure:
- The bot is running under the expected local user account.
- You are invoking
botctlas a user with permission to access that socket. - The socket directory is not blocked by restrictive filesystem permissions.
If the bot was stopped uncleanly, a stale socket file can also cause problems. Reloading the plugin normally removes and recreates the socket.
Run it from the plugin directory or call it with its full path:
~/runbot/plugins/LocalControl/botctl bot sysinfoIf you want to call it as botctl from anywhere, either add the directory to
your PATH or place a wrapper script on your PATH.
LocalControl logs one line per socket request by default. To disable or enable
this, use the socketRequestLogging plugin configuration option:
Check the current setting:
botctl bot config plugins.LocalControl.socketRequestLoggingDisable logging:
botctl bot config plugins.LocalControl.socketRequestLogging falseRe-enable logging:
botctl bot config plugins.LocalControl.socketRequestLogging trueWhen enabled, each request is logged with its request ID, status, reply count, execution time in milliseconds, command name, argument count, and any errors. Full command text is not logged by default.
To temporarily log full command text for debugging, enable:
botctl bot config plugins.LocalControl.socketRequestFullCommandLogging trueFull command logging redacts obvious sensitive values such as passwords, passphrases, secrets, tokens, API keys, and key-style fields. Leave it disabled unless you specifically need full command text in the bot logs.
Disable full command logging again with:
botctl bot config plugins.LocalControl.socketRequestFullCommandLogging falseLocalControl applies a short timeout to each connected client. A client that connects but does not send a command is disconnected instead of holding a handler thread indefinitely.
Socket dispatches are serialised while LocalControl temporarily captures Limnoria replies, preventing overlapping local requests from racing the shared IRC send and queue hooks.
The optional GUI apps are beta desktop builds in dist/. They are provided for
testing and may change before a stable GUI release. End users do not need
Python, Tk, PyInstaller, or the private build tooling to run them.
Use the binary that matches your desktop platform:
- Linux:
dist/LocalControl-GUI - Windows:
dist/LocalControl-GUI.exe
From a Linux shell in the repository root:
./dist/LocalControl-GUIFrom Windows PowerShell in the repository root:
.\dist\LocalControl-GUI.exeYou can also start the Windows binary from Explorer by opening dist and
double-clicking LocalControl-GUI.exe.
The GUI connects to LocalControl through the same local socket or configured
remote command path as botctl. Keep socket files and SSH access restricted to
trusted local users because GUI access can issue owner-level bot commands.
On Windows, SSH mode expects native Windows OpenSSH with a Windows-accessible key or ssh-agent identity. Password prompts and WSL-held keys are not available to the Windows binary.
Before using SSH mode on Windows, verify that native OpenSSH can authenticate without a password prompt:
ssh -o BatchMode=yes user@hostIf that command asks for a password or fails, configure a key under
%USERPROFILE%\.ssh or add the key to the Windows ssh-agent first.
These beta binaries target recent Linux distributions and current Windows releases. Older platforms are not a support target for the GUI beta.
The GUI binaries are distributed under the project's BSD 3-Clause licence. They may include bundled Python, Tcl/Tk, sv-ttk, and PyInstaller runtime components; their upstream licence terms continue to apply.
If you run multiple bots, wrapper scripts can save you from repeatedly passing different socket paths.
#!/usr/bin/env bash
BOT_CONTROL_SOCKET="$HOME/runbot/plugins/LocalControl/.localcontrol.sock" botctl "$@"Place the wrapper somewhere on your PATH, make it executable, and use it just
like botctl.
- English supported.
See LICENCE.md.