tidwall/evio
Fast event-loop networking for Go
Stale — last commit 3y ago
weakest axislast commit was 3y ago; top contributor handles 97% of recent commits…
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓4 active contributors
- ✓MIT licensed
- ✓CI configured
Show all 7 evidence items →Show less
- ⚠Stale — last commit 3y ago
- ⚠Small team — 4 contributors active in recent commits
- ⚠Single-maintainer risk — top contributor 97% of recent commits
- ⚠No test directory detected
What would change the summary?
- →Use as dependency Mixed → Healthy if: 1 commit in the last 365 days; diversify commit ownership (top <90%)
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/tidwall/evio)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/tidwall/evio on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: tidwall/evio
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/tidwall/evio shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
WAIT — Stale — last commit 3y ago
- 4 active contributors
- MIT licensed
- CI configured
- ⚠ Stale — last commit 3y ago
- ⚠ Small team — 4 contributors active in recent commits
- ⚠ Single-maintainer risk — top contributor 97% of recent commits
- ⚠ No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live tidwall/evio
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/tidwall/evio.
What it runs against: a local clone of tidwall/evio — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in tidwall/evio | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 958 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of tidwall/evio. If you don't
# have one yet, run these first:
#
# git clone https://github.com/tidwall/evio.git
# cd evio
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of tidwall/evio and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "tidwall/evio(\\.git)?\\b" \\
&& ok "origin remote is tidwall/evio" \\
|| miss "origin remote is not tidwall/evio (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "evio.go" \\
&& ok "evio.go" \\
|| miss "missing critical file: evio.go"
test -f "evio_unix.go" \\
&& ok "evio_unix.go" \\
|| miss "missing critical file: evio_unix.go"
test -f "internal/internal_linux.go" \\
&& ok "internal/internal_linux.go" \\
|| miss "missing critical file: internal/internal_linux.go"
test -f "evio_std.go" \\
&& ok "evio_std.go" \\
|| miss "missing critical file: evio_std.go"
test -f "internal/notequeue.go" \\
&& ok "internal/notequeue.go" \\
|| miss "missing critical file: internal/notequeue.go"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 958 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~928d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/tidwall/evio"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
evio is a high-performance event-loop networking framework for Go that bypasses the standard library's net package and makes direct syscalls to epoll (Linux) and kqueue (BSD/macOS) for network I/O multiplexing. It's designed to match Redis and HAProxy's packet-handling throughput while using minimal memory, and serves as the foundation for Tile38 and other latency-sensitive systems. Single-package design: core logic in evio.go, platform-specific code split across evio_unix.go, evio_linux.go, and internal/internal_*.go files. evio_std.go provides fallback using Go's net package for unsupported systems. examples/ contains runnable echo, HTTP, and Redis servers; benchmarks/ has comparative performance harnesses.
👥Who it's for
Backend engineers and systems programmers building high-throughput TCP/UDP servers (Redis clones, L7 proxies, real-time geospatial systems like Tile38) who need predictable low-latency performance without the overhead of Go's goroutine-per-connection model.
🌱Maturity & risk
Actively developed and production-ready. The codebase is well-established (foundational library for Tile38), includes CI via GitHub Actions (.github/workflows/go.yml), comprehensive benchmarks comparing against net/http and fasthttp, and three concrete example servers (echo, HTTP, Redis). However, it's a specialist tool—not a general-purpose networking library.
Minimal external dependencies (only github.com/kavu/go_reuseport in go.mod), but platform-specific: relies on OS-level syscalls (epoll/kqueue) with separate implementations for Linux (evio_linux.go), BSD (internal/internal_bsd.go), Darwin, and OpenBSD. Breaking changes to the event callback API would require careful versioning. Single maintainer (tidwall) is a concentration risk, though the library is stable.
Active areas of work
No active PR or milestone data visible in file list. The repo appears stable with no recent churn indicated. Focus seems to be maintenance and serving as a dependency for Tile38 and similar projects.
🚀Get running
git clone https://github.com/tidwall/evio.git
cd evio
go mod download
go test ./...
cd examples/echo-server && go run main.go
Daily commands:
cd examples/echo-server && go run main.go
# Server listens on tcp://localhost:5000
telnet localhost 5000
Or run benchmarks: cd benchmarks && bash bench-echo.sh
🗺️Map of the codebase
evio.go— Core event loop API and entry point; defines Conn, Serve, and all public interfaces that every networking use case depends on.evio_unix.go— Unix-specific epoll/kqueue implementation; contains the low-level event loop syscalls that are central to evio's performance.internal/internal_linux.go— Linux epoll wrapper and file descriptor management; directly used by evio_unix.go for high-performance I/O.evio_std.go— Standard Go net package fallback implementation; required for cross-platform compatibility and testing on unsupported platforms.internal/notequeue.go— Lock-free event queue for inter-thread communication in multithreaded mode; critical for load balancing and data races.examples/echo-server/main.go— Canonical example showing the simplest Serve usage pattern; reference for all contributors writing networking code.go.mod— Declares only one dependency (go_reuseport); defines the minimal external footprint that must be maintained.
🧩Components & responsibilities
- evio.Serve() (Go goroutines, net.Listener, syscall) — Entry point; initializes listener, spawns event loop(s), invokes user Events callbacks on connection lifecycle and I/O readiness.
- Failure mode: Listener bind failure → error returned to caller; event loop panic → unhandled handler exception kills process.
- evio_unix.go (Event Loop) (epoll/kqueue syscalls, unsafe pointers for buffer management) — Core loop: calls epoll_wait (or kqueue) in tight loop, routes kernel events to handler callbacks, manages connection state.
- Failure mode: Epoll registration failure → connection silently ignored; handler panic → event loop exits, all connections drop.
- Conn (Connection Handle) — Per-connection state: read/write buffers,
🛠️How to make changes
Add a custom event handler for a new protocol
- Define your Events interface implementation with Data, Open, Close, and Read callbacks (
evio.go) - In your handler's Read callback, parse incoming bytes from Conn and call Conn.Write() for responses (
examples/redis-server/main.go) - Call evio.Serve(ln, events, evio.WithLoadBalancing(true)) to start the event loop with your handler (
evio.go)
Enable multithreaded event loop with load balancing
- Pass evio.WithLoadBalancing(true) option to evio.Serve() (
evio.go) - Ensure your Events handlers are thread-safe if they access shared state; check notequeue.go for inter-thread communication patterns (
internal/notequeue.go) - Use evio.WithNumEventLoops(n) to pin event loops to CPU cores (
evio.go)
Add support for a new Unix variant (kqueue-based OS)
- Create internal/internal_newos.go with kqueue constants and syscall definitions, following internal_darwin.go as template (
internal/internal_darwin.go) - Update evio_unix.go build constraints to include your OS in the unix-like platforms that use kqueue (
evio_unix.go) - Test with examples and benchmarks to ensure event loop stability (
benchmarks/bench.sh)
🔧Why these technologies
- epoll (Linux) / kqueue (macOS/BSD) — Provides O(1) edge-triggered notifications for high connection counts; bypasses Go's net.Conn scheduling overhead to match Redis/HAProxy performance.
- Lock-free notequeue (internal/notequeue.go) — Enables cross-thread event notifications in multithreaded mode without mutex contention; critical for scaling load balancing across cores.
- Go's unsafe and syscall packages — Direct kernel API access (epoll_wait, kqueue) requires low-level syscalls; unavoidable for the performance goal of matching Redis.
- Fallback to net package (evio_std.go) — Provides portability to Windows and other unsupported platforms; graceful degradation where native event loops unavailable.
- go_reuseport dependency — Enables SO_REUSEPORT socket option for kernel-level load balancing across multiple event loop threads on the same port.
⚖️Trade-offs already made
-
Direct syscalls instead of Go net package
- Why: Go's net.Conn multiplexes all I/O onto internal goroutine scheduler, adding latency and GC pressure unsuitable for microsecond-scale handlers.
- Consequence: User code must handle all I/O via callbacks; cannot use standard blocking Read/Write patterns; platform-specific syscall bugs require platform-specific fixes.
-
Synchronous single-threaded event loop by default
- Why: Simplest model matching libuv/libevent; avoids shared-state complexity and data race bugs.
- Consequence: Long-running handlers block all other connections; CPU-bound work must be offloaded to goroutines or external threads.
-
Edge-triggered epoll/kqueue only (no level-triggered fallback in core)
- Why: Edge-triggered is more efficient and matches Redis/HAProxy design; forces handler discipline (must fully drain before waiting again).
- Consequence: Handler must consume all available data in one Read callback or explicitly requeue; partially-read buffers lose events if not manually re-registered.
-
No TLS/encryption built-in
- Why: Focus scope on packet routing; encryption can be added at proxy layer (Tile38 use case) or by caller wrapping connections.
- Consequence: HTTPS, TLS termination, and encryption not available; users must implement or delegate to wrapper.
🚫Non-goals (don't propose these)
- Does not support Windows (no native epoll/kqueue equivalent; falls back to net package)
- Does not provide HTTP/REST framework; examples only show protocol parsing patterns
- Does not handle TLS or encryption; designed for plaintext or proxy scenarios
- Not a drop-in replacement for Go net or net/http; callback-based API is fundamentally different
- Not a full async/await runtime; single-threaded by default requires explicit goroutines for concurrent logic
🪤Traps & gotchas
Platform specificity: evio behavior differs by OS—epoll (Linux) vs kqueue (BSD/macOS) vs fallback (others). Tests must account for this or use build tags. Callback model: Events run synchronously in the event loop—blocking in a callback (like sleeping) stalls all connections. Buffer management: Output buffers are the caller's responsibility; no automatic backpressure handling like net.Conn. SO_REUSEPORT: Not available on all platforms; fallback code path may have different behavior. No graceful shutdown primitives: Must design shutdown logic externally (via Shutdown action).
🏗️Architecture
💡Concepts to learn
- epoll (Linux) — The core I/O multiplexing mechanism evio uses on Linux—understanding how epoll_wait batches events and triggers callbacks is essential to understand why evio is fast
- kqueue (BSD/macOS) — The BSD/macOS equivalent to epoll that evio uses via
evio_unix.go—has different semantics (kevent64 syscall, filter-based) that require separate implementation - SO_REUSEPORT — Socket option evio leverages (via
go_reuseportdependency) to allow multiple listener sockets on the same address for load balancing across event loops - Event-driven architecture / Reactor pattern — evio's entire design—single-threaded event loop, callback-based handlers for connection events—is the Reactor pattern; understanding this pattern is necessary to use evio correctly
- Memory-mapped I/O and zero-copy buffers — evio avoids Go allocations in hot paths by reusing buffers across events; the design assumes careful buffer management by the application (see Data callback in examples)
- Load balancing strategies (LoopBalance, LeastConnBalance, IPHashBalance) — evio supports multiple event loops with configurable work distribution; choosing the right balance strategy affects throughput and latency characteristics
- Connection detachment (Detach event) — evio's Detach callback allows handing off a raw socket to external code (e.g., for TLS termination or protocol switching), requiring understanding of file descriptor ownership and lifecycle
🔗Related repos
tidwall/tile38— Canonical production user of evio—a geospatial database that depends on evio for its high-throughput network layervalyala/fasthttp— Alternative high-performance HTTP framework for Go; direct benchmark competitor shown inbenchmarks/libuv/libuv— Inspiration and spiritual predecessor—the C library evio emulates for Go, handling cross-platform event loops (epoll/kqueue/IOCP)golang/go— evio's net package fallback (evio_std.go) wraps stdlib; understanding Go's runtime scheduler and net package design is necessary for optimizationeranyanay/nes— Similar Go event-loop networking library; useful reference for alternative design decisions in the same space
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add platform-specific integration tests for evio_unix.go, evio_darwin.go, and evio_linux.go
The repo has platform-specific implementations (evio_unix.go, internal/internal_*.go for BSD/Darwin/Linux/OpenBSD) but evio_test.go appears to lack comprehensive platform-specific test coverage. Given that evio makes direct epoll/kqueue syscalls, platform-specific behavior is critical. A new contributor could add matrix-based CI tests and unit tests validating syscall behavior per platform.
- [ ] Expand evio_test.go with platform-specific subtests using build tags (+build linux, +build darwin, etc.)
- [ ] Add test cases for epoll-specific behavior in evio_linux.go (edge-triggered vs level-triggered)
- [ ] Add test cases for kqueue-specific behavior in evio_unix.go and internal/internal_darwin.go
- [ ] Update .github/workflows/go.yml to run tests across multiple OS runners (ubuntu, macos) to catch platform regressions
Add Go 1.21+ minimum version enforcement and update deprecated dependencies
The go.mod specifies go 1.15 (released Oct 2020, EOL ~Jun 2021). The project depends on github.com/kavu/go_reuseport which may have security issues or compatibility problems with modern Go versions. A contributor could audit this dependency, verify compatibility with recent Go versions (1.21+), and potentially replace it with stdlib equivalents or a maintained fork.
- [ ] Review github.com/kavu/go_reuseport for security advisories and maintenance status
- [ ] Test evio against Go 1.21+ to identify any compatibility issues
- [ ] If go_reuseport is unmaintained, evaluate replacing it with a maintained alternative or Go 1.23+ stdlib support
- [ ] Update go.mod to reflect the new minimum Go version and refresh go.sum
Add comprehensive benchmark comparison documentation and automated benchmark reporting
The benchmarks/ directory contains test scripts (bench-echo.sh, bench-http.sh, bench-redis.sh) and output files (echo.txt, redis*.txt) but benchmarks/README.md and the main README lack detailed guidance on running and interpreting benchmarks. A contributor could document the benchmark suite, add a GitHub Actions workflow to run benchmarks on each commit, and generate a comparison report.
- [ ] Expand benchmarks/README.md with detailed instructions on running each benchmark script and interpreting results
- [ ] Create a GitHub Actions workflow (.github/workflows/benchmarks.yml) to run benchmarks/bench.sh on each PR against main branch
- [ ] Add a script to parse benchmark output and generate a markdown report comparing current vs baseline performance
- [ ] Document expected performance targets (vs Redis/Haproxy/net.http mentioned in README) in benchmarks/README.md
🌿Good first issues
- Add benchmarks for UDP and Unix socket performance in
benchmarks/(currently only TCP/HTTP/Redis are benchmarked). Compare against libuv or libevent if possible. - Expand
evio_test.goto test theDetachevent and detached connection behavior—currently sparse coverage of connection lifecycle edge cases. - Document the load balancing strategies in README with concrete examples: add code samples showing
LoopBalance,LeastConnBalance, andIPHashBalanceinexamples/loadbalance/.
⭐Top contributors
Click to expand
Top contributors
📝Recent commits
Click to expand
Recent commits
fe60817— Delete FUNDING.yml (tidwall)c72befa— Create FUNDING.yml (tidwall)5e8d303— Update dependencies (tidwall)e7a23d2— Delete .travis.yml (tidwall)aa3f301— Update README.md (tidwall)72b3663— Create go.yml (tidwall)399d938— Add a deadline for epollwait (tidwall)34ff08a— Fix tick event consuming 100% CPU (tidwall)7a03219— Avoid allocation (tidwall)99e633d— Reuse connection output page (tidwall)
🔒Security observations
The evio codebase demonstrates reasonable security posture for a systems programming library, but has notable concerns. The most critical issue is the use of Go 1.15, which is severely outdated and lacks security patches. The direct syscall approach (epoll/kqueue) is intentional but requires careful input validation. The single third-party dependency should be audited for vulnerabilities. No hardcoded secrets, injection vulnerabilities, or obvious misconfigurations were identified in the visible structure. Recommendations include updating Go version, verifying dependency security, implementing comprehensive input validation for syscall handlers, and establishing a vulnerability disclosure policy.
- Medium · Outdated Go Version —
go.mod. The go.mod file specifies Go 1.15, which was released in August 2020 and is now significantly outdated. This version lacks security patches and bug fixes from newer releases. Go 1.15 reached end-of-life and no longer receives security updates. Fix: Update to a supported Go version (1.21 or later). Review and test the codebase for compatibility with newer Go versions. - Medium · Unverified Third-party Dependency —
go.mod - github.com/kavu/go_reuseport v1.5.0. The dependency 'github.com/kavu/go_reuseport' at version 1.5.0 lacks version pinning specificity and the package's maintenance status is unclear. This could pose supply chain risks if the dependency is compromised or abandoned. Fix: Verify the dependency is actively maintained and regularly audited. Consider using 'go get -u' to update to the latest secure version. Run 'go mod tidy' and 'go mod verify' to ensure integrity. Consider running 'nancy' or 'go list -json -m all | nancy sleuth' to check for known vulnerabilities in dependencies. - Low · Low-level System Calls Without Input Validation Framework —
evio.go, evio_unix.go, evio_darwin.go, internal/. The project makes direct epoll and kqueue syscalls (as stated in README), bypassing Go's standard net package safety mechanisms. While intentional for performance, this increases the attack surface for issues like buffer overflows or file descriptor leaks if input handling is not carefully implemented. Fix: Ensure comprehensive bounds checking and input validation in all syscall handlers. Implement proper error handling for syscall failures. Add fuzzing tests to identify edge cases. Review C-style code patterns for common vulnerabilities like integer overflows or off-by-one errors. - Low · Missing Security Policy Documentation —
Repository root. No SECURITY.md file or documented vulnerability disclosure process is visible in the repository structure. This makes it difficult for security researchers to responsibly report vulnerabilities. Fix: Create a SECURITY.md file documenting how to responsibly report security vulnerabilities. Include contact information and expected response timeline.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.