Skip to content

feat: abstract workspace and config I/O behind OpenClawBackend#1

Open
nickmarden wants to merge 5 commits intomainfrom
feat/openclaw-api-backend
Open

feat: abstract workspace and config I/O behind OpenClawBackend#1
nickmarden wants to merge 5 commits intomainfrom
feat/openclaw-api-backend

Conversation

@nickmarden
Copy link
Copy Markdown

@nickmarden nickmarden commented Mar 11, 2026

Summary

Decouples Pinchy's workspace and config operations from the shared filesystem, enabling Pinchy and OpenClaw to run in separate containers without shared volumes (e.g. Kubernetes).

Introduces an OpenClawBackend interface with two implementations:

  • FilesystemBackend (default): reads/writes directly to the shared filesystem. Zero behavioral change for existing deployments.
  • ApiBackend: uses OpenClaw Gateway RPC (agents.files.*, config.*) over WebSocket.

Selected via OPENCLAW_BACKEND env var ("filesystem" or "api").

Commits

1. feat: abstract workspace and config I/O behind OpenClawBackend

Core abstraction. All workspace and config functions are now async and delegate to the backend singleton. All callers updated. FilesystemBackend preserves existing behavior; ApiBackend uses Gateway RPC.

2. fix: ensure OpenClaw knows about agents before workspace writes

In API mode, agents.files.set requires the agent to already exist in OpenClaw's config. Filesystem mode has no such constraint. Agent creation now pushes the OpenClaw config between the DB insert and workspace file writes via an onAgentCreated callback. The migrateExistingSmithers migration is split into DB-only and file-only phases for the same reason.

3. fix: handle config.set restart semantics in API backend

OpenClaw's config.set RPC writes the config and then restarts (SIGTERM from the file watcher), killing the WebSocket before the response arrives. The API backend now treats config.set as fire-and-forget: send the request, then wait for the client to disconnect and reconnect, confirming OpenClaw restarted with the new config.

4. fix: restart OpenClaw when shared agent config or files change

Edits to shared agent settings (personality, instructions, permissions, name) or workspace files now call regenerateOpenClawConfig so changes take effect immediately. Personal agent edits skip the restart to avoid disrupting other users. OpenClaw bakes the system prompt into sessions at creation time, so a restart is required to invalidate stale sessions.

5. fix: make validateGatewayToken async to support DB-stored tokens

In API mode the gateway token lives in the DB, not on the filesystem. validateGatewayToken is now async and reads from DB settings first, falling back to the config file for filesystem mode. The gateway token is persisted to Pinchy's DB on first OpenClaw connection so it's available for regenerateOpenClawConfig and internal route auth without reading the (redacted) config.

Testing

# Default filesystem mode (no behavioral change)
docker compose up --build

# API backend mode
docker compose -f docker-compose.yml -f docker-compose.api.yml up --build

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Refactor
  • Tests
  • Tooling / CI

Checklist

  • I've read the Contributing Guide
  • My code follows the project's style
  • I've added tests for new functionality (if applicable)
  • I've updated the documentation (if applicable)
  • All existing tests pass

@nickmarden
Copy link
Copy Markdown
Author

Note: agent-files.test.ts has some formatting noise because Prettier reformatted the file when the pre-commit hook ran. The actual functional change is just mockImplementationOnce(() => { throw ... })mockRejectedValueOnce(new Error(...)), which is needed because the workspace functions now return promises (so errors need to be rejections, not synchronous throws).

Introduce an OpenClawBackend interface that decouples Pinchy's workspace
and config operations from the shared filesystem. Two implementations:

- FilesystemBackend (default): reads/writes directly to the shared
  filesystem, identical to previous behavior.
- ApiBackend: uses OpenClaw Gateway RPC (agents.files.*, config.*) so
  Pinchy and OpenClaw no longer need a shared volume.

Selected via OPENCLAW_BACKEND env var ("filesystem" or "api").

All workspace and config functions are now async and delegate to the
backend singleton. All callers updated accordingly.
In API backend mode, workspace file writes (agents.files.set) require
OpenClaw to already have the agent in its config. This was not
guaranteed because regenerateOpenClawConfig could run after agent
creation but before the config was pushed.

Changes:
- Add onAgentCreated callback to createSmithersAgent so callers can
  push config between DB insert and workspace writes
- Split migrateExistingSmithers into DB-only and file-only phases so
  the migration can update allowedTools before config push, then write
  workspace files after
- Move regenerateOpenClawConfig before workspace writes in POST
  /api/agents
- Persist gateway token in DB settings so regenerateOpenClawConfig can
  include it in plugin configs without relying on config.get (which
  redacts secrets in API mode)
OpenClaw's config.set RPC writes the config file to disk, but the file
watcher immediately triggers a full process restart (SIGTERM). This
kills the WebSocket connection before the RPC response is sent, leaving
the caller hanging on a promise that never resolves.

Replace the await-response pattern with fire-and-forget: send the
config.set request, then wait for the client to disconnect and
reconnect. Reconnection confirms OpenClaw has restarted with the new
config applied.

Also adds docker-compose.api.yml override and docs for running in API
backend mode.
Editing a shared agent's settings, personality, or instructions now
triggers regenerateOpenClawConfig so OpenClaw restarts and all users'
sessions pick up the changes. Previously only agent creation and
deletion pushed config; updates to existing agents were silent.

Personal agent edits skip the restart to avoid disrupting other users.
The owner starts a new conversation to pick up their changes.
In API mode the gateway token is stored in the DB, not on the
filesystem. Reading it requires an async getSetting call, so
validateGatewayToken must be async. Updated all three internal
route callers and their test mocks accordingly.
@nickmarden nickmarden force-pushed the feat/openclaw-api-backend branch from 53036c8 to e9a9881 Compare March 13, 2026 14:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant