Files
beaver_project/docs/superpowers/specs/2026-06-02-openclaw-sidecar-connectors-design.md

12 KiB

OpenClaw Sidecar Connectors Design

Date: 2026-06-02

Goal

Add real Weixin personal-account QR login and Feishu/Lark OpenClaw plugin onboarding to Beaver through a docker-compose predeclared sidecar service. Beaver must not dynamically create containers or require Docker socket access.

This design implements the next connector layer after docs/superpowers/plans/2026-06-02-channel-connectors-foundation.md.

Sources

  • Tencent openclaw-weixin supports Weixin QR login and persists login credentials after scan confirmation: https://github.com/Tencent/openclaw-weixin
  • The Weixin ClawBot install flow shown by the user uses npx -y @tencent-weixin/openclaw-weixin-cli@latest install.
  • Feishu's OpenClaw article documents the official Lark/Feishu plugin install command npx -y @larksuite/openclaw-lark install, bot creation/linking, /feishu start verification, and user-vs-bot identity modes: https://www.feishu.cn/content/article/7613711414611463386

Scope

Included:

  • A repo-local openclaw-connector sidecar service.
  • A docker-compose service declaration for the sidecar.
  • Sidecar HTTP API for health, connector metadata, pairing/install status, logout/remove, outbound send, and inbound event forwarding.
  • Beaver WeixinConnector and FeishuConnector objects registered in ChannelConnectorRegistry.
  • Beaver connector bridge endpoints that accept normalized sidecar inbound events and submit them to ChannelRuntime.accept_inbound().
  • ExternalConnectorChannel runtime object for sidecar-backed outbound sends.
  • Web UI connection wizard for Weixin QR login and Feishu/Lark plugin onboarding.
  • Unit tests using fake sidecar clients and bridge events.

Excluded:

  • Dynamic Docker container creation from Beaver.
  • Docker socket mounts in Beaver.
  • Reimplementing Weixin iLink or Feishu OpenClaw protocol inside Beaver.
  • Building a generic plugin marketplace.
  • Hot-swapping running adapters without backend restart. This phase may create/update connections and require a Beaver app restart for runtime materialization unless explicitly handled by the existing runtime.
  • Multi-user enterprise permission governance beyond local connector ownership and bridge token validation.

Architecture

Use one predeclared sidecar for OpenClaw-backed platform connectors:

Beaver backend
-> OpenClawConnector HTTP client
-> openclaw-connector sidecar
-> OpenClaw CLI/runtime
-> @tencent-weixin/openclaw-weixin
-> @larksuite/openclaw-lark

Beaver owns:

  • connection state in ChannelConnectionStore
  • credential references in CredentialStore
  • bridge endpoint authentication
  • normalized runtime message admission
  • runtime dedupe/session identity
  • outbound dispatch into sidecar /send

The sidecar owns:

  • OpenClaw installation/runtime state
  • plugin install/update commands
  • Weixin QR login and login-state persistence
  • Feishu/Lark plugin install, bot creation/linking, and OpenClaw-side verification
  • platform receive loops
  • sidecar-to-Beaver inbound event delivery

Runtime Flow

Inbound:

Weixin or Feishu/Lark platform event
-> OpenClaw plugin inside sidecar
-> sidecar normalized event
-> POST Beaver /api/channel-connector-bridge/events
-> ChannelRuntime.accept_inbound()
-> MessageBus
-> AgentService

Outbound:

AgentService
-> MessageBus outbound
-> ChannelManager.dispatch_outbound()
-> ExternalConnectorChannel.send()
-> POST sidecar /send
-> OpenClaw plugin send path
-> Weixin or Feishu/Lark platform

ExternalConnectorChannel implements the existing runtime channel protocol:

channel_id: str
kind: str
mode: str

async def start() -> None
async def stop() -> None
async def send(message: OutboundMessage) -> None

It is not a platform protocol adapter. It is a generic HTTP bridge to a sidecar.

Runtime materialization for sidecar-backed connections always emits:

ChannelConfig(
    enabled=True,
    kind="external_connector",
    mode="http",
    account_id=spec.account_id,
    display_name=spec.display_name,
    config={
        "platformKind": "weixin",
        "connectionId": "conn_...",
        "sidecarBaseUrl": "http://openclaw-connector:8787",
    },
    secrets={"bridgeToken": "..."},
)

