Add optional Nanobot plugin controls (#4396)

* feat: add optional nanobot features

* test: update azure install hint expectation

* fix: validate optional feature extras

maintainer edit: verify requested dependency extras before treating optional features as installed, propagate restart state from feature enablement, and align docs with the new plugins enable command.

* fix: bound optional feature installs

maintainer edit: make optional feature installs time out as a normal install failure instead of leaving the WebUI or CLI action waiting indefinitely.

* feat: slim optional channel dependencies

* fix: log optional install commands

* fix(webui): gate remote feature installs

* docs: clarify webhook plugin example

* fix(webui): harden optional feature installs

* fix: install optional deps without package fallback

* fix(cli): refine plugin feature controls

* fix(webui): count enabled nanobot features

* fix(webui): allow slow feature install routes

* fix(webui): allow disabling websocket channel

* fix(plugins): simplify optional feature controls

* fix(webui): polish apps catalog states

* fix(webui): confirm nanobot support installs

* fix(webui): polish nanobot install dialog

* fix(webui): suppress empty websocket handshakes

* fix(webui): clarify apps plugin summary

* fix(webui): localize workspace access copy

* fix(plugins): polish optional feature controls (#4691)

---------

Co-authored-by: Xubin Ren <52506698+Re-bin@users.noreply.github.com>
This commit is contained in:
chengyongru
2026-07-03 18:17:52 +08:00
committed by GitHub
co-authored by Xubin Ren
parent 00cc0da530
commit 5283ceae85
61 changed files with 3061 additions and 258 deletions
+6 -6
View File
@@ -155,7 +155,7 @@ The key (`webhook`) becomes the config section name. The value points to your `B
```bash
python -m pip install -e .
nanobot plugins list # verify "Webhook" shows as "plugin"
nanobot plugins list # verify the installed example plugin appears as "webhook"
nanobot onboard # auto-adds default config for detected plugins
```
@@ -552,7 +552,7 @@ If not overridden, the base class returns `{"enabled": false}`.
git clone https://github.com/you/nanobot-channel-webhook
cd nanobot-channel-webhook
python -m pip install -e .
nanobot plugins list # should show "Webhook" as "plugin"
nanobot plugins list # should show the installed example plugin as "webhook"
nanobot gateway # test end-to-end
```
@@ -561,8 +561,8 @@ nanobot gateway # test end-to-end
```bash
$ nanobot plugins list
Name Source Enabled
telegram builtin yes
discord builtin no
webhook plugin yes
Name Type Enabled
discord channel no
telegram channel yes
webhook channel yes
```
+64 -12
View File
@@ -12,6 +12,21 @@ If that fails, fix installation, config, provider, or model setup first with [`q
Most examples below are snippets to merge into `~/.nanobot/config.json`.
> [!NOTE]
> If you are upgrading from a version where chat app SDKs were installed by default,
> install the channel extra in the same Python environment before enabling or
> restarting that channel:
>
> ```bash
> nanobot plugins enable <channel>
> ```
>
> Replace `<channel>` with names such as `telegram`, `slack`, `feishu`,
> `dingtalk`, `matrix`, `qq`, `napcat`, `weixin`, `wecom`, or `msteams`.
> To turn a channel off later, run `nanobot plugins disable <channel>`.
> nanobot keeps the saved settings, but stops loading that channel after the
> next restart.
## Common Setup Pattern
Every chat app uses the same shape:
@@ -59,6 +74,12 @@ If `nanobot channels status` does not show the channel as enabled, the config sn
<details>
<summary><b>Telegram</b></summary>
**Install the optional channel dependency**
```bash
nanobot plugins enable telegram
```
**1. Create a bot**
- Open Telegram, search `@BotFather`
- Send `/newbot`, follow prompts
@@ -123,6 +144,14 @@ Telegram uses long polling by default. To receive updates through a webhook, exp
Uses **Socket.IO WebSocket** by default, with HTTP polling fallback.
**Install the optional realtime dependency**
```bash
nanobot plugins enable mochat
```
Without this extra, Mochat still works through HTTP polling.
**1. Ask nanobot to set up Mochat for you**
Simply send this message to nanobot (replace `xxx@xxx` with your real email):
@@ -233,14 +262,14 @@ nanobot gateway
<details>
<summary><b>Matrix (Element)</b></summary>
Install Matrix dependencies first:
Enable Matrix support first:
```bash
python -m pip install "nanobot-ai[matrix]"
nanobot plugins enable matrix
```
> [!NOTE]
> Matrix is not supported on Windows. `matrix-nio[e2e]` depends on `python-olm`, which has no pre-built Windows wheel and is skipped by the `matrix` extra on `sys_platform == 'win32'`. The command above will still succeed on Windows but without `matrix-nio` installed, so enabling the Matrix channel will fail at startup. Use macOS, Linux, or WSL2.
> Matrix encryption is disabled by default on Windows because `matrix-nio[e2e]` depends on `python-olm`, which has no pre-built Windows wheel. Use macOS, Linux, or WSL2 if you need Matrix E2EE.
**1. Create/choose a Matrix account**
@@ -306,9 +335,7 @@ nanobot gateway
Requires the WhatsApp optional dependencies:
```bash
pip install "nanobot-ai[whatsapp]"
# Source checkout:
python -m pip install -e ".[whatsapp]"
nanobot plugins enable whatsapp
```
**1. Link device with QR**
@@ -384,6 +411,7 @@ Uses **WebSocket** long connection — no public IP required.
**Quick setup: QR login**
```bash
nanobot plugins enable feishu
nanobot channels login feishu
# Use --force to create/sign in with a new bot
```
@@ -454,6 +482,12 @@ nanobot gateway
Uses **botpy SDK** with WebSocket — no public IP required. Currently supports **private messages only**.
**Install the optional channel dependency**
```bash
nanobot plugins enable qq
```
**1. Register & create bot**
- Visit [QQ Open Platform](https://q.qq.com) → Register as a developer (personal or enterprise)
- Create a new bot application
@@ -506,6 +540,12 @@ Connects to a [Napcat](https://github.com/NapNeko/NapCatQQ) instance over its **
- Copy the forward websocket server's token
- (Optional) In the webui, follow "系统配置" -> "登陆配置" -> "快速登录QQ" to automatically login after restarts
**Install the optional channel dependency**
```bash
nanobot plugins enable napcat
```
**2. Configure**
```json
@@ -543,6 +583,12 @@ Connects to a [Napcat](https://github.com/NapNeko/NapCatQQ) instance over its **
Uses **Stream Mode** — no public IP required.
**Install the optional channel dependency**
```bash
nanobot plugins enable dingtalk
```
**1. Create a DingTalk bot**
- Visit [DingTalk Open Platform](https://open-dev.dingtalk.com/)
- Create a new app -> Add **Robot** capability
@@ -585,6 +631,12 @@ nanobot gateway
Uses **Socket Mode** — no public URL required.
**Install the optional channel dependency**
```bash
nanobot plugins enable slack
```
**1. Create a Slack app**
- Go to [Slack API](https://api.slack.com/apps) → **Create New App** → "From scratch"
- Pick a name and select your workspace
@@ -695,10 +747,10 @@ nanobot gateway
Uses **HTTP long-poll** with QR-code login via the ilinkai personal WeChat API. No local WeChat desktop client is required.
**1. Install with WeChat support**
**1. Enable WeChat support**
```bash
python -m pip install "nanobot-ai[weixin]"
nanobot plugins enable weixin
```
**2. Configure**
@@ -747,10 +799,10 @@ nanobot gateway
>
> Uses **WebSocket** long connection — no public IP required.
**1. Install the optional dependency**
**1. Enable WeCom support**
```bash
python -m pip install "nanobot-ai[wecom]"
nanobot plugins enable wecom
```
**2. Create a WeCom AI Bot**
@@ -786,10 +838,10 @@ nanobot gateway
> Direct-message text in/out, tenant-aware OAuth, conversation reference persistence.
> Uses a public HTTPS webhook — no WebSocket; you need a tunnel or reverse proxy.
**1. Install the optional dependency**
**1. Enable Microsoft Teams support**
```bash
python -m pip install "nanobot-ai[msteams]"
nanobot plugins enable msteams
```
**2. Create a Teams / Azure bot app registration**
+18
View File
@@ -16,6 +16,7 @@ Use this page when you know what you want to run and need the command shape. For
| Deliver a local trigger | `nanobot trigger <id> "message"` | Created first with `/trigger <name>` in the target chat/session |
| Serve an OpenAI-compatible API | `nanobot serve` | Starts `/v1/chat/completions`, `/v1/models`, and `/health` |
| Check chat channel setup | `nanobot channels status` | Useful before starting `nanobot gateway` |
| Manage optional features | `nanobot plugins list` | Shows channels and optional capabilities you can turn on |
| Log in to QR/OAuth-style channels | `nanobot channels login <channel>` | Used by channels such as WhatsApp and WeChat |
| Log in to OAuth model providers | `nanobot provider login <provider>` | Used by OAuth providers such as OpenAI Codex and GitHub Copilot |
@@ -217,6 +218,23 @@ nanobot channels status
See [`chat-apps.md`](./chat-apps.md) for channel-specific setup.
## Optional Features
Use these commands when you want nanobot to add or remove a built-in capability
without hand-editing JSON. Enabling may install the support package first.
Disabling is for channels such as Telegram, Matrix, or Slack; it keeps your
saved settings and turns the channel off.
| Command | Description |
|---|---|
| `nanobot plugins list` | Show available channels and optional capabilities |
| `nanobot plugins enable <name>` | Install missing support and enable the feature or channel |
| `nanobot plugins enable <name> --logs` | Show package install logs while enabling |
| `nanobot plugins disable <channel>` | Turn off a channel without deleting its saved settings |
| `nanobot plugins list --config <path>` | Read a specific config file |
| `nanobot plugins enable <name> --config <path>` | Update a specific config file |
| `nanobot plugins disable <channel> --config <path>` | Turn off a channel in a specific config file |
## Provider OAuth
| Command | Description |
+16 -4
View File
@@ -41,7 +41,7 @@ If you are not sure where a setting belongs, start from the task you are trying
| Make the first model reply work | `providers.<name>.apiKey`, optional `providers.<name>.apiBase`, `modelPresets.<preset>`, `agents.defaults.modelPreset` | `nanobot status`, then `nanobot agent -m "Hello!"` | [Providers](#providers), [Model Presets](#model-presets) |
| Add fallback models | `modelPresets.<fallback>`, `agents.defaults.fallbackModels` | `nanobot status`, then a normal agent run | [Model Fallbacks](#model-fallbacks) |
| Keep secrets out of the config file | `${ENV_VAR}` placeholders inside any string value | Start nanobot from the same environment that sets the variable | [Environment Variables for Secrets](#environment-variables-for-secrets) |
| Open the bundled WebUI | `channels.websocket.enabled`, optional `channels.websocket.port`, `channels.websocket.tokenIssueSecret` | `nanobot gateway`, then open `http://127.0.0.1:8765` | [Channel Settings](#channel-settings), [WebSocket docs](./websocket.md) |
| Open the bundled WebUI | Optional `channels.websocket.port`, `channels.websocket.tokenIssueSecret` | `nanobot gateway`, then open `http://127.0.0.1:8765` | [Channel Settings](#channel-settings), [WebSocket docs](./websocket.md) |
| Connect one chat app | `channels.<channel>.enabled`, channel credentials, `channels.<channel>.allowFrom` | `nanobot channels status`, then `nanobot gateway --verbose` | [Channel Settings](#channel-settings), [Chat Apps](./chat-apps.md) |
| Enable voice transcription | `transcription.enabled`, `transcription.provider`, matching `providers.<name>.apiKey` | Send or upload a short voice message through a configured surface | [Transcription Settings](#transcription-settings) |
| Enable web search or fetch | `tools.web.search.*`, `tools.web.fetch.*`, optional `tools.ssrfWhitelist` | Ask a question that requires current web information, then inspect logs if needed | [Web Tools](#web-tools), [Security](#security) |
@@ -380,7 +380,7 @@ Omit `apiKey` (or leave it empty / unset). The provider falls back to [`DefaultA
Install the optional dependency:
```bash
python -m pip install 'nanobot-ai[azure]'
nanobot plugins enable azure
```
`DefaultAzureCredential` walks this chain in order and uses the first identity that succeeds:
@@ -395,7 +395,7 @@ python -m pip install 'nanobot-ai[azure]'
The identity that ends up signing the request **must be assigned the `Cognitive Services OpenAI User` RBAC role** (or higher) on the Azure OpenAI resource. Without that role you will see `401`/`403` errors at the first request.
> `apiBase` remains mandatory in both modes — it's your Azure resource endpoint and cannot be inferred. If neither `apiKey` is set nor `azure-identity` is installed, the provider raises a clear error pointing you at `python -m pip install 'nanobot-ai[azure]'`.
> `apiBase` remains mandatory in both modes — it's your Azure resource endpoint and cannot be inferred. If neither `apiKey` is set nor `azure-identity` is installed, the provider raises a clear error pointing you at `nanobot plugins enable azure`.
</details>
@@ -439,6 +439,17 @@ Bedrock uses the native `bedrock-runtime` Converse API, so it can call Bedrock m
This provider is for Bedrock's native Converse API, not Bedrock's OpenAI-compatible `/openai/v1` endpoint. For OpenAI-compatible Bedrock models, you can still use `custom` if you specifically want that API surface.
Install Bedrock support first:
```bash
nanobot plugins enable bedrock
```
> [!NOTE]
> If you configured Bedrock before `boto3` became an optional dependency, run
> `nanobot plugins enable bedrock` after upgrading. Otherwise the provider will
> fail when it first tries to create a Bedrock client.
**1. Configure credentials**
Use the normal AWS credential chain (`AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`, an AWS profile, or an IAM role). The IAM identity needs:
@@ -1511,7 +1522,7 @@ Global settings that apply to all channels. Configure under the `channels` secti
| `sendProgress` | `true` | Stream agent's text progress to the channel |
| `sendToolHints` | `false` | Stream tool-call hints (e.g. `read_file("…")`) |
| `showReasoning` | `true` | Allow channels to surface model reasoning/thinking content (DeepSeek-R1 `reasoning_content`, Anthropic `thinking_blocks`, inline `<think>` tags). Reasoning flows as a dedicated stream with `_reasoning_delta` / `_reasoning_end` markers — channels override `send_reasoning_delta` / `send_reasoning_end` to render in-place updates. Even with `true`, channels without those overrides stay no-op silently. Currently surfaced on CLI and WebSocket/WebUI (italic shimmer header, auto-collapses after the stream ends); Telegram / Slack / Discord / Feishu / WeChat / Matrix keep the base no-op until their bubble UI is adapted. Independent of `sendProgress`. |
| `extractDocumentText` | `true` | Extract supported document/text attachments into the model prompt. Set to `false` to keep document content out of the prompt and include attachment path references instead. |
| `extractDocumentText` | `true` | Extract supported document/text attachments into the model prompt. Install parser dependencies with `nanobot plugins enable documents`. If you used document parsing before those parsers became optional, run that command after upgrading. Set to `false` to keep document content out of the prompt and include attachment path references instead. |
| `sendMaxRetries` | `3` | Max delivery attempts per outbound message, including the initial send (0-10 configured, minimum 1 actual attempt) |
`channels.transcriptionProvider` and `channels.transcriptionLanguage` are deprecated compatibility fields. They remain as a read-only fallback for older configs, but new configuration should use top-level `transcription.provider` and `transcription.language`.
@@ -1906,6 +1917,7 @@ For API keys, tokens, and other secrets, see [Environment Variables for Secrets]
| `tools.exec.timeout` | `60` | Default hard timeout in seconds for shell commands. Config values may exceed the per-call tool cap; set `0` to disable the hard timeout for trusted long-running commands. |
| `tools.exec.pathPrepend` | `""` | Extra directories to prepend to `PATH` when running shell commands. Use this when configured tools should win executable lookup precedence, such as a Python virtual environment's `bin` or `Scripts` directory. |
| `tools.exec.pathAppend` | `""` | Extra directories to append to `PATH` when running shell commands (e.g. `/usr/sbin` for `ufw`). |
| `tools.webuiAllowRemotePackageInstall` | `false` | When `false`, the WebUI can install missing optional packages only from a browser opened on the same machine as nanobot. Set to `true` only when a trusted remote admin is allowed to install Python packages into this nanobot environment. |
| `tools.ssrfWhitelist` | `[]` | CIDR ranges exempted from the shared SSRF guard used by web fetches and HTTP/SSE MCP connections. Prefer exact host CIDRs such as `192.168.1.50/32`; broad ranges increase SSRF exposure. |
| `channels.*.allowFrom` | omitted | Access control per channel. Omit to use pairing-only mode; set `["*"]` to allow everyone; or list specific user IDs. See [Pairing](#pairing) for details. |
+1 -2
View File
@@ -38,14 +38,13 @@ Restart the deployed process after editing `config.json`. Long-running processes
> Official Docker usage currently means building from this repository with the included `Dockerfile`. Docker Hub images under third-party namespaces are not maintained or verified by HKUDS/nanobot; do not mount API keys or bot tokens into them unless you trust the publisher.
> [!IMPORTANT]
> The gateway and WebSocket channel default to `host: "127.0.0.1"` in `config.json` (set in `nanobot/config/schema.py`). Docker `-p` port forwarding cannot reach a container's loopback interface, so for the host or LAN to reach the exposed ports you must set both binds to `0.0.0.0` in `~/.nanobot/config.json` before starting the container. To serve the bundled WebUI from Docker, enable the WebSocket channel and protect bootstrap with a secret:
> The gateway and WebSocket channel default to `host: "127.0.0.1"` in `config.json` (set in `nanobot/config/schema.py`). Docker `-p` port forwarding cannot reach a container's loopback interface, so for the host or LAN to reach the exposed ports you must set both binds to `0.0.0.0` in `~/.nanobot/config.json` before starting the container. To serve the bundled WebUI from Docker, bind the WebSocket channel externally and protect bootstrap with a secret:
>
> ```json
> {
> "gateway": { "host": "0.0.0.0" },
> "channels": {
> "websocket": {
> "enabled": true,
> "host": "0.0.0.0",
> "port": 8765,
> "tokenIssueSecret": "your-secret-here"
+1 -1
View File
@@ -3,7 +3,7 @@
nanobot can expose a minimal OpenAI-compatible endpoint for local integrations:
```bash
python -m pip install "nanobot-ai[api]"
nanobot plugins enable api
nanobot agent -m "Hello!"
nanobot serve
```
+1 -1
View File
@@ -329,7 +329,7 @@ nanobot --version
If you use WhatsApp from a source checkout, keep the optional dependencies installed:
```bash
python -m pip install -e ".[whatsapp]"
nanobot plugins enable whatsapp
```
## First-Run Troubleshooting
+2 -4
View File
@@ -181,11 +181,11 @@ For the first setup, choose `[Q] Quick Start`. It configures the recommended loc
4. Paste your API key if the wizard asks for one.
5. Paste the provider base URL if the wizard asks for one.
6. Paste a model ID that provider can run.
7. Confirm that Quick Start should enable the WebSocket channel for the local WebUI.
7. Confirm that Quick Start should configure the local WebUI.
8. Set the WebUI password when prompted.
9. Review the Quick Start summary. The wizard saves and exits when Quick Start finishes.
The recommended path enables `channels.websocket` for the local WebUI, requires a WebUI password, and writes default AI settings. You do not need to choose a separate chat app for the first run.
The recommended path configures the local WebUI, requires a WebUI password, and writes default AI settings. You do not need to choose a separate chat app for the first run.
If you already know that you need custom headers, provider-specific request fields, a chat app, or tools, choose `Advanced Settings` instead. [`provider-cookbook.md`](./provider-cookbook.md) has copyable examples for several common provider setups. After you change advanced settings, a save option appears in the main menu. Choose `[S] Save and Exit`.
@@ -225,7 +225,6 @@ Merge them into one object:
},
"channels": {
"websocket": {
"enabled": true,
"tokenIssueSecret": "your-webui-password",
"websocketRequiresToken": true
}
@@ -288,7 +287,6 @@ If this is a brand-new install and you have not configured anything else yet, re
},
"channels": {
"websocket": {
"enabled": true,
"tokenIssueSecret": "your-webui-password",
"websocketRequiresToken": true
}
+3 -8
View File
@@ -16,13 +16,13 @@ Nanobot can act as a WebSocket server, allowing external clients (web apps, CLIs
### 1. Configure
Add to `config.json` under `channels.websocket`:
The WebSocket channel is enabled by default. Add only the fields you want to
override under `channels.websocket`:
```json
{
"channels": {
"websocket": {
"enabled": true,
"host": "127.0.0.1",
"port": 8765,
"path": "/",
@@ -208,7 +208,7 @@ All fields go under `channels.websocket` in `config.json`.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `enabled` | bool | `false` | Enable the WebSocket server. |
| `enabled` | bool | `true` | Enable the WebSocket server. Set to `false` only when you intentionally do not want the bundled WebUI/WebSocket surface. |
| `host` | string | `"127.0.0.1"` | Bind address. Use `"0.0.0.0"` to accept external connections. |
| `port` | int | `8765` | Listen port. |
| `path` | string | `"/"` | WebSocket upgrade path. Trailing slashes are normalized (root `/` is preserved). |
@@ -272,7 +272,6 @@ For production deployments where `websocketRequiresToken: true`, use short-lived
{
"channels": {
"websocket": {
"enabled": true,
"port": 8765,
"path": "/ws",
"tokenIssuePath": "/auth/token",
@@ -367,7 +366,6 @@ Outbound `message` events may include a `media` field containing local filesyste
{
"channels": {
"websocket": {
"enabled": true,
"host": "0.0.0.0",
"port": 8765,
"websocketRequiresToken": false,
@@ -384,7 +382,6 @@ Outbound `message` events may include a `media` field containing local filesyste
{
"channels": {
"websocket": {
"enabled": true,
"token": "my-shared-secret",
"allowFrom": ["alice", "bob"]
}
@@ -400,7 +397,6 @@ Clients connect with `?token=my-shared-secret&client_id=alice`.
{
"channels": {
"websocket": {
"enabled": true,
"host": "0.0.0.0",
"port": 8765,
"path": "/ws",
@@ -421,7 +417,6 @@ Clients connect with `?token=my-shared-secret&client_id=alice`.
{
"channels": {
"websocket": {
"enabled": true,
"path": "/chat/ws",
"allowFrom": ["*"]
}
+42 -8
View File
@@ -15,14 +15,14 @@ First confirm your provider and model can answer:
nanobot agent -m "Hello!"
```
Then merge the WebSocket channel into your existing `~/.nanobot/config.json`.
Set `tokenIssueSecret` to the password you will enter in the WebUI login form:
The local WebSocket channel is enabled by default because it serves the bundled
WebUI. To require a browser login password, merge `tokenIssueSecret` into your
existing `~/.nanobot/config.json`:
```json
{
"channels": {
"websocket": {
"enabled": true,
"tokenIssueSecret": "your-webui-password",
"websocketRequiresToken": true
}
@@ -94,9 +94,20 @@ for provider setup and output behavior.
## Apps
Open Apps from the sidebar or settings navigation to manage integrations that
nanobot can call from a chat. CLI Apps install local adapters that nanobot runs
on your machine; they do not modify the native apps themselves. MCP presets add
predefined MCP server configurations.
nanobot can call from a chat. Nanobot features can enable built-in channels and
optional capabilities such as `bedrock` or `documents`. CLI Apps install local
adapters that nanobot runs on your machine; they do not modify the native apps
themselves. MCP presets add predefined MCP server configurations.
Enabling a Nanobot feature may install Python packages into the environment
running nanobot. By default, the WebUI can install missing packages only when
you open it on the same machine as nanobot. If you open the WebUI from another
device, a domain name, a tunnel, or a reverse proxy, package install is blocked
unless you explicitly allow it with `tools.webuiAllowRemotePackageInstall`.
Optional feature installs use your existing pip download settings. If PyPI is
slow or unavailable from your network, configure pip or set `PIP_INDEX_URL`
before starting nanobot.
Some MCP presets connect to hosted keyless endpoints. For example, the Firecrawl
preset uses Firecrawl's hosted MCP endpoint for search, scrape, crawl, and
@@ -187,7 +198,6 @@ channel to all interfaces and set a token or token issue secret:
{
"channels": {
"websocket": {
"enabled": true,
"host": "0.0.0.0",
"port": 8765,
"tokenIssueSecret": "your-secret-here"
@@ -201,12 +211,36 @@ The gateway refuses to start with `host` set to `"0.0.0.0"` unless `token` or
`http://<your-ip>:8765` from the other device and enter the secret in the login
form.
Remote WebUI clients can view Apps and toggle already-installed features with a
valid token, but they cannot install missing Python packages by default. To allow
trusted remote admins to install optional feature dependencies from the WebUI,
opt in explicitly:
```json
{
"tools": {
"webuiAllowRemotePackageInstall": true
}
}
```
Use this only for a private deployment where every authenticated WebUI user is
trusted to change the Python environment that nanobot runs in. If you publish
the WebUI through Nginx, Caddy, Cloudflare Tunnel, or a similar service, treat it
as remote access and leave package installs disabled unless that is intentional.
Optional feature installs use pip's configured package index, including
`PIP_INDEX_URL`.
Leave remote package installs disabled when the WebUI is exposed beyond a
private, trusted network.
## Troubleshooting
If the page does not open, check these in order:
1. `nanobot agent -m "Hello!"` works in the same Python environment.
2. The WebSocket channel is enabled in `~/.nanobot/config.json`.
2. `~/.nanobot/config.json` does not explicitly set `channels.websocket.enabled` to `false`.
3. `nanobot gateway` is still running.
4. You are opening port `8765`, not the gateway health port.
5. LAN access uses `host: "0.0.0.0"` and a token or token issue secret.