websocketd
CLAUDE.md17k starsChanged 55 days agoBSD-2-ClauseView on GitHub
A small command-line tool (Go) that wraps an existing CLI program and exposes it via WebSocket. Any program that reads STDIN and writes STDOUT becomes a WebSocket server. Unit tests are in libwebsocketd/. Integration tests are in qa/integration/. .github/workflows/test.yml's lint job runs go vet, gofmt -l, staticcheck, and gosec on every push and pull request; there is no separate local lint command to run before that. The qa/integration tests build the websocketd binary from source in TestMain, but go test…
- Commits and pushes
We split this CLAUDE.md into the rules it gives an agent
18 rules. About 0 are common, 13 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈13
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- could be automatic: a setting could enforce it
- Distinctive: line 29
When you make a mistake (especially forgetting something the user asked for):
- Distinctive: line 33
Don't just apologize — fix the system.
- Distinctive: line 37
When the user expresses a coding preference, convention, or correction during a session, offer to encode it into this CLAUDE.md file so it …
- Distinctive: line 41
Update README.md (and any relevant docs) before committing if the change affects:
- Very specific: line 71
Update CHANGES before committing if the change affects:
- Distinctive: line 87
Every Go source file starts with the 4-line BSD copyright header.
- Distinctive: line 96
Before implementing a feature or fix:
- Distinctive: line 97
Write a test that captures the expected behavior
- Distinctive: line 98
Run it — verify it fails (if it passes, the test isn't testing the right thing)
- Distinctive: line 103
When a test captures a process's output, capture stdout and stderr separately and assert on the specific stream — never merge them.
- Very specific: line 110
Run tests: go test ./... — do not commit if tests fail
- Very specific: line 111
If the change affects user-visible behavior, dependencies, or bug fixes: update CHANGES in the same commit (not a follow-up)
- Distinctive: line 146
Never merge on an agent's report.
- Distinctive: line 181
Run the suite from inside its own module instead:
- Distinctive: line 194
If an agent delegates a question to a subagent, its findings may be cited only after that subagent's completion notification has arrived.
- Distinctive: line 229
TCP ports: Always bind port 0 / allocate a free port.
- Very specific: line 260
SECURITY_AUDIT.mdstays deleted: Do not reinstate it. - Very specific: line 261
website/CNAME: must not be deleted or renamed.
≈ “Common” is an estimate: at least 1 in 5 of the 25,680 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
websocketd
A small command-line tool (Go) that wraps an existing CLI program and exposes it via WebSocket. Any program that reads STDIN and writes STDOUT becomes a WebSocket server.
Build
go buildTest
go test ./...Unit tests are in libwebsocketd/. Integration tests are in qa/integration/. .github/workflows/test.yml's lint job runs go vet, gofmt -l, staticcheck, and gosec on every push and pull request; there is no separate local lint command to run before that.
The qa/integration tests build the websocketd binary from source in
TestMain, but go test caches the result on that package's own inputs.
Changing libwebsocketd/, main.go, or config.go does not invalidate
the integration cache even though the built binary's behavior changes — a
cached green can hide a real break. After changing any non-qa/integration
package, re-run with go test -count=1 ./... (and -race for concurrency
changes).
Mistake retrospectives
When you make a mistake (especially forgetting something the user asked for):
- Acknowledge it directly
- Identify the root cause — why did this happen?
- Suggest a concrete project change to prevent recurrence (add a rule to CLAUDE.md, add a pre-commit check, etc.) Don't just apologize — fix the system.
Evolving preferences
When the user expresses a coding preference, convention, or correction during a session, offer to encode it into this CLAUDE.md file so it persists across sessions.
Documentation
Update README.md (and any relevant docs) before committing if the change affects:
- Public API, CLI interface, or configuration
- Setup/installation steps
- Feature behavior visible to users
The public website (websocketd.com) lives in website/ on this branch and
deploys via .github/workflows/pages.yml. It is not a separate branch, so
check it alongside README.md for the same changes — the tutorial and feature
list there go stale in exactly the same way. website/CNAME carries the custom
domain and must not be deleted or renamed.
Which surface does it go on
The docs site (docsite/, served at websocketd.com/docs) is the only surface
allowed to be long; the others are short because it exists.
--helpis the single source of truth for flag text.tools/gendocsgenerates the man page andreference/cli-flagsfrom it. Never hand-write a third copy.README.mdserves two readers briefly, one evaluating and one about to build, and points onward for everything else.website/index.htmlis marketing and orientation: no flag reference, no deploy recipes, no tutorial. A visitor scrolling past the fold for "how do I actually use this" means content landed on the wrong surface.
pages.yml copies website/ verbatim to the site root, so anything left in
that directory is published. Planning documents go in docs-archive/.
Changelog
Update CHANGES before committing if the change affects:
- Dependencies (especially security fixes)
- User-visible behavior, CLI flags, or defaults
- New features or test infrastructure
- Bug fixes referenced by issue number
Latest version goes at the top. Follow the existing format.
Classification: "Breaking change" is reserved for changes that require operators to act before upgrading (config migrations, removed/renamed flags, tightened accept criteria). Bug fixes that observably change behavior stay classified as fixes — fixing a bug isn't breaking a promise. Leading a version's entry with a "Breaking changes" block is fine; keep it honest.
License headers
Every Go source file starts with the 4-line BSD copyright header. New files
use the current year ("Copyright 2026 ..."), not the year copied from an
existing file. Existing files keep their original year. When adding a
missing header to an old file, use the year the file first appeared
(git log --follow --format=%ad --date=short -- <file> | tail -1) —
don't assume 2013.
Test-first
Before implementing a feature or fix:
- Write a test that captures the expected behavior
- Run it — verify it fails (if it passes, the test isn't testing the right thing)
- Implement until the test passes
Test precision
When a test captures a process's output, capture stdout and stderr separately and assert on the specific stream — never merge them. Which stream something is logged to is part of the behavior under test.
Pre-commit checks
Before every commit:
- Run tests:
go test ./...— do not commit if tests fail - If the change affects user-visible behavior, dependencies, or bug fixes: update
CHANGESin the same commit (not a follow-up)
Commits
Break work into small atomic commits — one logical change per commit. Don't bundle unrelated changes. A bug fix, a new feature, and a refactor are three commits, not one.
Engineering diary
Maintain DIARY.md — add an entry when making significant changes, architectural decisions, or non-obvious tradeoffs. Latest entries at top. Focus on why and context, not what (that's in the commits).
Bug tracking
Bugs and tasks are tracked in GitHub Issues. Use gh issue list to view and gh issue create to add new ones.
Agent operations
Configuration for /go-team and any other unattended multi-agent run.
Setup version: project-setup 2026-09-02
Requests lane: ASKS.md. Joe's own requests live there and outrank
anything an agent invents for itself. One agent must always be working its
top open item.
Lesson ledger: LESSONS.md. Append only when something actually bit;
prefer sharpening an existing entry over adding a near-duplicate.
Autonomy: merge locally only. Agents may merge verified work to local
main; nothing is pushed to origin. git push is deliberately absent
from .claude/settings.json. Joe reviews before anything leaves the machine.
Crew size: 3.
Verification recipe
Never merge on an agent's report. Run this yourself, in a throwaway worktree on the agent's branch — never in the shared checkout:
go build ./...
go test -count=1 ./... # -race as well for concurrency changesgo test alone is not sufficient evidence — see the integration-cache note
under Test above; -count=1 is what makes a green meaningful after a
change to libwebsocketd/, main.go, or config.go.
For anything a user would notice, also drive the real binary:
# pick a free port; never a fixed one (see singletons)
PORT=$(python3 -c 'import socket;s=socket.socket();s.bind(("127.0.0.1",0));print(s.getsockname()[1]);s.close()')
./websocketd --port=$PORT --devconsole ./qa/integration/testcmd echo
curl -sS -D- "http://127.0.0.1:$PORT/" | head -40For dev-console changes the drive must be a real browser, not curl.
qa/browser is its own Go module (own go.mod), the same pattern as
qa/capture, so that chromedp never enters the module that builds the
shipped binary. Two ways this bites if you forget it, both checked by hand
before writing this down:
go test -count=1 ./qa/browser/...run from the repo root does NOT silently pass — it fails outright withdirectory prefix qa/browser does not contain main module or its selected dependencies. Loud, not a vacuous green, but also not what you wanted.- The broad
go test -count=1 ./...from the repo root is the quieter trap: it neither errors nor mentionsqa/browserat all — it is simply never attempted, and the overall command can still report a clean pass having run zero browser tests.
Run the suite from inside its own module instead:
(cd qa/browser && go test -count=1 ./...)This builds a fresh binary and drives the chromedp suite against it. Exercising the mechanism is not exercising the integration — a console test that never opens a socket, never sends a frame, and never reads one back has verified nothing a user cares about.
A pending delegation is not a result
If an agent delegates a question to a subagent, its findings may be cited only after that subagent's completion notification has arrived. If a report is due while the delegate is still running, the report says so and marks the claim unknown. An agent cannot distinguish "the delegate answered" from "I know what the delegate would say", and neither can its reader.
Name the instrument for delegated research the way you would for a measurement. "An agent said so" is not an instrument. This is written down because a round asserted what six named web servers do, labelled it verified against primary sources, and had invented all of it while its subagent was still running; the claims reached a merge commit on main before the worker retracted them.
Never commit anything that identifies Joe's machine
Docs get written by running real commands and pasting real output, which
is how the tutorial came to ship Joe's actual computer name inside its
example transcripts. Use example-host.local, /Users/you/ (or
$HOME), and you as placeholders — see docsite/STYLE.md §7 for the
full table.
TestNoLocalMachineIdentifiersInRepo (root package) enforces this: it
reads the live hostname, home directory and account name of whatever
machine is running the tests and fails if any of them appear in a
committed file, plus flags any other real-looking /Users/<name> or
/home/<name> path. It keys off the current machine deliberately, so it
protects whoever drafts next without needing a hardcoded blocklist.
Paste output freely while drafting; scrub what the test names before
committing. Do not weaken or skip it to get a branch green.
Shared singletons
Parallel agents collide on these. Each agent gets its own, or the run corrupts itself:
- TCP ports. Always bind port 0 / allocate a free port. A hardcoded 8080 in a test or a manual drive will fight every sibling agent.
- Unix socket paths.
--unixsockettargets go in the agent's ownt.TempDir(), never a shared/tmp/websocketd.sock. - Headless Chrome user-data-dir. Two chromedp sessions sharing a profile directory crash each other; give each run its own.
refs/stash. Shared across every worktree of this repo — nevergit stash. Use a second worktree orgit diff > fileinstead.- The built binary path.
qa/integrationbuilds into its own temp dir; don't add a build that writes./websocketdfrom two agents at once.
Do not touch
Settled decisions. Reopen only with Joe, and read the DIARY entry first:
- No global SIGTERM handler for unix-socket cleanup. Rejected: it changes Ctrl+C and exit-code semantics for every user to serve a niche flag. Startup recovery is the only cleanup mechanism (DIARY 2026-08-17).
- No
ReadTimeout/WriteTimeouton the main server. They would kill long-lived WebSocket and streaming-CGI responses.ReadHeaderTimeoutonly (DIARY 2026-08-17). --maxframesizedefaults to 1 MiB;--maxforksto 1024. Both are deliberate backstops, not capacity plans.--originport matching is strict, with:*as the explicit wildcard (issue #473).--socketmodedoes not change the default socket permissions (issue #474).SERVER_NAME/SERVER_PORTstay Host-derived. Documented, not "fixed" (issue #475).- The default origin policy stays permissive for now; the stderr
banner plus
--anyoriginis the agreed shape until the announced flip. SECURITY_AUDIT.mdstays deleted. Do not reinstate it.website/CNAMEmust not be deleted or renamed.
Asset locations
Screenshots and recordings of the dev console go in website/img/console/
so the site and the docs share one source. Reference them from README.md
and website/index.html rather than duplicating files.
Structure
main.go— entry point, flag parsingconfig.go— configuration typeshelp.go— help textversion.go— version infolibwebsocketd/— core library (WebSocket handling, HTTP, process management)examples/— example scripts in various languagesrelease/— release/packaging scripts
Trending
More talk lately.about the whole repository
Who's talking about joewalnes/websocketd
- A Hacker News story“Turn any application that uses stdin/stdout into a WebSocket server”Hacker News · Dec 10, 2013 · 311 points
- Gabriel Zimmermann“Fixing my weaknesses with Go | Part 5 | Websocketd and IPC”YouTube · Sep 10, 2020 · 265 views
- A Hacker News story“Turn any program that uses stdin/stdout into a WebSocket server”Hacker News · Jan 29, 2019 · 34 points
- Stack Overflow“Websocket slow transfer rate in Google Chrome”Stack Overflow · Jan 20, 2015 · 5 votes
- tindy2013/stairspeedtest-reborn“MacOS遇到的问题总结+解决方法”GitHub · Aug 1, 2022 · 3 reactions
The biggest 5 of 38 mentions linking to the repository that our crawl found. Each line is their own headline.
See all 38
Talked about on GitHub, Stack Overflow, Hacker News, the web, YouTube and Bluesky (38 times).
On GitHub · 23
- Aug 1, 2022MacOS遇到的问题总结+解决方法 — tindy2013/stairspeedtest-reborn3 reactions · 5 comments
- Sep 23, 2026websocketd: ignore 0.4.2 — repology/repology-rules0 reactions
- May 9, 2026Update repo table — arran4/arran40 reactions
- Oct 29, 2025Allow Case-Insensitive WebSocket Upgrade Headers — factor/factor0 reactions · 1 comment
- Dec 1, 2024add websocketd package — wolfi-dev/os0 reactions · 1 comment
18 more on GitHub
- Aug 13, 2024Language Server for Playground IDE — AElfProject/aelf-playground-build-service0 reactions
- Nov 12, 2022[Package]: websocketd: any command as websocket server, thus interactive app with html5 — termux/termux-packages0 reactions · 1 comment
- Oct 7, 2022Download the correct architecture of websocketd based on ansible_architecture fact — mcoia/eg-docker0 reactions
- Oct 25, 2021search context validation "check for errors" doesn't show errors — sourcegraph/sourcegraph-public-snapshot0 reactions · 1 comment
- Jun 8, 2021Bug Report: directory paths containing the ‘#’ character led to broken links — sourcegraph/sourcegraph-public-snapshot0 reactions · 2 comments
- Feb 9, 2021websocketd: 0.3.1 -> 0.4.1 — NixOS/nixpkgs0 reactions
- Nov 16, 2018Run WS server on Linux — arrayio/array-io-keychain0 reactions · 2 comments
- Nov 16, 2018Run WS server on MacOS — arrayio/array-io-keychain0 reactions
- Nov 16, 2018Run keychain from WS server on Windows — arrayio/array-io-keychain0 reactions · 2 comments
- Feb 23, 2018[agent] Stream stdin and stdout if needed by cli, browser — benchhub/benchhub0 reactions
- Feb 6, 2018Alternative for pusher — openware/peatio0 reactions · 3 comments
- Jun 6, 2016improvements to websocketd — learn-co/learn-ide0 reactions · 1 comment
- Oct 21, 2015formula: websocketd 0.2.11 — Homebrew/legacy-homebrew0 reactions · 13 comments
- May 6, 2015Example with repeated read/print needed — ponylang/ponyc0 reactions · 1 comment
- Feb 16, 2015Feature: Add websocket to determine the beginning of the next round — phxql/restwars0 reactions · 2 comments
- Mar 28, 2014Explore websocketd — getlantern/natty0 reactions · 1 comment
- Jan 19, 2014investigate porting all the things to websocketd — nejohnson2/telesource0 reactions
- Dec 22, 2013Integrate pure-java WebSockets functionality. — jasonpr59/chess0 reactions
On Stack Overflow · 6
- Jan 20, 2015Websocket slow transfer rate in Google Chrome5 votes
- Jan 15, 2017How to handle the full set of websocket events in websocketd?2 votes
- Jul 5, 2016How to configure logstash 2.3.3 websocket2 votes · 1 answer
- Jul 5, 2017websocketd with json over STDIN/STDOUT0 votes
- Jan 9, 2014powershell breaks at 180 characters0 votes · 1 answer
1 more on Stack Overflow
- Mar 27, 2017Stream PhantomJS output to frontend application-1 votes · 1 answer
On Hacker News · 4
- Dec 10, 2013Turn any application that uses stdin/stdout into a WebSocket server311 points · 88 comments
- Jan 29, 2019Turn any program that uses stdin/stdout into a WebSocket server34 points · 7 comments
- Sep 12, 2013Turn any CLI into a websocket with websocketd3 points
- Jan 30, 2019Joewalnes/websocketd: Inetd for WebSockets [Golang]1 point
On the web · 3
- Oct 11, 2026websocketd: review and tradeoffs — google.com
- May 8, 2017Github 开源项目(一)websocketd (实战:实时监控服务器 ... — google.com
- Nov 3, 2016Consola de desarrollo · joewalnes/websocketd Wiki — google.com
On YouTube · 1
- Sep 10, 2020Fixing my weaknesses with Go | Part 5 | Websocketd and IPC — Gabriel Zimmermann265 views · 1 comment
On Bluesky · 1
- Nov 21, 2024websocketd by @joewalnes (⭐️ 17165) #go — @golangoss.bsky.social1 likes and repost
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

