jsclaw is the lightweight engine for autonomous Claude AI agents — isolated in Docker, woken by heartbeats, reachable from a browser or over Nostr. Zero dependencies, down to the WebSocket server.
The host side is built entirely on Node.js built-ins — no native modules, no build step, no supply chain. You bring the I/O and storage; jsclaw brings the engine.
Every agent runs in its own Docker, Podman, or Apple container with an isolated filesystem. Not an optional sandbox — the architecture.
Agents wake on an interval, read their HEARTBEAT.md, act if anything needs attention — and stay silent when it doesn't. Quiet hours included.
Cron, interval, and one-shot tasks — including a full 5-field cron parser in plain JavaScript. Agents schedule their own follow-ups.
openclaw's identity stack — SOUL.md, IDENTITY.md, AGENTS.md, TOOLS.md, USER.md — loaded into the system prompt in order.
Persistent context as plain files the agent edits itself — preferences.md, projects.md, custom categories. Human-readable, git-diffable.
Teach capabilities with SKILL.md files — YAML frontmatter, keyword / regex / attachment triggers, instructions injected only when they fire.
One small interface — connect, sendMessage, ownsJid — and the manager routes outbound messages to the right platform automatically.
Ingress with {{body.field}} templates and secret auth; egress on events like agent.task.completed. CI, Slack, PagerDuty, home automation.
npx jsclaw gateway runs the whole host: WebSocket control plane in openclaw's wire shape, token auth, and a browser chat at /chat.
Your agent gets an npub. Encrypted DMs from any Nostr client — BIP340 Schnorr and NIP-04 in pure node:crypto, verified against the Bitcoin test vectors.
Declare any of the 32,000+ MCP servers in openclaw's mcp.servers shape and your agents get their tools — secrets via stdin, never ps-visible.
Allowlist-validated volume mounts with symlink resolution. .ssh, .aws, .env and friends are blocked by default, always.
The gateway wires everything — orphan reaping, scheduler, heartbeat, IPC — and exposes a WebSocket control plane in openclaw's frame shape, with a chat UI in your browser. The WebSocket server itself is hand-rolled RFC 6455 on Node built-ins: still zero dependencies.
$ npx jsclaw gateway jsclaw gateway v0.0.10 chat: http://127.0.0.1:18789/chat?token=2f0a… ws: ws://127.0.0.1:18789/?token=2f0a… agents: main token: 2f0a… (generated; set JSCLAW_GATEWAY_TOKEN to pin) Ctrl-C to stop.
Open the chat URL and talk to your containerized agent — or connect anything that speaks {type:"req", method:"chat.send"}: dashboards, scripts, other agents. Methods: status · chat.send · tasks.* · heartbeat.trigger · memory.*
DM it from Damus, Amethyst, or any Nostr client — encrypted NIP-04 DMs, no bot tokens, no platform accounts, no central server. The only claw runtime with a decentralized channel built in, and the whole stack — BIP340 Schnorr, NIP-04, bech32, relays — is node:crypto + native WebSocket.
import { createNostrChannel } from 'jsclaw'; const channel = createNostrChannel({ privateKey: process.env.NOSTR_PRIVATE_KEY, // hex or nsec relays: ['wss://relay.damus.io', 'wss://nos.lol'], allowedPubkeys: ['npub1you…'], // default-closed onMessage: (jid, text) => { /* run the agent, reply */ }, }); await channel.connect(); console.log('DM your agent at', channel.npub);
Every inbound event is signature-verified; only allowlisted pubkeys reach the agent. Schnorr signing is validated against the official BIP340 test vectors and cross-checked against node:crypto's independent secp256k1.
Input goes in over stdin; results stream back as sentinel-delimited JSON. Everything else — messages, tasks, follow-ups — is atomic JSON files on a shared mount. No sockets to secure, no ports to leak.
Plain JavaScript, granular imports, no compilation. These four snippets are the whole learning curve.
import { runContainerAgent, createConfig } from 'jsclaw'; const result = await runContainerAgent( { name: 'main', folder: 'main' }, { prompt: 'Summarize the repos in my workspace.', agentId: 'main', chatJid: 'user-1', isMain: true }, null, async (output) => console.log(output.result), // streams as it works createConfig(), );
import { TaskStore, createTaskIpcHandler, startTaskScheduler, startHeartbeat, startIpcWatcher, createConfig } from 'jsclaw'; const config = createConfig(); const store = new TaskStore(config); // Agents schedule their own work via the schedule_task MCP tool… startIpcWatcher({ onTask: createTaskIpcHandler(store), /* … */ }, config); // …the scheduler fires them (cron / interval / once)… startTaskScheduler({ store, runTask: runAgentForTask }, config); // …and the heartbeat wakes each agent to check HEARTBEAT.md. startHeartbeat({ getAgents: () => agents, runAgent, onAlert }, config, { quietHours: { start: '22:00', end: '07:00' } });
--- name: deploy-helper description: "Helps with deployments" trigger: "deploy|ship|push to prod" tools: [shell, http] --- # Deploy Helper When asked to deploy: 1. Run the test suite first — abort on failure 2. Tag the release and push the tag 3. Report what shipped, in one line # jsclaw skill install ./deploy.skill.md # injected only when a message mentions deploy / ship
import { ChannelManager, startIpcWatcher, createConfig } from 'jsclaw'; const channels = new ChannelManager(); channels.register(telegramChannel); // any object with the channels.register(discordChannel); // Channel interface await channels.connectAll(); // Outbound routing by JID — agents' send_message just works startIpcWatcher({ sendMessage: channels.sendMessage, /* onTask, getRegisteredAgents */ }, createConfig());
Drop them in an agent folder. The agent reads both — one shapes who it is, the other tells it what to watch.
Tasks and memory are plain files, so npx jsclaw inspects and manages everything directly — no gateway process, no RPC, no port.
$ npx jsclaw status jsclaw v0.0.10 runtime: docker (jsclaw-agent:latest) agents: ./agents (2: main, research) tasks: 3 total, 2 active main: heartbeat, soul, memory $ npx jsclaw tasks list b2b5bf61 [active] cron:0 9 * * 1-5 main next: tomorrow 09:00 Post the morning briefing to the channel $ npx jsclaw skill test ./deploy.skill.md "ship the api" MATCH deploy-helper (trigger: deploy|ship|push to prod) $ npx jsclaw heartbeat main HEARTBEAT_OK
openclaw is the full platform. nanoclaw is the secure WhatsApp assistant it inspired. jsclaw is the engine underneath, repackaged as a library you can build anything on.
| jsclaw | nanoclaw | openclaw | |
|---|---|---|---|
| Shape | Library + CLI | Personal assistant app | Full agent platform |
| Language | Pure JS, no build step | TypeScript | TypeScript |
| Host dependencies | 0 | ~7 (sqlite, baileys…) | Many |
| Container isolation | ✓ core design | ✓ core design | Optional sandbox |
| Heartbeat + scheduler | ✓ | Scheduler only | ✓ |
| SOUL.md · memory · skills · MCP | ✓ openclaw-compatible | — | ✓ |
| Channels | Nostr built in + interface | WhatsApp, Telegram | 26+ built in |
| Gateway / web UI | ✓ optional, one command | — | ✓ required |
Granular ESM exports — every module works standalone. Full reference in the README.
One install, one Docker build, one Markdown file with standing orders.