Sno Station
Sno Station is your agents' workstation: open-source software that turns the AI agents you already run into one squad on your own machine. Two heads are better than one, and they get better with every
A 0-to-1 product case study on making one Claude chat multiplayer.
Translation pending
PluginsHTML · JavaScriptGitHub @praharsha0403OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Share your Claude session with collaborators in real-time. Their messages come through your Claude, your responses mirror to them.
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
Real-time, multiplayer Claude. Share any Claude session with a friend from a Chrome extension — they open a link (no install), watch the whole conversation stream live, and send prompts that land inside your real Claude. Works on Claude Code and regular Claude chat. Free, always-on, instant.Read the full README ↗Real-time, multiplayer Claude. Share any Claude session with a friend from a Chrome extension — they open a link (no install), watch the whole conversation stream live, and send prompts that land inside your real Claude. Works on Claude Code and regular Claude chat. Free, always-on, instant.
Owner (Chrome extension, on claude.ai) ──► Cloudflare Worker + Durable Object ──► Friend (web viewer, any browser)
reads Claude's own data stateful relay + history live transcript + send box
[Name]: …, Claude responds, and the response mirrors back to everyone.| Component | Tech | Role |
|---|---|---|
Extension (extension/) | Chrome MV3 — background service worker + content script + popup | Owner side. Reads the conversation from Claude's own APIs, opens the WebSocket, injects friend prompts. |
Relay (backend/worker.js) | Cloudflare Worker + Durable Object (WebSocket Hibernation) | One SessionRoom DO per session token. Holds live sockets + persisted history; fans messages out. |
Viewer (backend/public/viewer.html) | Single static HTML/JS page, served by the Worker | Friend side. WebSocket client that renders the transcript (keyed reconcile) and sends prompts. |
Why Durable Objects: the only option that is instant (native WebSockets), free forever, never sleeps (Hibernation keeps sockets alive with idle time unbilled), and needs no credit card. One DO per session token gives strong consistency and a natural per-room boundary; history persists in DO storage so it survives hibernation.
OWNER (claude.ai / Claude Code tab) FRIEND (any browser)
+--------------------------------------+ +--------------------------+
| content.js | | viewer.html |
| • read transcript from Claude API | | • keyed reconcile render |
| • poll adaptively (~0.45–1.8s) | | • send prompts |
| • inject friend prompts | +------------+-------------+
+------------------+-------------------+ | WSS
| chrome.runtime |
+------------------+-------------------+ WSS |
| background.js (service worker) |------------+ |
| • WebSocket owner-extension | v v
+--------------------------------------+ +-------------------------------------+
| Cloudflare Worker (router) |
| /ws -> idFromName(token) -> DO |
| /api/sessions/* , /join/:token |
| +-------------------------------+ |
| | Durable Object SessionRoom | |
| | • Hibernation socket set | |
| | • history[] in DO storage | |
| | • fan-out + injection routing | |
| +-------------------------------+ |
+-------------------------------------+
claude.ai exposes no public API and virtualizes its DOM. The breakthrough is that the content script runs inside claude.ai as the logged-in user, so it can call Claude's own internal APIs with the page's cookies. Each message therefore arrives with a stable id, an authoritative role, and raw markdown — no scraping, no guessing.
The extension detects the surface and picks the right reader.
Claude Code sessions (URL claude.ai/code/session_…) are read from:
GET /v1/code/sessions/<session_id>/events?limit=500
Header: anthropic-version: 2023-06-01 ← REQUIRED (400 without it)
Response is { data: [ …events… ], next_cursor }. Only two event_types are chat messages — "user" and "assistant"; everything else (system, env_manager_log, control_request/response, tool_use/tool_result, thinking, …) is internal plumbing and is skipped.
Text extraction is shape-tolerant (a deep harvest of text-bearing blocks), because the two roles nest text differently:
payload.message.content is a plain string (e.g. "hi", or "[Chey]: hi" for a friend).payload.message.content is an array of blocks; the visible answer is the { type: "text", text } blocks (thinking / tool blocks are ignored).Each message keeps its event_id (dedup), created_at (ordering), and role → owner / friend / Claude attribution is exact.
Normal chats (claude.ai/chat/<uuid>) use:
GET /api/organizations/<org>/chat_conversations/<uuid>?tree=True&rendering_mode=messages
chat_messages[] carry sender (human/assistant), uuid, created_at, and text/content — mapped the same way.
A legacy scroll-and-scrape of the rendered DOM exists only as a fallback for states the APIs don't cover (e.g. logged-out shapes on a plain chat). It produces flat, structureless text, so it is used sparingly and never on Claude Code.
On Claude Code the events reader is the only writer. The DOM scrape and the DOM live-capture are fully disabled there. This is deliberate: when two writers (reader + scrape) both pushed history, the stored transcript flipped between the clean markdown version and a flat scraped version every few seconds. One writer = stable, complete, correctly-formatted transcript.
Sync cadence is adaptive: ~0.45s while an exchange is active (someone just sent, or Claude is streaming), relaxing to ~1.8s when idle. A content-hash guard means it only pushes when something actually changed.
When a friend sends a prompt, the Worker forwards it to the owner extension, which types it into the Claude composer as [Name]: <text> and submits it (guarded so the extension doesn't re-capture its own injection). It then appears in the transcript attributed to that friend, and Claude's reply flows back to everyone through the normal capture path.
The session token is derived deterministically from the Claude session/conversation id (the session_… in a Claude Code URL, or the chat UUID), sanitized to [A-Za-z0-9]. Same conversation ⇒ same token ⇒ same Durable Object ⇒ persistent history + remembered participants across time and re-shares. The token is sanitized identically on the server (/ws) and in the viewer, so a copy-pasted link with a stray character still resolves to the right room.
Roles on connect: owner-extension or viewer (query params ?token&role).
| Message | From → To | Effect |
|---|---|---|
owner_history | ext → DO | Replace stored history with the authoritative reader snapshot; broadcast to viewers |
owner_sent_message | ext → DO | Store + broadcast an owner prompt (non-code surfaces) |
owner_claude_response | ext → DO | Store + broadcast a Claude reply (non-code surfaces) |
viewer_prompt | viewer → DO | Store, broadcast to all, and forward as viewer_prompt_pending to the extension for injection |
history | DO → viewer | Full ordered transcript on connect / re-sync |
The reader snapshot is the source of truth for ordering; the server does not re-sort by synthetic timestamps.
assistant → left white card (Claude), human → right (owner), [Name]: → right, labelled with the friend's name.@font-face with absolute /fonts/… paths (relative paths would resolve under /join/ and the Worker would return HTML there).extension/ Chrome MV3 extension (the owner side)
manifest.json
background.js service worker: session + WebSocket + relay
content.js surface detection, Claude API readers, injection, sync loop
popup.html/.css/.js share UI (QR + link)
qrcode.js QR generation
assets/ icons/ popup art + extension icons
backend/ Cloudflare Worker + viewer
worker.js router + SessionRoom Durable Object
wrangler.toml deploy config (DO binding, static assets)
public/
viewer.html the friend-facing web app
fonts/ (Anthropic fonts NOT committed — see fonts/README.md)
Backend (Cloudflare):
cd backend
npx wrangler deploy # deploys to <name>.<subdomain>.workers.dev
Extension (Chrome):
SERVER_URL in extension/background.js to your deployed Worker URL.chrome://extensions → enable Developer mode → Load unpacked → select extension/.(For public distribution, upload a zip of extension/ to the Chrome Web Store.)
The Claude-accurate typefaces are Anthropic Sans and Anthropic Serif — Anthropic's proprietary fonts. They are not included in this repository. The viewer falls back to Helvetica/Georgia without them; to reproduce the exact look, supply the WOFF2 files as described in backend/public/fonts/README.md. Using Anthropic's fonts and brand marks is the deployer's responsibility.
All Rights Reserved. This repository is proprietary and confidential — it is not open source. Copyright is held exclusively by Chaitanya Sai (@CHAITANYASAI-del) and Praharsha (@praharsha0403). No copying, modification, distribution, or use by any other party is permitted without prior written permission. See LICENSE for the full terms.
Claude Groupchats is an independent, third-party tool and is not affiliated with or endorsed by Anthropic.