The original ChannelConnection.kind remains weixin or feishu; only the runtime transport kind is generic.

Sidecar Deployment

Add a sidecar service that can be enabled in deployment:

services:
  openclaw-connector:
    build: ./openclaw-connector
    restart: unless-stopped
    environment:
      BEAVER_BRIDGE_BASE_URL: http://app-instance:8080
      BEAVER_BRIDGE_TOKEN: ${BEAVER_BRIDGE_TOKEN}
      OPENCLAW_HOME: /var/lib/openclaw
    volumes:
      - openclaw-connector-state:/var/lib/openclaw

For the current create-instance.sh-style deployment, the implementation adds:

  • docker-compose.openclaw.yml for local/development sidecar tests.
  • documentation for attaching openclaw-connector to the same Docker network as the target app instance.
  • instance environment OPENCLAW_CONNECTOR_BASE_URL=http://openclaw-connector:8787.

The implementation must not depend on Beaver mounting /var/run/docker.sock.

Sidecar HTTP API

All sidecar requests and responses are JSON. The sidecar listens on port 8787.

GET  /health
GET  /connectors
POST /pairings
GET  /pairings/{pairing_id}
POST /pairings/{pairing_id}/cancel
POST /connections/{connection_id}/logout
POST /send

GET /connectors returns:

[
  {
    "kind": "weixin",
    "displayName": "Weixin",
    "authType": "qr",
    "capabilities": ["receive_text", "send_text", "receive_media", "direct_messages"]
  },
  {
    "kind": "feishu",
    "displayName": "Feishu/Lark",
    "authType": "openclaw_plugin",
    "capabilities": ["receive_text", "send_text", "receive_media", "groups"]
  }
]

POST /pairings request:

{
  "kind": "weixin",
  "connectionId": "conn_...",
  "channelId": "weixin-main",
  "displayName": "Weixin Main",
  "callbackBaseUrl": "http://app-instance:8080",
  "bridgeToken": "..."
}

For Feishu/Lark, kind is feishu and the request may include domain, mode, and optional app credentials when linking an existing bot. If using the OpenClaw official installer to create a bot, the sidecar starts that installer flow and reports QR or action status back to Beaver.

GET /pairings/{pairing_id} response:

{
  "pairingId": "pair_...",
  "kind": "weixin",
  "status": "pending",
  "qrCode": "weixin://...",
  "qrImage": "data:image/png;base64,...",
  "accountId": null,
  "displayName": null,
  "error": null,
  "metadata": {}
}

Allowed pairing statuses:

  • pending
  • qr_ready
  • scanned
  • confirmed
  • connected
  • expired
  • error
  • cancelled

POST /send request:

{
  "connectionId": "conn_...",
  "channelId": "weixin-main",
  "kind": "weixin",
  "target": {
    "peerId": "wx_user_or_chat_id",
    "peerType": "dm",
    "threadId": null
  },
  "content": "reply text",
  "metadata": {
    "contextToken": "optional"
  }
}

Beaver Bridge API

Add a backend bridge endpoint for sidecar inbound messages:

POST /api/channel-connector-bridge/events

The sidecar must authenticate every bridge request using a bearer token scoped to the connector service. Beaver rejects missing or invalid bridge tokens.

Bridge event body:

{
  "connectionId": "conn_...",
  "channelId": "weixin-main",
  "kind": "weixin",
  "accountId": "weixin:...",
  "peerId": "wx_user_or_chat_id",
  "peerType": "dm",
  "userId": "wx_sender",
  "threadId": null,
  "messageId": "platform-message-id",
  "messageType": "text",
  "content": "hello",
  "metadata": {
    "contextToken": "optional"
  }
}

The bridge endpoint constructs ChannelIdentity, then InboundMessage, then calls ChannelRuntime.accept_inbound().

Beaver Connectors

WeixinConnector

