chore: update CHANGELOG and Docker installation docs

- Added a fix for device pairing rejection during local connection retries in the CHANGELOG.
- Included guidance for Docker configuration to bypass device pairing in the installation documentation, detailing the necessary JSON configuration for the Control UI.
This commit is contained in:
Edward Tang 2026-01-28 22:47:56 -05:00
parent 699784dbee
commit 98224f31fb
5 changed files with 74 additions and 13 deletions

View File

@ -6,6 +6,8 @@ Docs: https://docs.molt.bot
Status: beta.
### Changes
- Gateway: fix device pairing rejection when local connection retries with existing non-silent pending request. Pending requests now upgrade to silent when a new local connection retries.
- Docs: add Docker config guidance for Control UI (controlUi.allowInsecureAuth to bypass device pairing for Docker bridge network connections).
- Rebrand: rename the npm package/CLI to `moltbot`, add a `moltbot` compatibility shim, and move extensions to the `@moltbot/*` scope.
- Commands: group /help and /commands output with Telegram paging. (#2504) Thanks @hougangdev.
- macOS: limit project-local `node_modules/.bin` PATH preference to debug builds (reduce PATH hijacking risk).
@ -13,7 +15,6 @@ Status: beta.
- Branding: update launchd labels, mobile bundle IDs, and logging subsystems to bot.molt (legacy com.clawdbot migrations). Thanks @thewilloftheshadow.
- Tools: add per-sender group tool policies and fix precedence. (#1757) Thanks @adam91holt.
- Agents: summarize dropped messages during compaction safeguard pruning. (#2509) Thanks @jogi47.
- Memory Search: allow extra paths for memory indexing (ignores symlinks). (#3600) Thanks @kira-ariaki.
- Skills: add multi-image input support to Nano Banana Pro skill. (#1958) Thanks @tyler6204.
- Agents: honor tools.exec.safeBins in exec allowlist checks. (#2281)
- Matrix: switch plugin SDK to @vector-im/matrix-bot-sdk.
@ -108,7 +109,6 @@ Status: beta.
- Telegram: centralize API error logging for delivery and bot calls. (#2492) Thanks @altryne.
- Voice Call: enforce Twilio webhook signature verification for ngrok URLs; disable ngrok free tier bypass by default.
- Security: harden Tailscale Serve auth by validating identity via local tailscaled before trusting headers.
- Media: fix text attachment MIME misclassification with CSV/TSV inference and UTF-16 detection; add XML attribute escaping for file output. (#3628) Thanks @frankekn.
- Build: align memory-core peer dependency with lockfile.
- Security: add mDNS discovery mode with minimal default to reduce information disclosure. (#1882) Thanks @orlyjamie.
- Security: harden URL fetches with DNS pinning to reduce rebinding risk. Thanks Chris Zheng.

View File

@ -1,19 +1,20 @@
services:
moltbot-gateway:
image: ${CLAWDBOT_IMAGE:-moltbot:local}
image: ${MOLTBOT_IMAGE:-moltbot:local}
environment:
HOME: /home/node
TERM: xterm-256color
CLAWDBOT_GATEWAY_TOKEN: ${CLAWDBOT_GATEWAY_TOKEN}
MOLTBOT_GATEWAY_TOKEN: ${MOLTBOT_GATEWAY_TOKEN}
MOLTBOT_SERVICE_TOKEN: ${MOLTBOT_SERVICE_TOKEN}
CLAUDE_AI_SESSION_KEY: ${CLAUDE_AI_SESSION_KEY}
CLAUDE_WEB_SESSION_KEY: ${CLAUDE_WEB_SESSION_KEY}
CLAUDE_WEB_COOKIE: ${CLAUDE_WEB_COOKIE}
volumes:
- ${CLAWDBOT_CONFIG_DIR}:/home/node/.clawdbot
- ${CLAWDBOT_WORKSPACE_DIR}:/home/node/clawd
- ${MOLTBOT_CONFIG_DIR:-~/.moltbot}:/home/node/.moltbot
- ${MOLTBOT_WORKSPACE_DIR:-~/clawd}:/home/node/clawd
ports:
- "${CLAWDBOT_GATEWAY_PORT:-18789}:18789"
- "${CLAWDBOT_BRIDGE_PORT:-18790}:18790"
- "${MOLTBOT_GATEWAY_PORT:-18789}:18789"
- "${MOLTBOT_BRIDGE_PORT:-18790}:18790"
init: true
restart: unless-stopped
command:
@ -22,13 +23,13 @@ services:
"dist/index.js",
"gateway",
"--bind",
"${CLAWDBOT_GATEWAY_BIND:-lan}",
"${MOLTBOT_GATEWAY_BIND:-lan}",
"--port",
"${CLAWDBOT_GATEWAY_PORT:-18789}"
"${MOLTBOT_GATEWAY_PORT:-18789}"
]
moltbot-cli:
image: ${CLAWDBOT_IMAGE:-moltbot:local}
image: ${MOLTBOT_IMAGE:-moltbot:local}
environment:
HOME: /home/node
TERM: xterm-256color
@ -37,8 +38,8 @@ services:
CLAUDE_WEB_SESSION_KEY: ${CLAUDE_WEB_SESSION_KEY}
CLAUDE_WEB_COOKIE: ${CLAUDE_WEB_COOKIE}
volumes:
- ${CLAWDBOT_CONFIG_DIR}:/home/node/.clawdbot
- ${CLAWDBOT_WORKSPACE_DIR}:/home/node/clawd
- ${MOLTBOT_CONFIG_DIR}:/home/node/.moltbot
- ${MOLTBOT_WORKSPACE_DIR}:/home/node/moltbot
stdin_open: true
tty: true
init: true

View File

@ -56,6 +56,27 @@ It writes config/workspace on the host:
- `~/.clawdbot/`
- `~/clawd`
**Important for Docker**: The dashboard requires additional config to bypass device pairing when accessed via the Docker bridge network. Create `~/.moltbot/moltbot.json` (or `~/.clawdbot/moltbot.json`) with:
```json
{
"gateway": {
"mode": "local",
"auth": {
"mode": "token",
"token": "YOUR_GATEWAY_TOKEN_HERE"
},
"controlUi": {
"allowInsecureAuth": true
}
}
}
```
Replace `YOUR_GATEWAY_TOKEN_HERE` with the actual token (or set `MOLTBOT_GATEWAY_TOKEN` environment variable instead).
The `controlUi.allowInsecureAuth` setting allows token-only authentication for the Control UI, bypassing device identity pairing. This is necessary because connections from the Docker bridge network (e.g., `172.21.0.1`) are not detected as "local" connections, even when accessing via `http://127.0.0.1:18789`. Without this setting, you will see "pairing required" errors.
Running on a VPS? See [Hetzner (Docker VPS)](/platforms/hetzner).
### Manual flow (compose)

View File

@ -41,4 +41,37 @@ describe("device pairing tokens", () => {
paired = await getPairedDevice("device-1", baseDir);
expect(paired?.tokens?.operator?.scopes).toEqual(["operator.read"]);
});
test("updates silent flag when local connection retries existing pending request", async () => {
const baseDir = await mkdtemp(join(tmpdir(), "moltbot-device-pairing-"));
// First request from non-local connection (silent: false)
const firstRequest = await requestDevicePairing(
{
deviceId: "device-2",
publicKey: "public-key-2",
role: "operator",
scopes: [],
silent: false,
},
baseDir,
);
expect(firstRequest.created).toBe(true);
expect(firstRequest.request.silent).toBe(false);
// Second request from local connection (silent: true) for same device
const secondRequest = await requestDevicePairing(
{
deviceId: "device-2",
publicKey: "public-key-2",
role: "operator",
scopes: [],
silent: true,
},
baseDir,
);
expect(secondRequest.created).toBe(false);
expect(secondRequest.request.silent).toBe(true);
expect(secondRequest.request.requestId).toBe(firstRequest.request.requestId);
});
});

View File

@ -246,6 +246,12 @@ export async function requestDevicePairing(
}
const existing = Object.values(state.pendingById).find((p) => p.deviceId === deviceId);
if (existing) {
// If the new request is silent (local connection), update the existing request
// to allow auto-approval even if the original request was not silent.
if (req.silent && !existing.silent) {
existing.silent = true;
await persistState(state, baseDir);
}
return { status: "pending", request: existing, created: false };
}
const isRepair = Boolean(state.pairedByDeviceId[deviceId]);