@goplausible/regent-plugin-openclaw
v1.0.3
Published
OpenClaw Channel Plugin — connects OpenClaw gateway to Regent peers over WebRTC
Downloads
428
Readme
@goplausible/regent-plugin-openclaw
| Component | Version |
| --- | --- |
| @goplausible/regent-plugin-openclaw (this) | 1.0.3 |
| @goplausible/regent-sdk (resolved from npm) | ^1.0.0 |
| regent (matching controller wallet) | 1.1.21 |
The plugin is published on npm. See Plugin lifecycle for install/update/uninstall and Post-install setup for the one-shot config command you run after every install or update.
See version history for the full per-release changelog.
OpenClaw Channel Plugin that connects an OpenClaw gateway to a Regent controller (peer) over Liquid Auth signaling + WebRTC. Built on top of @goplausible/regent-sdk.
A controller (e.g. a wallet client) pairs with this OpenClaw agent once. After that the link is self-healing: the plugin auto-listens on startup and auto-relistens after any disconnect. Detection runs on five layers — dataChannel.onclose, iceConnectionState ∈ {failed, closed}, an 8 s iceConnectionState=disconnected stall, a 60 s inbound-activity watchdog (catches suspended-peer cases where ICE state never transitions), and a sub-second signaling.signalClient.disconnect/error event subscription on the agent's WebSocket. The retry loop runs with bounded backoff ([500 ms, 1 s, 2 s, 5 s], capped at 5 s) so the listener is in the DO room ≥99 % of the time.
The controller persists its requestId for 7 days and reconnects automatically. One controller per plugin instance — the controller may be paired with many other agents in parallel, but each agent sees one controller.
Install & setup
Experimental — preview. This OpenClaw plugin and the Regent SDK are experimental. Issues and frictions are expected — please send issues on GitHub.
# Step 1: Install or update the plugin (from npm registry)
openclaw plugins install @goplausible/regent-plugin-openclaw
# OR
openclaw plugins update regent-plugin-openclaw
# Step 2: Restart the gateway
openclaw gateway restart
# Step 3: Launch interactive setup wizard (enables/disables x402 toggle)
openclaw regent setup
# Step 4: Restart the gateway
openclaw gateway restartCommands
Slash (/regent …) — inside an OpenClaw session
| Command | What it does |
|---|---|
| /regent pair | Show a QR + deep link for the controller to scan. First-time pairing if unpaired; rotates to a fresh requestId if paired-but-offline (replaces the removed connect). Returns Already online (peer=…) if a session is currently active. |
| /regent pair cancel | Abort an in-flight pairing invitation. |
| /regent status | Show pairing record, online state, signaling server, agent DID. |
| /regent forget | Drop the pairing record from disk; channel returns to unpaired. |
Shell (openclaw regent …)
The shell CLI exits as soon as the action returns, which would tear down the pairing WebSocket — so pair is only available as a slash command. The shell surface is intentionally read/write-once:
openclaw regent # full command reference (bare invocation prints help)
openclaw regent setup # apply OpenClaw config required for the plugin (idempotent)
openclaw regent status # prints state + exits
openclaw regent forget # drops pairing + exits
openclaw regent version # plugin + bundled SDK versions
openclaw regent helpdoes NOT work — OpenClaw's CLI parser interceptshelpas a special token before our handler runs. Use bareopenclaw regentinstead.
Plugin lifecycle
Install/update require a gateway restart so OpenClaw reloads the package, then
openclaw regent setup, then one more restart so the generated config is active.
Install
openclaw plugins install @goplausible/regent-plugin-openclaw
openclaw gateway restart
openclaw regent setup
openclaw gateway restartUpdate
openclaw plugins update regent-plugin-openclaw
openclaw gateway restart
openclaw regent setup
openclaw gateway restartUninstall
openclaw plugins uninstall regent-plugin-openclaw
cd ~ && openclaw gateway restartUninstall does NOT remove the plugin's on-disk state at
~/.openclaw/plugins/regent-plugin-openclaw/(peer record, agent keypair, pending invitations, active-session marker). If you reinstall later, the peer record auto-resumes pairing. To wipe everything:openclaw plugins uninstall regent-plugin-openclaw rm -rf ~/.openclaw/plugins/regent-plugin-openclaw cd ~ && openclaw gateway restartUninstall also leaves the openclaw.json config entries (plugins.allow, channels.regent, bindings, tools.alsoAllow) — these are harmless and
openclaw regent setupis idempotent if you reinstall.
Post-install setup
openclaw regent setup is idempotent — safe to re-run after every install/update. It applies the 5 OpenClaw config changes required for the plugin to function:
| Setting | What it does |
|---|---|
| plugins.allow += "regent-plugin-openclaw" | Marks the plugin as explicitly trusted (suppresses untracked-code warnings). |
| plugins.entries.regent-plugin-openclaw.enabled = true | Enables the plugin entry. |
| channels.regent.liquidAuthServer = https://liquidauth.goplausible.xyz | Picks the default signaling server (override in openclaw.json if you self-host). |
| bindings += { agentId: "main", match: { channel: "regent" } } | Routes the main agent to Regent inbound. Adjust agentId if you use a non-default agent. |
| tools.alsoAllow += [10 × regent_* tool names] | Exposes the agent-callable signing/verification tools regardless of the active tools.profile (coding/messaging/minimal/full). tools.alsoAllow bypasses profile filtering; tools.allow does not. |
Each setting prints ✓ applied, = already in place, or ✗ failed so you see exactly what changed.
For live plugin diagnostics (handshake events, ICE state changes, auto-relisten attempts, etc.), run the OpenClaw gateway in the foreground — its stdout carries the [regent] … traces directly. The structured-logger subset (plugin registration, command results) also lands in /tmp/openclaw/openclaw.log if you prefer to tail a file.
connect and disconnect were removed in v0.0.19 — auto-listen-on-startup + auto-relisten cover the recovery story; pair covers the rotate-requestId case formerly served by connect.
Agent tools
The plugin registers 12 agent-callable tools via api.registerTool. One discovery tool (regent_capabilities — handshake-based, no signing), one sign tool (HITL through the paired controller), one proactive send tool (regent_send), and nine local-only verifiers covering the Regent spec's full sig_hint matrix:
| Tool | sig_hint | Behavior |
|---|---|---|
| regent_capabilities | (n/a) | Sends regent/CapabilityRequest to the paired controller and returns its descriptor: version, features, signing identities (public keys / DIDs), and on-chain accounts grouped by blockchain. Pure discovery — no approval modal, no signing. Call before regent_sign to pick a real address and a wallet-supported sig_hint. |
| regent_sign | (all) | Sends regent/SigningRequest to the paired controller. Controller (e.g. Regent) shows an approval modal, gates on passkey/biometric, signs with the user-selected key, and returns signature + publicKey (+ optional address, keyType). HITL — never autonomous. |
| regent_send | (n/a) | Proactively sends a chat message to the paired wallet user without waiting for an inbound message — for scheduled/heartbeat/cron turns or unprompted notes mid-task. Replies to an inbound message do not need this (they auto-deliver). Fire-and-forget; reaches the user via FCM push when the WebRTC link is down. |
| regent_verify_raw_ed25519 | raw-ed25519 | Local Ed25519 verify via @noble/curves. Accepts raw-bytes pubkey or did:key:z…. |
| regent_verify_raw_secp256k1 | raw-secp256k1 | Local secp256k1 verify via @noble/curves. |
| regent_verify_message_algorand | message-algorand | ARC-60 message verify (Ed25519 with MX prefix). |
| regent_verify_message_evm | message-evm | EIP-191 personal-sign verify (secp256k1 ecrecover via viem). |
| regent_verify_message_solana | message-solana | Ed25519 message verify against a Solana address. |
| regent_verify_typed_data_evm | typed-data-evm | EIP-712 typed-data verify via viem. |
| regent_verify_transaction_algorand | transaction-algorand | Decodes SignedTxn msgpack via algosdk, Ed25519-verifies against the sender. |
| regent_verify_transaction_evm | transaction-evm | viem.recoverTransactionAddress — handles EIP-1559 / 2930 / legacy / 4844. |
| regent_verify_transaction_solana | transaction-solana | Legacy + versioned transactions; Ed25519 against static-account-key signer slots. |
All inputs/outputs are base64. Verifiers are pure-local (no network calls); the chain SDKs (algosdk, viem, @solana/web3.js) are used as math libraries only. See src/tools.ts for the typebox parameter schemas and specs/regent.md §regent/SigningRequest for the spec-level display_hint × sig_hint × key_type matrix.
Native approvals → wallet
When OpenClaw needs to approve a tool call — an exec approval (a shell/host command gated by OpenClaw's policy) or a plugin approval (a before_tool_call requireApproval from any plugin) — the Regent channel is a native approval surface: the pending approval is delivered to the paired wallet as an approve/deny card (the same Reply "yes …" format Regent already renders), the user taps approve/deny, and the verdict resolves the approval over the gateway. The tool call blocks until then; timeout/blocking are OpenClaw's, not ours. Implemented in src/approvals.ts via the channel's approvalCapability.render.
Routing. By default OpenClaw forwards approvals to the session origin (approvals.exec.mode/approvals.plugin.mode default "session"), so an approval raised during a turn that came in over Regent (i.e. the wallet user messaged the agent) routes straight back to that wallet with no config. To also route approvals from turns that did not originate over Regent (e.g. a scheduled/cron turn), add an explicit target and set the mode to "both":
{
"approvals": {
"exec": { "mode": "both", "targets": [{ "channel": "regent", "to": "<paired-peer-did>" }] },
"plugin": { "mode": "both", "targets": [{ "channel": "regent", "to": "<paired-peer-did>" }] }
}
}The <paired-peer-did> is the wallet's peer DID (see /regent status). Only the paired wallet is authorized to approve. When the WebRTC link is down the card fails over via FCM; the verdict returns once the wallet reconnects.
Configuration
openclaw regent setup writes the canonical config (under channels.regent). Manual overrides go into the same place. Example of the full shape after setup, in ~/.openclaw/openclaw.json:
{
"plugins": {
"allow": ["regent-plugin-openclaw"],
"entries": {
"regent-plugin-openclaw": { "enabled": true }
}
},
"channels": {
"regent": {
"enabled": true,
"liquidAuthServer": "https://liquidauth.goplausible.xyz",
"agentDid": "did:web:openclaw.local"
}
},
"bindings": [
{ "agentId": "main", "match": { "channel": "regent" } }
],
"tools": {
"profile": "coding",
"alsoAllow": [
"regent_sign",
"regent_verify_raw_ed25519",
"regent_verify_raw_secp256k1",
"regent_verify_message_algorand",
"regent_verify_message_evm",
"regent_verify_message_solana",
"regent_verify_typed_data_evm",
"regent_verify_transaction_algorand",
"regent_verify_transaction_evm",
"regent_verify_transaction_solana",
"regent_capabilities"
]
}
}Two fields (liquidAuthServer, agentDid) are read by the plugin at runtime. Precedence:
api.pluginConfig(OpenClaw's per-plugin injection)plugins.entries.regent-plugin-openclaw.config.<field>(legacy override path, still supported)channels.regent.<field>(canonical path written bysetup)- Fallback (see table below)
| Field | Fallback |
| --- | --- |
| liquidAuthServer | https://liquidauth.goplausible.xyz |
| agentDid | Auto-generated did:key:z... (Ed25519); persisted at ~/.openclaw/plugins/regent-plugin-openclaw/agent-key.json and reused on subsequent runs |
So the plugin works out of the box with zero config — the agent gets a fresh did:key on first install. Override only if you need a specific identity (e.g., did:web:... published at a known host).
Note — shell-environment reads were removed from this plugin in v0.0.20 (and further reduced in v0.0.34 to silence OpenClaw's substring-matched security audit). Configure via
openclaw.jsoninstead.
Persistence
The paired peer is stored at ~/.openclaw/plugins/regent-plugin-openclaw/peer.json (mode 0o600):
{
"peerDid": "did:key:z6Mk...",
"walletAddress": "ALGORAND_BASE32_ADDRESS",
"pairedAt": 1714230000000,
"lastConnectedAt": 1714233600000,
"lastRequestId": "01HXX..."
}lastRequestId is what makes auto-listen-on-startup work: on plugin boot the manager re-enters the same Liquid Auth WalletRoom and Regent's persisted session reaches it without rescanning a QR. /regent forget deletes the file. The plugin's own Ed25519 keypair is at agent-key.json (0o600) in the same directory.
Wire format
v0 runs in lenient/text mode: chat traffic is plain text on the existing liquid data channel from liquid-auth-js, with a JSON discriminator overlay for typed Regent messages. The plugin recognizes:
| OpenClaw flow | Regent frame | v0 status |
|---|---|---|
| Inbound user text | plain string (or regent/StreamRequest JSON, body.text extracted) | ✅ |
| Outbound agent reply | plain string via dispatchReplyWithBufferedBlockDispatcher | ✅ |
| regent_capabilities tool / /regent capabilities / openclaw regent capabilities | regent/CapabilityRequest → regent/Capabilities (symmetric; also auto-exchanged on connect) | ✅ |
| regent_sign tool (HITL) | regent/SigningRequest → regent/SigningResponse / regent/SigningRejected | ✅ |
| Streaming chunks | regent/StreamResponse + regent/StreamChunk | deferred |
| File transfer | regent/AttachmentBegin + binary on regent-data | deferred (single-channel POC) |
| Generic tool HITL | regent/ApprovalRequest / Response / Rejected | deferred |
The conversationId in OpenClaw's session model = the controller's DID.
When the controller becomes Regent-native (Path A), this overlay is replaced by full envelope wrapping via RegentSession + PlainEnvelope.
Versioning + publishing
The plugin pulls @goplausible/regent-sdk from npm as a regular transitive dependency — there is no more bundleDependencies / manual-SDK-materialization dance (retired in v0.0.33 once the SDK landed on npm publicly).
Three version sites must move in lockstep when bumping:
packages/regent-plugin-openclaw/package.json—"version"openclaw.plugin.json—"version"(THIS is the field OpenClaw caches by)- Root
package-lock.json—packages/regent-plugin-openclawnested entry's"version"
Plus this README's version banner at the top.
Build & publish:
# From repo root.
npm run build --workspaces --if-present # SDK build (and any other workspace builds)
( cd packages/regent-plugin-openclaw && npm pack --pack-destination ../../dist-packs )
( cd packages/regent-plugin-openclaw && npm publish )
# publishConfig.access=public is set in the plugin's package.json — no --access flag needed.Output: dist-packs/goplausible-regent-plugin-openclaw-<version>.tgz (~10 KB, ~80 KB smaller than the bundled era).
Install on an OpenClaw host:
# Primary path: pull from npm.
openclaw plugins install @goplausible/regent-plugin-openclaw
# Fallback for air-gapped hosts: tarball install.
openclaw plugins install /path/to/goplausible-regent-plugin-openclaw-<version>.tgzThe npm-published tarball and the dist-packs/ tarball are bit-for-bit identical (the publish step takes whatever npm pack produced).
License
Apache-2.0
