Lightweight HTTP service that exposes OpenClaw workspace files over REST API. This service runs as a sidecar container alongside OpenClaw and provides file access for MosBot OS.
- REST API for workspace file operations (list, read, write, delete)
- Bearer token authentication (required by default)
- Symlink remapping for cross-container paths
- Path traversal protection
- Health check endpoint
- Multi-platform Docker images (amd64, arm64)
This service can read, write, and delete files under the mounted OpenClaw root. Treat it as a privileged internal API.
- Authentication is required —
WORKSPACE_SERVICE_TOKENmust be set. The service will refuse to start without it. - Never expose port 18780 to the public internet — use a VPN, private network, or Kubernetes
ClusterIPservice. - Always use a strong, randomly generated bearer token (
openssl rand -hex 32). - The service runs as a non-root user inside the container.
- Path traversal protection is built-in and cannot be bypassed via the API.
- For normal MosBot usage, mount the OpenClaw root read-write so Projects/Skills/Docs and config edits can succeed.
See SECURITY.md for the full threat model and vulnerability reporting process.
services:
mosbot-workspace:
image: ghcr.io/bymosbot/mosbot-workspace-service:latest
environment:
WORKSPACE_SERVICE_TOKEN: your-secure-token # required
CONFIG_ROOT: /openclaw-config
MAIN_WORKSPACE_DIR: workspace
volumes:
- /path/to/.openclaw:/openclaw-config
ports:
- "18780:18780"docker run -d \
--name mosbot-workspace \
-e WORKSPACE_SERVICE_TOKEN=your-secure-token \
-e CONFIG_ROOT=/openclaw-config \
-e MAIN_WORKSPACE_DIR=workspace \
-v /path/to/.openclaw:/openclaw-config \
-p 18780:18780 \
ghcr.io/bymosbot/mosbot-workspace-service:latestFor full MosBot integration (agent discovery via openclaw.json + Projects/Skills/Docs CRUD), use
a read-write mount for CONFIG_ROOT.
| Variable | Default | Description |
|---|---|---|
PORT |
18780 |
HTTP server port |
CONFIG_ROOT |
/openclaw-config |
Absolute OpenClaw root mount containing config, shared dirs, and agent workspaces |
MAIN_WORKSPACE_DIR |
workspace |
Main workspace directory name under CONFIG_ROOT (single folder name only; no /, \, ., ..) |
WORKSPACE_SERVICE_TOKEN |
— | Required. Bearer token for authentication. The service will not start without this. |
SYMLINK_REMAP_PREFIXES |
/home/node/.openclaw |
Comma-separated list of symlink prefixes to remap (for cross-container symlinks) |
WORKSPACE_SERVICE_ALLOW_ANONYMOUS |
— | Set to true to disable auth requirement. For local development only. Never use in production. |
Removed and no longer honored: WORKSPACE_FS_ROOT, CONFIG_FS_ROOT, WORKSPACE_ROOT,
WORKSPACE_SUBDIR, WORKSPACE_PATH, AUTH_TOKEN.
Given CONFIG_ROOT=/openclaw-config and MAIN_WORKSPACE_DIR=workspace:
- Main workspace filesystem root:
/openclaw-config/workspace - Sub-agent workspaces:
/openclaw-config/workspace-<agent> - Shared directories:
/openclaw-config/projects,/openclaw-config/skills,/openclaw-config/docs
Routing rules:
- Main workspace canonical paths:
/workspaceand/workspace/**(mapped toCONFIG_ROOT/MAIN_WORKSPACE_DIR) - Config-root allowlist:
/openclaw.json,/agents.json,/projects/**,/skills/**,/docs/**,/workspace-<agent>/**, and legacy archive paths such as/_archived_workspace_main/** - All other absolute paths are denied with
403and codePATH_NOT_ALLOWED - Virtual root
/is not allowlisted and is denied with403 PATH_NOT_ALLOWED
Canonical main workspace virtual path is /workspace.
GET /healthReturns service status and configuration. Does not require authentication.
GET /status
Authorization: Bearer <token>Returns workspace accessibility status.
GET /files?path=/workspace&recursive=false
Authorization: Bearer <token>List files and directories. Use recursive=true for recursive listing.
path=/ (or omitted path) is denied with 403 PATH_NOT_ALLOWED.
GET /files/content?path=/path/to/file&encoding=utf8
Authorization: Bearer <token>Read file content.
POST /files
Authorization: Bearer <token>
Content-Type: application/json
{
"path": "/path/to/file",
"content": "file content",
"encoding": "utf8"
}PUT /files
Authorization: Bearer <token>
Content-Type: application/json
{
"path": "/path/to/file",
"content": "updated content",
"encoding": "utf8"
}DELETE /files?path=/path/to/file
Authorization: Bearer <token>GET /links/:type/:agentId
Authorization: Bearer <token>Returns per-agent link state for supported types.
- Supported
type:docs agentId:mainmaps toMAIN_WORKSPACE_DIR- any other valid slug maps to
workspace-<agentId>
- Valid states:
linkedmissingconflict(includesconflict.reason, andconflict.symlinkTargetwhen relevant)
PUT /links/:type/:agentId
Authorization: Bearer <token>For type=docs:
- ensures
CONFIG_ROOT/docsexists - ensures target workspace directory exists
- creates a managed
docssymlink only when missing - returns
action: "created"oraction: "unchanged" - returns
409 LINK_CONFLICTfor non-managed/conflicting existing paths
DELETE /links/:type/:agentId
Authorization: Bearer <token>For type=docs:
- removes only the managed symlink targeting
CONFIG_ROOT/docs - returns
action: "deleted"oraction: "unchanged"(when already missing) - returns
409 LINK_CONFLICTfor non-managed/conflicting paths
Error codes:
LINK_TYPE_UNSUPPORTEDfor unsupported:typeINVALID_AGENT_IDfor invalid:agentIdLINK_CONFLICTfor conflicting existing paths
npm install
cp .env.example .env
# Edit .env and set WORKSPACE_SERVICE_TOKEN
npm startAlternatively, export variables directly in your shell:
export WORKSPACE_SERVICE_TOKEN=dev-token-change-me
npm startFor local development without a token (not for production):
# In .env:
WORKSPACE_SERVICE_ALLOW_ANONYMOUS=true
# Or via shell:
export WORKSPACE_SERVICE_ALLOW_ANONYMOUS=true
npm startnpm run test:run # run once
npm run test:coverage # run with 100% coverage enforcementnpm run format # auto-fix
npm run format:check # check only (used in CI)docker build -t mosbot-workspace-service:latest .- MosBot API — Backend API that consumes this service
- MosBot Dashboard — Frontend UI
- MosBot OS Documentation — Full system documentation