diff --git a/Dockerfile b/Dockerfile
index c9ee20717..4899f1b13 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -26,7 +26,7 @@ RUN chmod +x scripts/render-start.sh
RUN pnpm install --frozen-lockfile
COPY . .
-RUN CLAWDBOT_A2UI_SKIP_MISSING=1 pnpm build
+RUN MOLTBOT_A2UI_SKIP_MISSING=1 pnpm build
# Force pnpm for UI build (Bun may fail on ARM/Synology architectures)
ENV CLAWDBOT_PREFER_PNPM=1
RUN pnpm ui:install
diff --git a/docs/render.mdx b/docs/render.mdx
index 8dfaed833..7806cadaa 100644
--- a/docs/render.mdx
+++ b/docs/render.mdx
@@ -11,14 +11,12 @@ Deploy Moltbot on Render using Infrastructure as Code. The included `render.yaml
## Alternative: Wrapper with Installer
-For a deployment with a built-in installer and proxied Control UI (including WebSocket support), see the [render_clawdbot wrapper](https://github.com/ojusave/render_clawdbot). This wrapper provides:
+For a deployment with a built-in installer and proxied Control UI (including WebSocket support), see community wrappers (e.g. in the ecosystem docs). Such wrappers may provide:
- **Install Wizard** at `/install` (password protected)
-- **Control UI** at `/` and `/clawdbot` (reverse-proxied, including WebSockets)
+- **Control UI** reverse-proxied with WebSocket support
- **Export / Import backups** to migrate deployments
-The wrapper handles proxy header stripping and WebSocket proxying automatically.
-
## Deploy with a Render Blueprint
Deploy to Render
@@ -26,7 +24,7 @@ The wrapper handles proxy header stripping and WebSocket proxying automatically.
Clicking this link will:
1. Create a new Render service from the `render.yaml` Blueprint at the root of this repo.
-2. Prompt you to set `CLAWDBOT_GATEWAY_TOKEN` (or set it in **Environment** after deploy).
+2. Prompt you to set `MOLTBOT_GATEWAY_TOKEN` (or set it in **Environment** after deploy).
3. Build the Docker image and deploy.
Once deployed, your service URL follows the pattern `https://.onrender.com`.
@@ -46,11 +44,11 @@ services:
envVars:
- key: PORT
value: "8080"
- - key: CLAWDBOT_GATEWAY_TOKEN
+ - key: MOLTBOT_GATEWAY_TOKEN
sync: false # set in Render dashboard (secret)
- - key: CLAWDBOT_STATE_DIR
- value: /data/.clawdbot
- - key: CLAWDBOT_WORKSPACE_DIR
+ - key: MOLTBOT_STATE_DIR
+ value: /data/.moltbot
+ - key: MOLTBOT_WORKSPACE_DIR
value: /data/workspace
# LLM Provider API Keys (set these in Render dashboard as secrets)
- key: ANTHROPIC_API_KEY
@@ -94,7 +92,7 @@ The Blueprint defaults to `starter`. To use free tier, change `plan: free` in yo
### Set the gateway token
-1. In Render **Dashboard → your service → Environment**, set `CLAWDBOT_GATEWAY_TOKEN` to a long random secret (or generate one with `openssl rand -hex 32`).
+1. In Render **Dashboard → your service → Environment**, set `MOLTBOT_GATEWAY_TOKEN` to a long random secret (or generate one with `openssl rand -hex 32`).
2. Save changes; Render will redeploy.
### Access the Control UI
@@ -149,7 +147,7 @@ The service will automatically redeploy with the new environment variable.
**Alternative: Config file method**
-You can also configure API keys in the `moltbot.json` config file (under `CLAWDBOT_STATE_DIR` or `~/.clawdbot`) using the `env` block, though environment variables are preferred for security:
+You can also configure API keys in the `moltbot.json` config file (under `MOLTBOT_STATE_DIR` or `~/.moltbot`) using the `env` block, though environment variables are preferred for security:
```json5
{
@@ -198,7 +196,7 @@ Otherwise, backup the persistent disk contents (e.g. under `/data/.moltbot`) via
Check the deploy logs in the Render Dashboard. Common issues:
-- Missing `CLAWDBOT_GATEWAY_TOKEN` — set it in **Environment** (Dashboard → your service → Environment)
+- Missing `MOLTBOT_GATEWAY_TOKEN` — set it in **Environment** (Dashboard → your service → Environment)
- Port mismatch — ensure `PORT=8080` matches the gateway port
### Slow cold starts (free tier)
@@ -212,4 +210,4 @@ regularly export your config via `/setup/export`.
### Health check failures
-If Render is configured with `healthCheckPath: /health`, it expects a 200 from `/health` within 30 seconds. This blueprint does not set a health check by default. If deploys fail, check deploy logs and that `scripts/render-start.sh` runs correctly (config written under `CLAWDBOT_STATE_DIR` or `~/.moltbot`/`~/.clawdbot`, then gateway started with the token).
+If Render is configured with `healthCheckPath: /health`, it expects a 200 from `/health` within 30 seconds. This blueprint does not set a health check by default. If deploys fail, check deploy logs and that `scripts/render-start.sh` runs correctly (config written under `MOLTBOT_STATE_DIR` or `~/.moltbot`, then gateway started with the token).
diff --git a/render.yaml b/render.yaml
index dd0ff11f6..1805c6195 100644
--- a/render.yaml
+++ b/render.yaml
@@ -7,11 +7,11 @@ services:
envVars:
- key: PORT
value: "8080"
- - key: CLAWDBOT_GATEWAY_TOKEN
+ - key: MOLTBOT_GATEWAY_TOKEN
sync: false
- - key: CLAWDBOT_STATE_DIR
- value: /data/.clawdbot
- - key: CLAWDBOT_WORKSPACE_DIR
+ - key: MOLTBOT_STATE_DIR
+ value: /data/.moltbot
+ - key: MOLTBOT_WORKSPACE_DIR
value: /data/workspace
# LLM Provider API Keys - Set these in Render dashboard as secrets
# Required: Set at least one API key for the provider you want to use
diff --git a/scripts/canvas-a2ui-copy.ts b/scripts/canvas-a2ui-copy.ts
index e95be5fdd..ca930ef06 100644
--- a/scripts/canvas-a2ui-copy.ts
+++ b/scripts/canvas-a2ui-copy.ts
@@ -6,9 +6,9 @@ const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."
export function getA2uiPaths(env = process.env) {
const srcDir =
- env.CLAWDBOT_A2UI_SRC_DIR ?? path.join(repoRoot, "src", "canvas-host", "a2ui");
+ env.MOLTBOT_A2UI_SRC_DIR ?? env.CLAWDBOT_A2UI_SRC_DIR ?? path.join(repoRoot, "src", "canvas-host", "a2ui");
const outDir =
- env.CLAWDBOT_A2UI_OUT_DIR ?? path.join(repoRoot, "dist", "canvas-host", "a2ui");
+ env.MOLTBOT_A2UI_OUT_DIR ?? env.CLAWDBOT_A2UI_OUT_DIR ?? path.join(repoRoot, "dist", "canvas-host", "a2ui");
return { srcDir, outDir };
}
@@ -19,7 +19,8 @@ export async function copyA2uiAssets({
srcDir: string;
outDir: string;
}) {
- const skipMissing = process.env.CLAWDBOT_A2UI_SKIP_MISSING === "1";
+ const skipMissing =
+ process.env.MOLTBOT_A2UI_SKIP_MISSING === "1" || process.env.CLAWDBOT_A2UI_SKIP_MISSING === "1";
try {
await fs.stat(path.join(srcDir, "index.html"));
await fs.stat(path.join(srcDir, "a2ui.bundle.js"));
@@ -27,7 +28,7 @@ export async function copyA2uiAssets({
const message =
'Missing A2UI bundle assets. Run "pnpm canvas:a2ui:bundle" and retry.';
if (skipMissing) {
- console.warn(`${message} Skipping copy (CLAWDBOT_A2UI_SKIP_MISSING=1).`);
+ console.warn(`${message} Skipping copy (MOLTBOT_A2UI_SKIP_MISSING=1).`);
return;
}
throw new Error(message, { cause: err });
diff --git a/src/canvas-host/a2ui.ts b/src/canvas-host/a2ui.ts
index 2a19d03dc..a8052e733 100644
--- a/src/canvas-host/a2ui.ts
+++ b/src/canvas-host/a2ui.ts
@@ -54,6 +54,11 @@ async function resolveA2uiRootReal(): Promise {
return resolvingA2uiRoot;
}
+/** Returns true if A2UI assets (index.html + a2ui.bundle.js) are available. Use in tests to skip when bundle not built. */
+export async function isA2uiAvailable(): Promise {
+ return (await resolveA2uiRootReal()) !== null;
+}
+
function normalizeUrlPath(rawPath: string): string {
const decoded = decodeURIComponent(rawPath || "/");
const normalized = path.posix.normalize(decoded);
diff --git a/src/canvas-host/server.test.ts b/src/canvas-host/server.test.ts
index e460b2630..f6a6cbd0c 100644
--- a/src/canvas-host/server.test.ts
+++ b/src/canvas-host/server.test.ts
@@ -7,7 +7,12 @@ import { describe, expect, it, vi } from "vitest";
import { WebSocket } from "ws";
import { rawDataToString } from "../infra/ws.js";
import { defaultRuntime } from "../runtime.js";
-import { CANVAS_HOST_PATH, CANVAS_WS_PATH, injectCanvasLiveReload } from "./a2ui.js";
+import {
+ CANVAS_HOST_PATH,
+ CANVAS_WS_PATH,
+ injectCanvasLiveReload,
+ isA2uiAvailable,
+} from "./a2ui.js";
import { createCanvasHostHandler, startCanvasHost } from "./server.js";
describe("canvas host", () => {
@@ -201,6 +206,9 @@ describe("canvas host", () => {
}, 20_000);
it("serves the gateway-hosted A2UI scaffold", async () => {
+ if (!(await isA2uiAvailable())) {
+ return; // Skip when A2UI bundle not built (e.g. CI before canvas:a2ui:bundle or path not found)
+ }
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-canvas-"));
const server = await startCanvasHost({