Responsibilities:

  • discover sidecar health
  • start Weixin pairing through sidecar /pairings
  • poll sidecar pairing status
  • create or update ChannelConnection
  • store sidecar connection token or state reference in CredentialStore
  • validate by checking sidecar connection status
  • materialize runtime config for ExternalConnectorChannel
  • revoke/logout by calling sidecar /connections/{connection_id}/logout

FeishuConnector

Responsibilities:

  • discover sidecar health
  • start Feishu/Lark OpenClaw plugin install/link flow
  • optionally pass appId/appSecret/domain/mode for existing bot linking
  • poll installer/pairing status
  • create or update ChannelConnection
  • validate by sidecar /pairings/{id} or connector status
  • materialize runtime config for ExternalConnectorChannel
  • revoke/remove plugin connection by calling sidecar logout/remove API

Feishu is sidecar-backed in this design because the user's supplied Feishu article describes the official OpenClaw plugin flow, not only a static bot-credential runtime adapter.

Frontend

Replace the old static Weixin fields with connector-driven UI:

  • fetch GET /api/channel-connectors
  • show Telegram, Weixin, and Feishu/Lark as connector options
  • for Weixin:
    • start pairing
    • show QR image
    • poll status until connected/expired/error
    • show connected account and logout
  • for Feishu/Lark:
    • choose create bot or link existing bot
    • collect domain and optional app credentials
    • start sidecar pairing/install
    • show QR/instructions/status returned by sidecar
    • show connected account and logout

The old /api/channels static config editor may remain for advanced runtime config, but connector onboarding should not rely on manual JSON editing or direct token entry for Weixin/Feishu.

Error Handling

  • Sidecar unavailable: show connector as unavailable; do not create a running connection.
  • OpenClaw install command fails: status error, with redacted stderr summary.
  • QR expired: status expired, user can start a new pairing.
  • Bridge token invalid: reject with 401, record event without platform secret values.
  • Unknown connection id in bridge event: reject with 404.
  • Outbound send failure: mark outbound delivery failed and record connector error.
  • Sidecar restart: persisted OpenClaw state should survive through sidecar volume.

Security

  • Beaver never logs raw tokens, app secrets, or sidecar connection tokens.
  • Bridge token is generated by Beaver and stored behind credentials_ref.
  • Sidecar can only call bridge endpoints with its bridge token.
  • Sidecar state volume contains OpenClaw login state and must be treated as sensitive.
  • Feishu user-identity mode has stronger privacy risk than bot-identity mode; UI must label it clearly if exposed.

Testing

Backend unit tests:

  • sidecar client fake for Weixin pairing start/status/logout/send
  • sidecar client fake for Feishu pairing start/status/logout/send
  • ExternalConnectorChannel.send() target mapping
  • bridge endpoint accepts valid events and rejects invalid token/connection id
  • registry lists telegram, weixin, and feishu
  • materialized sidecar connections produce ChannelConfig(kind="external_connector", mode="http") compatible with runtime factory

Sidecar tests:

  • HTTP API shape for health/connectors/pairings/send
  • fake OpenClaw provider status transitions
  • command runner error redaction

Frontend tests:

  • Weixin connector option opens QR modal
  • polling reaches connected state
  • expired/error states are visible
  • Feishu flow starts install/link and shows returned instructions/status

Manual verification:

  • Build app and sidecar Docker images.
  • Start docker-compose sidecar setup.
  • In terminaltest, open Weixin connector, scan QR, observe connected status.
  • Send a Weixin text message and verify Beaver receives it.
  • Send a Beaver reply and verify sidecar /send path.
  • Start Feishu connector flow using official OpenClaw Lark plugin install path and verify /feishu start.

Rollout

Implement in this order:

  1. Sidecar HTTP contract with fake provider.
  2. Beaver ExternalConnectorChannel and bridge endpoint.
  3. Weixin connector against fake sidecar client.
  4. Feishu connector against fake sidecar client.
  5. Frontend connector UI.
  6. Real sidecar provider that shells out to OpenClaw/OpenClaw plugin commands.
  7. Docker build/compose integration.
  8. Manual live verification.

The fake provider is test-only. The production sidecar provider must use real OpenClaw plugin commands for Weixin and Feishu/Lark; the fake provider only makes Beaver and frontend tests deterministic while the live provider handles the non-deterministic external login flow.