Capabilities
Compatibility
Verification
Tags
@privateclaw/privateclaw
@privateclaw/privateclaw is the npm-publishable runtime behind PrivateClaw sessions.
It is responsible for:
- creating one-time relay sessions,
- optionally turning those sessions into small encrypted group rooms with join/leave presence notices,
- exposing
/renew-session,/session-qr,/mute-bot, and/unmute-botas dynamic built-in PrivateClaw commands when appropriate, - emitting bilingual Chinese + English invite / CLI / command text for the built-in PrivateClaw surfaces,
- generating encrypted invite QR payloads,
- terminating app-side ciphertext,
- forwarding user messages into an upstream bridge,
- and sending assistant replies back through the relay.
Join the community on Telegram: PrivateClaw Telegram group
Client app access:
- iOS App Store (YourClaw): https://apps.apple.com/us/app/yourclaw/id6760531637
- Android closed alpha tester group: https://groups.google.com/g/gg-studio-ai-products
- Android closed alpha (Google Play): https://play.google.com/store/apps/details?id=gg.ai.privateclaw
- Desktop GitHub Releases (Windows / macOS / Linux, look for
app-v*): https://github.com/topcheer/PrivateClaw/releases
For Android closed alpha, Google Play access only opens after the tester joins the Google Group above.
Provider control flow
sequenceDiagram
participant Public as Telegram / Discord / QQ
participant Plugin as OpenClaw plugin
participant Provider as PrivateClawProvider
participant Relay as Relay
participant App as PrivateClaw App
Public->>Plugin: /privateclaw or /privateclaw group
Plugin->>Provider: createInviteBundle(...)
Provider->>Relay: create session
Relay-->>Provider: sessionId + expiresAt
Provider-->>Plugin: bilingual announcement + invite URI + QR
Plugin-->>Public: reply payload
App->>Relay: connect with sessionId
Relay-->>Provider: encrypted frame
Provider-->>App: welcome + capabilities
Install
Current OpenClaw plugin guidance assumes Node.js 22 or newer on the machine that installs or runs this package.
If openclaw is already installed on the current machine, the quickest standalone bootstrap flow is now:
npx -y @privateclaw/privateclaw@latest
That npx setup wizard checks local OpenClaw, installs or updates the plugin, enables it, restarts the gateway, and then immediately starts pairing. It prompts for single vs group chat and for one of these session-duration presets: 30m, 2h, 4h, 8h, 24h, 1w, 1mo, 1y, or permanent (100 years). Because it downloads the npm package directly, it is also the fastest way to pick up a newer npm patch before the matching ClawHub package is updated.
The production default relay for this package is:
https://relay.privateclaw.us
Keep --dangerously-force-unsafe-install on every manual install path. The simplest published install is:
openclaw plugins install --dangerously-force-unsafe-install @privateclaw/privateclaw@latest
openclaw plugins enable privateclaw
If you are using the default public relay at https://relay.privateclaw.us, the relayBaseUrl override is optional and can be skipped. Only run openclaw config set plugins.entries.privateclaw.config.relayBaseUrl ... when you want to change the default relay for the whole plugin. For one-off invites, you can now override the relay per slash command or per CLI invocation instead of changing persistent config.
If you already have a local package directory, prefer installing that directory instead of an archive. This avoids the OpenClaw 2026.3.31 archive path that drops the unsafe-install override:
openclaw plugins install --dangerously-force-unsafe-install ./packages/privateclaw-provider
openclaw plugins enable privateclaw
If you explicitly install from ClawHub, keep the same flag there too:
openclaw plugins install --dangerously-force-unsafe-install clawhub:@privateclaw/privateclaw@latest
openclaw plugins enable privateclaw
After openclaw plugins install, openclaw plugins enable, or any openclaw config set plugins.entries.privateclaw.config... change, restart the running OpenClaw gateway/service before testing so it reloads the extension and config. In practice, that means restarting the running openclaw start process or whichever service unit hosts your gateway.
You can also preselect those choices:
npx -y @privateclaw/privateclaw@latest setup --group --duration 24h --open
If you are embedding the runtime programmatically instead of installing the OpenClaw plugin, install the packages directly:
npm install @privateclaw/privateclaw @privateclaw/protocol
For local development from this repository, use a linked checkout and point it at your local relay:
openclaw plugins install --link ./packages/privateclaw-provider
openclaw plugins enable privateclaw
openclaw config set plugins.entries.privateclaw.config.relayBaseUrl ws://127.0.0.1:8787
Because local development changes both the installed plugin and relay target, restart the running OpenClaw gateway/service before testing. After that restart, the OpenClaw-hosted plugin-service now starts eagerly, so local CLI pairing no longer needs a prior /privateclaw warm-up from another chat channel.
Bootstrap pairing still does not start with openclaw channels add privateclaw. To make /privateclaw available inside Telegram/Discord/QQ, add one of those normal OpenClaw channels separately with openclaw channels add --channel ..., then start pairing from that existing surface.
Once a PrivateClaw session is paired, app-created plain-text turns and ordinary slash commands are now re-owned by a real internal privateclaw OpenClaw channel. That lets reminders and follow-up results created from those app turns route back into the encrypted PrivateClaw session, while local provider-only commands such as /renew-session, /session-qr, /mute-bot, and /unmute-bot still stay inside the provider.
Relay endpoints
If you want to point the provider at a custom relay domain, resolve both sockets from one base URL:
import { EchoBridge, PrivateClawProvider, resolveRelayEndpoints } from "@privateclaw/privateclaw";
const relay = resolveRelayEndpoints("https://relay.privateclaw.us");
const provider = new PrivateClawProvider({
...relay,
bridge: new EchoBridge("PrivateClaw demo"),
providerLabel: "PrivateClaw",
});
resolveRelayEndpoints(...) accepts bare host:port, ws://, wss://, http://, and https:// base URLs.
Upstream bridge modes
The package ships with:
EchoBridgefor local smoke tests,OpenClawAgentBridgefor direct integration with a locally runningopenclawgateway viaopenclaw agent --json,WebhookBridgefor generic HTTP dispatch,OpenAICompatibleBridgefor local OpenClaw gateways or any OpenAI-compatible chat completions endpoint.
The demo CLI auto-selects bridges in this order:
- OpenClaw agent bridge
- OpenAI-compatible gateway
- Webhook bridge
- Echo bridge
OpenClaw plugin entrypoint
The package now ships a real OpenClaw extension entrypoint:
index.tsis exposed throughpackage.json#openclaw.extensionsopenclaw.plugin.jsondeclares the plugin manifest and config schema- the plugin registers a real
/privateclawplugin command viaapi.registerCommand(...) - the plugin also registers a virtual
privateclawOpenClaw channel for paired app-session ownership - the command returns both the invite URI and a PNG QR code through
ReplyPayload
That means the installed extension can surface /privateclaw, /privateclaw group, and one-off relay overrides such as /privateclaw relay=https://your-relay.example.com in native command menus such as Telegram instead of relying on the old local shim.
Capabilities advertise /renew-session and /session-qr for active sessions, and for group sessions they also advertise /mute-bot or /unmute-bot depending on the current session state.
If you want the provider to behave more like a proactive participant in group sessions, enable bot mode in plugin config:
openclaw config set plugins.entries.privateclaw.config.botMode true
When botMode is enabled, group sessions do two extra things:
- greet a newly joined participant if they stay silent for about 10 minutes
- send a short proactive re-engagement message after about 20 minutes of group silence
The idle re-engagement path now picks from a built-in bank of 200 whimsical topic prompts and avoids immediately repeating the previous topic for the same session.
Both behaviors go through the same upstream bridge / OpenClaw agent path as normal assistant replies, and /mute-bot or /unmute-bot also pause or resume these proactive turns.
For advanced tuning or tests, you can override the defaults in either of these ways:
- plugin config:
botModeSilentJoinDelayMs,botModeIdleDelayMs - environment variables:
PRIVATECLAW_BOT_MODE,PRIVATECLAW_BOT_MODE_SILENT_JOIN_DELAY_MS,PRIVATECLAW_BOT_MODE_IDLE_DELAY_MS
The timeout values are milliseconds. The defaults are 600000 (10 minutes) for silent-join greetings and 1200000 (20 minutes) for idle-group follow-ups. If both plugin config and environment variables are present, plugin config takes precedence.
Channel QR delivery notes
When the plugin sends a pairing QR back through an existing OpenClaw chat transport, it must follow the channel outbound contract rather than the provider's internal attachment schema. In practice that means OpenClaw ReplyPayload with text plus mediaUrl / mediaUrls.
- Rich-media transports such as
discord,telegram,slack,signal,imessage,bluebubbles,mattermost,msteams,matrix,googlechat,feishu,whatsapp,line, andzalousercan usually render a generated QR directly frommediaUrl. qqbotstill supports legacy inline<qqimg>...</qqimg>text, but its current gateway also accepts structuredmediaUrl/mediaUrls, so both paths are valid.zalo,tlon, andsynology-chatare effectively URL-only for QR delivery: they need a reachable HTTP(S) image URL rather than a private local file path.irc,nextcloud-talk, andtwitchdegrade to plain text links, whilenostrcurrently hasmedia: false, so those channels should always rely on the invite URI text as the guaranteed fallback.
The current provider implementation already special-cases QQ-style inline image replies for compatibility and uses mediaUrl for the general path. If you extend pairing delivery in the future, think in four buckets instead of just qq vs everyone else: qqbot, rich-media channels, URL-only channels, and no-media / text-only channels.
Demo CLI
PRIVATECLAW_RELAY_BASE_URL=ws://127.0.0.1:8787 npm run demo --workspace @privateclaw/privateclaw -- pair
Provider CLI reference
The standalone npm binary exposes privateclaw-provider <subcommand> and also powers npx -y @privateclaw/privateclaw@latest.
Once the plugin is installed and enabled, OpenClaw also exposes the shared session-management commands as openclaw privateclaw <subcommand>.
The standalone-only setup wizard intentionally lives outside the OpenClaw alias because it is the thing that bootstraps that local OpenClaw install/update flow.
Public subcommands:
| Subcommand | Example | Purpose | Notes |
|---|---|---|---|
setup | npx -y @privateclaw/privateclaw@latest | Install/update/enable the local OpenClaw plugin and immediately start pairing. | Standalone only. privateclaw-provider with no subcommand also defaults to this wizard. Use --group or --single to preselect chat mode, --duration <preset> for 30m, 2h, 4h, 8h, 24h, 1w, 1mo, 1y, or permanent (100 years), and --ttl-ms <ms> for a custom duration. |
pair | privateclaw-provider pair | Create a local PrivateClaw session and render the pairing QR in the terminal. | The OpenClaw alias is openclaw privateclaw pair, and both support --relay <url> for one-off relay overrides. |
sessions | privateclaw-provider sessions | List active locally managed sessions. | The output includes the total count plus each session's type, participants, state, expires, host, and optional label. |
sessions follow <sessionId> | privateclaw-provider sessions follow <sessionId> | Follow the OpenClaw session log for one managed session. | This tails the session JSONL written by OpenClaw so you can watch the agent-side run in real time. |
sessions qr <sessionId> | privateclaw-provider sessions qr <sessionId> | Reprint the QR for a currently managed session. | The QR is rendered in the terminal by default. Add --open to also launch the local browser preview, or --notify to broadcast the same QR to the session's currently connected participants as an ephemeral assistant message. |
sessions kill <sessionId> | privateclaw-provider sessions kill <sessionId> | Terminate a locally managed session. | On current hosts this closes just the selected session. If an already-running older foreground/background host does not support per-session shutdown yet, the command falls back to terminating that legacy host process. |
sessions killall | privateclaw-provider sessions killall | Terminate every background daemon-managed session. | This only targets pair-daemon sessions so it does not interrupt foreground hosts or plugin-service sessions. The standalone binary also exposes the same shortcut as privateclaw-provider killall. |
kick <sessionId> <appId> | privateclaw-provider kick <sessionId> <appId> | Remove one participant from a group session. | This also closes that app's relay connection and blocks the same appId from rejoining the current session. |
pair supports these public flags:
| Flag | Effect |
|---|---|
--ttl-ms <ms> | Override the session TTL. Fresh sessions default to 24 hours. |
--label <label> | Attach an optional relay-side label that also appears in sessions output. |
--relay <url> | Override the relay base URL just for this command without changing plugin config. |
--group | Allow multiple app clients to join the same session. |
--print-only | Print the invite URI and QR, then exit immediately. This also closes the session instead of keeping it alive. |
--open | Open a local browser preview page for the generated QR. |
--foreground | Keep the session in the current terminal until it ends or you press Ctrl+C. On supported runtimes, pressing Ctrl+D hands the live session off to a detached background daemon without invalidating the QR. |
--verbose | Emit more detailed provider / bridge debug logs. For live diagnosis, combine it with --foreground so the extra logs stay visible in the current terminal. |
Typical examples:
# Standalone npm binary / npx
npx -y @privateclaw/privateclaw@latest
npx -y @privateclaw/privateclaw@latest setup --group --duration permanent --open
privateclaw-provider
privateclaw-provider pair --group --foreground
privateclaw-provider pair --foreground --verbose
privateclaw-provider pair --relay https://your-relay.example.com
privateclaw-provider sessions
privateclaw-provider sessions follow <sessionId>
privateclaw-provider sessions qr <sessionId> --open
privateclaw-provider sessions kill <sessionId>
privateclaw-provider sessions killall
privateclaw-provider killall
privateclaw-provider kick <sessionId> <appId>
# The same commands through the installed OpenClaw plugin
openclaw privateclaw pair --group --foreground
openclaw privateclaw pair --foreground --verbose
openclaw privateclaw pair --relay https://your-relay.example.com
openclaw privateclaw sessions
openclaw privateclaw sessions follow <sessionId>
openclaw privateclaw sessions qr <sessionId> --notify
openclaw privateclaw sessions kill <sessionId>
openclaw privateclaw sessions killall
openclaw privateclaw kick <sessionId> <appId>
When a first-time participant joins a group session without providing a name, the provider assigns a local animal-style nickname. The label is chosen deterministically from the session/app identity, avoids collisions with other participants already in the same session, and stays stable when that same app reconnects later.
Active participants can use /session-qr to re-share the current invite QR while a session is live, and the local operator can do the same from the terminal with privateclaw-provider sessions qr <sessionId> or openclaw privateclaw sessions qr <sessionId>. Once less than 30 minutes remain, the provider emits a reminder so any participant can run /renew-session. In group sessions, /mute-bot and /unmute-bot pause or resume assistant replies without interrupting participant-to-participant chat delivery.
Background daemon sessions can outlive OpenClaw main-process restarts. Use privateclaw-provider sessions or openclaw privateclaw sessions to inspect them, sessions kill <sessionId> when you want to stop one explicitly, and sessions killall (or standalone privateclaw-provider killall) when you want to clear every background daemon session at once.
After the app attaches, both the mobile app and the mobile web chat show the resolved relay host. They also warn before connecting when an invite points at a non-default relay.
After the app attaches, the in-app session panel also shows the current relay server so users can verify which relay endpoint the invite resolved to.
Voice STT / ASR
When a user sends a voice attachment, the provider now tries to transcribe it before it enters the normal OpenClaw text flow.
The runtime order is:
- local
whisperCLI fromopenai-whisper, whenwhisperis available on the host - provider-side direct STT from the configured OpenClaw audio model or
PRIVATECLAW_STT_*overrides - the bridge
transcribeAudioAttachments(...)path as the final fallback
If one provider-side layer fails, PrivateClaw logs the fallback and continues to the next layer instead of failing the whole voice turn immediately. For live diagnosis, use privateclaw-provider pair --foreground --verbose or openclaw privateclaw pair --foreground --verbose.
If you want provider-side network STT from OpenClaw config, configure the default audio model, for example:
openclaw config set tools.media.audio.models '[{"baseUrl":"http://127.0.0.1:8090","model":"whisper-1","headers":{"Authorization":"Bearer local"}}]' --strict-json
openclaw config validate
Optional local whisper overrides:
PRIVATECLAW_WHISPER_BINPRIVATECLAW_WHISPER_MODELPRIVATECLAW_WHISPER_LANGUAGEPRIVATECLAW_WHISPER_DEVICEPRIVATECLAW_WHISPER_MODEL_DIR
Relay deployment
The recommended relay image is built from this repository and published by GitHub Actions to:
ghcr.io/topcheer/privateclaw-relay
The root README.md covers Docker Compose usage, GHCR image usage, and relay environment variables.
Publish to npm
From the repository root:
npm run publish:npm:dry-run
npm run publish:npm
@privateclaw/privateclaw depends on @privateclaw/protocol, so the protocol package must be published first. The combined scripts above publish in that order.
If you are publishing by hand, the minimum safe sequence is:
npm run publish:protocol
npm run publish:provider
Both packages are configured to publish publicly to https://registry.npmjs.org.
Important environment variables:
PRIVATECLAW_RELAY_BASE_URLPRIVATECLAW_OPENCLAW_AGENT_BRIDGEPRIVATECLAW_OPENCLAW_AGENT_BINPRIVATECLAW_OPENCLAW_AGENT_IDPRIVATECLAW_OPENCLAW_AGENT_CHANNELPRIVATECLAW_OPENCLAW_AGENT_THINKINGPRIVATECLAW_GATEWAY_BASE_URLPRIVATECLAW_GATEWAY_CHAT_COMPLETIONS_URLPRIVATECLAW_GATEWAY_MODELPRIVATECLAW_GATEWAY_API_KEYPRIVATECLAW_STT_BASE_URLPRIVATECLAW_STT_API_KEYPRIVATECLAW_STT_HEADERSPRIVATECLAW_STT_MODELPRIVATECLAW_STT_PROVIDERPRIVATECLAW_WHISPER_BINPRIVATECLAW_WHISPER_MODELPRIVATECLAW_WHISPER_LANGUAGEPRIVATECLAW_WHISPER_DEVICEPRIVATECLAW_WHISPER_MODEL_DIRPRIVATECLAW_WEBHOOK_URLPRIVATECLAW_WEBHOOK_TOKEN
