squad-openclaw
v2026.2.2708
Published
Entity registry, filesystem tools, and version management plugin for OpenClaw gateway
Maintainers
Readme
squad-openclaw
OpenClaw gateway plugin for Squad — provides entity registry, locked-down filesystem tools, agent setup wrappers, plugin safety controls, and Tailnet internal routes for onboarding helpers.
Features
| Tool / Method / Endpoint | Description |
|---|---|
| entity_list, entity_search, entity_sync | In-memory entity registry with filesystem watching (agents, skills, plugins, tools, media) |
| fs_read, fs_write, fs_list, fs_delete, fs_rename, fs_mkdir | Remote filesystem access for browser clients (subject to security restrictions below) |
| squad.agents.add | Plugin wrapper for creating agent scaffolding (workspace seed files, sessions skeleton, and config list entry) |
| squad.version.check | Plugin version reporting |
| squad.questions.validate-envelope | HUMAN_INPUT_REQUIRED envelope validation |
| squad.extensions.list, squad.extensions.update, squad.extensions.updateStatus | Installed extension metadata, cached version checks, and update lifecycle status |
| squad.gateway.restart | Trigger gateway restart for extension-update activation |
| tools.invoke, tools.list, squad.layout.get | Core plugin RPC entrypoints for tool invocation/listing and gateway layout metadata |
| squad.plugin.status, squad.plugin.recover, squad.plugin.disable | Plugin safety-state RPC control (status, recovery, manual disable) |
| GET /squad-internal/health | Tailnet internal health + pairing capability metadata |
| GET /squad-internal/plugin/status | Tailnet internal plugin safety-state status |
| POST /squad-internal/plugin/recover, POST /squad-internal/plugin/disable | Tailnet internal plugin safety-state controls |
| POST /squad-internal/pairing/request, GET /squad-internal/pairing/status | Tailnet internal pairing helper routes with origin/Tailnet/browser-proof checks |
Internal Endpoint Reference
All /squad-internal/* routes enforce Tailnet context and origin checks. CORS preflight (OPTIONS) is handled for these routes.
| Route | Method | Purpose |
|---|---|---|
| /squad-internal/health | GET | Returns plugin safety snapshot and pairing capability hints |
| /squad-internal/plugin/status | GET | Returns current plugin safety state |
| /squad-internal/plugin/recover | POST | Recovers plugin safety state back to active (unless env kill-switch is set) |
| /squad-internal/plugin/disable | POST | Manually disables plugin safety state |
| /squad-internal/pairing/request | POST | Creates pairing request via gateway-native pairing methods (node/devices/device.pair.request) |
| /squad-internal/pairing/status | GET | Resolves pairing status via gateway-native status methods (node/devices/device.pair.status/get) |
Extension-management routes intentionally do not expose install commands. Only update/status/restart are available via RPC methods.
State Directory Resolution
All paths in this plugin (and throughout this README) that reference ~/.openclaw resolve via environment override when set. This supports Docker and other containerized deployments where the OpenClaw data directory may not be at the default location.
Resolution priority:
OPENCLAW_STATE_DIR(canonical)OPENCLAW_DIR(compat alias)os.homedir() + "/.openclaw"(default)
| Environment | Typical path | How it's resolved |
|---|---|---|
| Standard install | ~/.openclaw | Default — os.homedir() + "/.openclaw" |
| Docker | /data/.openclaw | OPENCLAW_STATE_DIR=/data/.openclaw (or OPENCLAW_DIR=/data/.openclaw) |
| Custom / NAS | /mnt/data/.openclaw | OPENCLAW_STATE_DIR=/mnt/data/.openclaw |
This variable only controls where the plugin looks for OpenClaw's own data directory. It does not grant the plugin access to the parent directory or any other part of the filesystem. All security restrictions (blocked directories, allowed roots, write protection) are enforced relative to the resolved state directory — not the filesystem root.
The resolution logic lives in a single shared module (src/paths.ts) imported by every file that needs the state directory path.
Docker Workspace Mapping
If your container mounts the real workspace outside the OpenClaw state directory (for example, /data/workspace), create a symlink so OpenClaw-compatible tools can still resolve .../.openclaw/workspace:
mkdir -p /data/.openclaw /data/workspace
ln -sfn /data/workspace /data/.openclaw/workspaceThen start your gateway/server with explicit env vars (useful when .env is not loaded):
PORT=3334 \
OPENCLAW_STATE_DIR=/data/.openclaw \
OPENCLAW_DIR=/data/.openclaw \
node server.jsQuick verification:
ls -ld /data/.openclaw /data/.openclaw/workspace /data/workspace /data/.openclaw/openclaw.jsonSecurity Model
This plugin enforces a defense-in-depth security model with four independent layers. All security rules are hard-coded and non-configurable (except allowedRoots) so they can be verified by reading the source code. The bundle is intentionally not minified to allow security auditing of the distributed code.
Note: Throughout this section,
~/.openclawrefers to the resolved state directory (see above). In Docker or custom installs, substitute the actual path set byOPENCLAW_STATE_DIR.
Layer 1: Blocked Directories (hardcoded, non-configurable)
These directories are completely blocked from all filesystem operations (read, write, list, delete, rename):
| Path | Contents |
|---|---|
| ~/.openclaw/credentials/ | OAuth tokens, API keys |
| ~/.openclaw/devices/ | Device pairing secrets, private keys |
| ~/.openclaw/identity/ | Operator identity material |
Layer 1b: Blocked Files (hardcoded, non-configurable)
| Path | Reason |
|---|---|
| ~/.openclaw/squad-ceo-data/relay/squad-relay.json | Legacy relay-state material from older transport flows (blocked to prevent accidental secret disclosure) |
| ~/.openclaw/*.bak | Backup files at the top level contain unredacted config (tokens, keys) that would bypass redaction |
Layer 2: Redacted Files (hardcoded, non-configurable)
~/.openclaw/openclaw.json is readable but with sensitive fields replaced with "[REDACTED]" before returning to clients:
channels.*.botToken— channel bot tokensgateway.auth.*— all auth keysgateway.token— legacy token locationgateway.remote.token— legacy remote token
Layer 3: Allowed Roots (configurable, defaults to ~/.openclaw)
Filesystem operations are restricted to configured root directories. By default, only ~/.openclaw/ is accessible — covering all application needs:
~/.openclaw/squad-ceo-data/— entity databases, data files~/.openclaw/media/— asset uploads~/.openclaw/workspace*/— agent workspaces~/.openclaw/skills/— skill definitions~/.openclaw/extensions/— plugin manifests
Operators can customize via the fs.allowedRoots config option.
Layer 4: Filesystem Write Protection
These files/directories cannot be written via filesystem tools (fs_write, fs_rename, etc.), even if they fall within allowedRoots:
~/.openclaw/openclaw.json— operator configuration (tool-level read-only with redaction)~/.openclaw/squad-ceo-data/relay/squad-relay.json— legacy relay-state secret file (blocked)- All blocked directories above (credentials, devices, identity)
- All
.bakfiles at~/.openclaw/top level
Startup Config Migration (one-time, localized)
On plugin startup, Squad runs an internal migration harness. Current migration behavior:
- Checks
~/.openclaw/squad-ceo-data/migrations.jsonfor completed migration IDs. - If
001-enable-main-subagent-accessis not recorded, applies a localizedconfig.patchthat sets only:agents.defaults.maxConcurrent = 4agents.defaults.subagents.maxConcurrent = 8agents.list[id=main].identity.name = "Pepper"agents.list[id=main].tools.allow = ["*"]agents.list[id=main].subagents.allowAgents = ["*"]
- Records completion in
~/.openclaw/squad-ceo-data/migrations.json.
This migration is designed to run once. If an operator later changes these values manually, the plugin does not overwrite them again because the migration is already marked complete.
Plugin Safety State (Kill Switch + Quarantine)
The plugin persists runtime safety state in:
~/.openclaw/squad-ceo-data/safety/plugin-state.json
State values:
ACTIVEDISABLED_MANUALQUARANTINED_AUTO
Behavior:
- If disabled/quarantined, plugin methods return typed errors (
PLUGIN_DISABLED/PLUGIN_QUARANTINED) instead of crashing startup/runtime. - Quarantine auto-expires after a cooldown window and transitions back to
ACTIVE. - Environment kill switch overrides persisted state and is authoritative.
Environment controls:
SQUAD_PLUGIN_DISABLED=1— force disable pluginSQUAD_PLUGIN_DISABLE_REASON="..."— optional operator-facing reasonSQUAD_PLUGIN_FAILURE_THRESHOLD— failures before auto-quarantine (default3)SQUAD_PLUGIN_FAILURE_WINDOW_MS— failure counting window (default300000)SQUAD_PLUGIN_QUARANTINE_MS— quarantine duration (default600000)
Recovery paths:
- RPC:
openclaw gateway call squad.plugin.recover --json - HTTP (Tailnet):
POST /squad-internal/plugin/recover - If env kill switch is active, unset
SQUAD_PLUGIN_DISABLEDand restart gateway.
Tailnet Pairing and Locator Security
The active onboarding path is Tailnet-direct and gateway-owned:
- Browser connects directly to
wss://<gateway>.ts.net - OpenClaw gateway enforces pairing approval state
- Relay service is used for account auth and locator metadata only
Pairing Model (Current)
Pairing does not rely on plugin-owned approval state:
- Browser receives gateway
connect.challenge - Browser signs challenge with its per-browser device key
- Browser sends native
connectrequest - If unpaired, gateway returns pairing-required error (with
requestIdwhen available) - UI shows
openclaw devices approve <requestId> - UI polls reconnect automatically in the background until approved
This means approval is native to gateway pairing and not stored in plugin-local state.
Plugin Route Security (/squad-internal/pairing/*)
Pairing helper routes enforce:
- Allowed browser origin (
SQUAD_ALLOWED_ORIGINSor built-in defaults) - Tailnet context (Tailnet headers/hostname checks)
- Browser proof (device ID/public key/signature/nonce/signedAt) on pairing request creation
- Nonce freshness and per-device/IP rate limits
Pairing Helper Routes (Compatibility)
/squad-internal/pairing/request and /squad-internal/pairing/status are still present for compatibility, but the current SPA pairing flow no longer depends on them as primary path.
POST /squad-internal/pairing/request expects browser proof fields:
deviceIdpublicKey(Ed25519) orpublicKeyJwk(P-256)signaturenoncesignedAt
GET /squad-internal/pairing/status expects:
requestIdquery paramdeviceIdquery param
Legacy Relay-State File Handling
~/.openclaw/squad-ceo-data/relay/squad-relay.json is still blocked by filesystem security rules to protect historical secrets from older relay transport versions.
Remote Tool Invocation (tools.invoke)
The tools.invoke gateway method allows the browser to call plugin tools over WebSocket transport when direct HTTP tool calls are unavailable. This is not a generic RPC gateway — it is scoped exclusively to the tools registered by this plugin:
| Tool | What it can access | Restrictions |
|---|---|---|
| fs_read, fs_write, fs_list, fs_delete, fs_rename, fs_mkdir | ~/.openclaw/ only | All 4 security layers apply (blocked dirs, blocked files, redaction, allowed roots, write protection) |
| entity_list, entity_search, entity_sync | In-memory entity index | Read-only metadata (names, types, paths) |
It cannot invoke gateway core tools (exec, bash, read, write, web_fetch, etc.) — only the tools this plugin registers via api.registerTool(). Every invoked tool enforces its own security restrictions independently — tools.invoke is just a transport layer, not a privilege escalation.
Build Transparency
The build configuration (tsup.config.ts) is optimized for security auditing:
| Setting | Value | Reason |
|---|---|---|
| minify | false | Unminified bundle for human/AI security review |
| sourcemap | false | No internal path exposure |
| treeshake | false | All code preserved for complete auditing |
Configuration
Configure in your gateway's openclaw.json under the plugin section:
| Key | Type | Default | Description |
|---|---|---|---|
| fs.allowedRoots | string[] | [resolved OpenClaw state dir] | Restrict filesystem operations to these directories. Hardcoded blocks on credentials/, devices/, identity/, legacy relay/squad-relay.json, and .bak files always apply regardless. |
Source Code
- Repository: github.com/WorldBrain/squad
- Plugin directory:
extensions/squad-openclaw/ - Security-critical files:
src/filesystem.ts— path blocking, redaction, write protectionsrc/agents.ts— wrapper for agent setup (workspace/session skeleton + config entry update)src/http-routes.ts— Tailnet internal routes and browser-proof validationsrc/shared-api.ts— gateway method + tool registration boundariessrc/plugin-safety-gateway.ts,src/plugin-safety-state.ts— plugin safety-state control and persistence
License
MIT
