https://github.com/ractive/claw-socket
A websocket server streaming Claude Code activity
https://github.com/ractive/claw-socket
Last synced: 3 months ago
JSON representation
A websocket server streaming Claude Code activity
- Host: GitHub
- URL: https://github.com/ractive/claw-socket
- Owner: ractive
- Created: 2026-04-02T07:27:28.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-04-03T15:37:53.000Z (3 months ago)
- Last Synced: 2026-04-03T17:45:58.917Z (3 months ago)
- Language: TypeScript
- Size: 289 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# claw-socket
Real-time WebSocket bridge that streams Claude Code agent events to any connected client.
claw-socket watches your Claude Code sessions and forwards tool calls, agent state changes, usage stats, and lifecycle events over a WebSocket connection. Connect a dashboard, build automations, or stream events to your own tooling.
## Quick start
```bash
# Install globally with Bun
bun install -g claw-socket
# Start the server (installs Claude Code hooks automatically)
claw-socket
```
The server starts on `ws://localhost:3838` by default.
## CLI flags
```
Usage: claw-socket [options]
Options:
--port WebSocket server port (default: 3838, env: CLAW_SOCKET_PORT)
--host Hostname to bind (default: localhost, env: CLAW_SOCKET_HOST)
--verbose Enable verbose logging
--no-hooks Skip hook installation
--no-auth Disable token authentication
--rotate-token Regenerate auth token and exit
--install-hooks Install hooks and exit
--uninstall-hooks Remove claw-socket hooks from Claude settings and exit
--help Show help
--version Show version
```
## Authentication
claw-socket uses file-based token authentication. On first startup, a 32-byte random hex token is generated and written to `~/.claw-socket/token` (mode 600, directory mode 700). The token is reused across restarts.
- **WebSocket upgrade** requires `?token=` query parameter
- **POST /hook** requires `Authorization: Bearer ` header
- **GET /health**, **GET /docs**, **GET /asyncapi.json** are unauthenticated
Use `--no-auth` to disable authentication (development/testing). Use `--rotate-token` to regenerate the token and exit (requires server restart).
### Connecting from other projects
Read the token from `~/.claw-socket/token` and pass it as described above:
**Shell / curl:**
```bash
TOKEN=$(cat ~/.claw-socket/token)
# WebSocket (via websocat)
websocat "ws://localhost:3838?token=$TOKEN"
# POST /hook
curl -X POST http://localhost:3838/hook \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $TOKEN" \
-d '{"event": "..."}'
```
**TypeScript / JavaScript:**
```ts
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { homedir } from "node:os";
const token = readFileSync(join(homedir(), ".claw-socket", "token"), "utf-8").trim();
const ws = new WebSocket(`ws://localhost:3838?token=${token}`);
```
**Python:**
```python
from pathlib import Path
token = (Path.home() / ".claw-socket" / "token").read_text().strip()
```
## WebSocket protocol
Connect to `ws://localhost:3838?token=`. The server immediately sends a snapshot of all active sessions and agents.
### Client messages
**Subscribe to event topics**
```json
{ "type": "subscribe", "topics": ["tool.*", "session.*"] }
```
Glob patterns are supported (`*` matches within a segment). An optional `sessionId` field scopes events to a single session.
**Unsubscribe**
```json
{ "type": "unsubscribe", "topics": ["tool.*"] }
```
**Request snapshot**
```json
{ "type": "get_snapshot" }
```
**Get session history**
```json
{ "type": "get_session_history", "sessionId": "abc123", "limit": 100 }
```
**Request replay from a sequence number**
Each broadcast event carries a `seq` integer. Use this to reconnect without missing events:
```json
{ "type": "replay", "lastSeq": 42 }
```
The server replays all buffered events with `seq > lastSeq` that match your current subscriptions.
**Get usage stats**
```json
{ "type": "get_usage", "sessionId": "abc123" }
```
### Event envelope
All server-to-client events share this structure:
```json
{
"type": "tool.use",
"timestamp": 1712345678000,
"sessionId": "abc123",
"seq": 17,
"data": { }
}
```
### Common event types
| Type | Description |
|------|-------------|
| `session.discovered` | New Claude session started |
| `session.removed` | Session ended |
| `tool.use` | Tool invoked |
| `tool.result` | Tool completed |
| `agent.state_changed` | Agent state updated |
| `usage.updated` | Token/cost usage updated |
| `system.error` | Recoverable server error |
## Example client (JavaScript)
```javascript
const ws = new WebSocket("ws://localhost:3838");
let lastSeq = -1;
ws.onopen = () => {
ws.send(JSON.stringify({
type: "subscribe",
topics: ["tool.*", "session.*"]
}));
};
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
if (msg.seq != null) lastSeq = msg.seq;
if (msg.type === "snapshot") {
console.log("Active sessions:", msg.sessions.length);
} else if (msg.type === "tool.use") {
console.log("Tool used:", msg.data.toolName);
}
};
// On reconnect, replay missed events
function reconnect(ws) {
ws.send(JSON.stringify({ type: "subscribe", topics: ["tool.*", "session.*"] }));
if (lastSeq >= 0) {
ws.send(JSON.stringify({ type: "replay", lastSeq }));
}
}
```
## HTTP endpoints
| Path | Method | Description |
|------|--------|-------------|
| `/health` | GET | Server health check |
| `/hook` | POST | Claude Code hook receiver — returns `202 {status:"accepted"}` |
| `/asyncapi.json` | GET | AsyncAPI spec (JSON) |
| `/docs` | GET | AsyncAPI browser UI (requires generated docs, see below) |
## Docs generation
`/docs` serves pre-generated static HTML. Run the following after cloning or whenever the spec changes:
```bash
# 1. Export the AsyncAPI spec
bun run export-spec
# 2. Generate the HTML docs page (served at /docs)
asyncapi generate fromTemplate asyncapi.json @asyncapi/html-template@3.5.4 \
--param singleFile=true -o public --force-write
# 3. Generate the markdown reference (written to kb/docs/api-reference.md)
asyncapi generate fromTemplate asyncapi.json @asyncapi/markdown-template@2.0.0 \
--param outFilename=api-reference.md -o kb/docs --force-write
# 4. Patch sidebar layout bug in html-template output
bun run patch-docs
```
Install the AsyncAPI CLI if needed: `npm install -g @asyncapi/cli` (or substitute `asyncapi` with `bunx @asyncapi/cli`).
> **Note:** Step 4 patches a layout bug in `@asyncapi/html-template` where the fixed sidebar overflows its container. It's idempotent — safe to run multiple times.
## Security Scanning
[Gitleaks](https://github.com/gitleaks/gitleaks) is used to detect secrets accidentally committed to the repository. CI runs this automatically on every push and pull request.
To run locally:
```bash
gitleaks detect
```
Configuration lives in `.gitleaks.toml` at the project root.
## Development
```bash
# Run tests
bun test
# Type check
bun run typecheck
# Format + lint + typecheck + test
bun run check
# Build standalone binary
bun run build
# Run in watch mode
bun run dev
# Install / uninstall Claude Code hooks manually
bun run hooks:install
bun run hooks:uninstall
```