RepoPilotOpen in app →

fastly/pushpin

A proxy server for adding push to your API, used at the core of Fastly's Fanout service

Healthy

Healthy across all four use cases

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 1d ago
  • 3 active contributors
  • Apache-2.0 licensed
Show all 7 evidence items →
  • CI configured
  • Tests present
  • Small team — 3 contributors active in recent commits
  • Single-maintainer risk — top contributor 96% of recent commits

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 "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/fastly/pushpin)](https://repopilot.app/r/fastly/pushpin)

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/fastly/pushpin on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: fastly/pushpin

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:

  1. 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.
  2. 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.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/fastly/pushpin 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

GO — Healthy across all four use cases

  • Last commit 1d ago
  • 3 active contributors
  • Apache-2.0 licensed
  • CI configured
  • Tests present
  • ⚠ Small team — 3 contributors active in recent commits
  • ⚠ Single-maintainer risk — top contributor 96% of recent commits

<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 fastly/pushpin repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/fastly/pushpin.

What it runs against: a local clone of fastly/pushpin — 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 fastly/pushpin | Confirms the artifact applies here, not a fork | | 2 | License is still Apache-2.0 | Catches relicense before you depend on it | | 3 | Default branch main exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 31 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>fastly/pushpin</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of fastly/pushpin. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/fastly/pushpin.git
#   cd pushpin
#
# 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 fastly/pushpin and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "fastly/pushpin(\\.git)?\\b" \\
  && ok "origin remote is fastly/pushpin" \\
  || miss "origin remote is not fastly/pushpin (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(Apache-2\\.0)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"Apache-2\\.0\"" package.json 2>/dev/null) \\
  && ok "license is Apache-2.0" \\
  || miss "license drift — was Apache-2.0 at generation time"

# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
  && ok "default branch main exists" \\
  || miss "default branch main no longer exists"

# 4. Critical files exist
test -f "src/bin/pushpin.rs" \\
  && ok "src/bin/pushpin.rs" \\
  || miss "missing critical file: src/bin/pushpin.rs"
test -f "src/connmgr/mod.rs" \\
  && ok "src/connmgr/mod.rs" \\
  || miss "missing critical file: src/connmgr/mod.rs"
test -f "src/core/http1/mod.rs" \\
  && ok "src/core/http1/mod.rs" \\
  || miss "missing critical file: src/core/http1/mod.rs"
test -f "src/core/config.rs" \\
  && ok "src/core/config.rs" \\
  || miss "missing critical file: src/core/config.rs"
test -f "src/connmgr/server.rs" \\
  && ok "src/connmgr/server.rs" \\
  || miss "missing critical file: src/connmgr/server.rs"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 31 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~1d)"
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/fastly/pushpin"
  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).

</details>

TL;DR

Pushpin is a reverse proxy server written in Rust and C++ that enables transparent WebSocket, HTTP streaming, and HTTP long-polling on top of any stateless backend API. It sits between clients and your backend, letting you push data to connected clients via a simple HTTP control API (POST http://localhost:5561/publish) while your backend responds to proxied requests with GRIP headers (Grip-Hold, Grip-Channel) to indicate how Pushpin should handle the connection. Monolithic structure: src/bin/ contains 7 entry points (pushpin-proxy, pushpin-connmgr, pushpin-handler, m2adapter, pushpin-publish, pushpin-legacy, pushpin). Core logic is in src/connmgr/ (connection management with batch.rs, pool.rs, tls.rs, websocket.rs, zhttppacket.rs) and src/core/ (buffer.rs). Examples in examples/config/ show pushpin.conf routing config. Hybrid Rust+C++ via FFI bindings generated by cbindgen.

👥Who it's for

Backend API developers and DevOps engineers building real-time services (chat apps, dashboards, notifications) who want transparent realtime push without rewriting their application layer. Used by Fastly's Fanout service and anyone deploying the core Pushpin proxy.

🌱Maturity & risk

Production-ready and actively maintained by Fastly. The codebase is substantial (1.8M Rust, 1.7M C++), versioned at 1.42.0-dev with recent CI/workflows (.github/workflows/suite.yml, codeql.yml), benchmarks in place (benches/), and structured build tooling (build.rs, cbindgen.toml). This is a serious infrastructure component, not experimental.

Moderate risk: the project has a large C++ component (1.7M LOC) alongside Rust, which increases maintenance surface. Dependencies include security-sensitive libraries (openssl 0.10.72, rustls, zmq) that require careful updating. The 'do-qmake' and 'do-cpp-build' features indicate QMake-driven C++ compilation—build failures from QMake quirks are possible. Single organization (Fastly) ownership means less community diversity in patches.

Active areas of work

Version 1.42.0-dev indicates active development. The codebase has modern CI workflows (GitHub Actions), CodeQL security scanning enabled, and structured benchmarking (criterion benches for client, server, list, memorypool). No specific milestone data visible, but the presence of CHANGELOG.md and semantic versioning suggests steady release cadence.

🚀Get running

git clone https://github.com/fastly/pushpin.git
cd pushpin
cargo build --release
cargo run --bin pushpin -- examples/config/pushpin.conf

Daily commands: For development: cargo build && cargo run --bin pushpin -- examples/config/pushpin.conf. For connection manager: cargo run --bin pushpin-connmgr. The Makefile likely wraps build steps; check make help or Makefile for targets. The project requires QMake/C++ toolchain if C++ parts are compiled (do-cpp-build feature enabled by default).

🗺️Map of the codebase

  • src/bin/pushpin.rs — Main entry point and orchestrator for the pushpin proxy server; every contributor must understand the startup flow and component initialization
  • src/connmgr/mod.rs — Connection manager module that handles WebSocket/HTTP streaming proxying; core to understanding how client and backend connections are managed
  • src/core/http1/mod.rs — HTTP/1.1 protocol implementation for request/response handling; fundamental to understanding request proxying logic
  • src/core/config.rs — Configuration parsing and validation; essential for understanding how pushpin is configured and routed
  • src/connmgr/server.rs — Backend HTTP server connection handler that processes responses and determines proxy behavior
  • src/connmgr/zhttppacket.rs — ZeroMQ HTTP packet serialization for inter-component communication; critical IPC protocol
  • Cargo.toml — Project manifest showing Rust dependencies (mio, httparse, jsonwebtoken) and build features; required for understanding external constraints

🛠️How to make changes

Add a new HTTP header inspection rule

  1. Define the header matching logic in the HTTP protocol handler (src/core/http1/protocol.rs)
  2. Add configuration option to parse the rule from config files (src/core/config.rs)
  3. Implement the inspection in the connection server handler (src/connmgr/server.rs)
  4. Test with example configuration (examples/config/pushpin.conf)

Add a new authentication method (e.g., OAuth)

  1. Create new module under src/core/ for auth logic (e.g., src/core/oauth.rs) (src/core/mod.rs)
  2. Extend JWT validation or add new token validator (src/core/jwt.rs)
  3. Integrate into connection server request handling (src/connmgr/server.rs)
  4. Add configuration option to enable/configure auth (src/core/config.rs)

Add support for a new backend communication protocol

  1. Create new packet type alongside zhttppacket in connmgr (src/connmgr/zhttppacket.rs)
  2. Implement socket wrapper for the protocol (src/connmgr/zhttpsocket.rs)
  3. Extend connection manager to handle new protocol variant (src/connmgr/mod.rs)
  4. Add configuration parsing for the new protocol (src/core/config.rs)

Add a new proxy mode or request transformation

  1. Define request transformation logic in HTTP protocol layer (src/core/http1/protocol.rs)
  2. Implement transformation in client or server handler (src/core/http1/client.rs)
  3. Add configuration option to enable the transformation (src/core/config.rs)
  4. Test end-to-end via connection manager (src/connmgr/mod.rs)

🔧Why these technologies

  • Rust + mio — High-performance async I/O with minimal memory overhead; critical for handling thousands of concurrent connections with deterministic behavior
  • ZeroMQ (ZMTP protocol via zhttppacket) — Language-agnostic IPC allows backend handlers to be written in any language (Python, Node.js, Go, etc.) while maintaining reliable message passing
  • HTTP/1.1 protocol as primary backend interface — Decouples pushpin from backend framework; any standard web server can handle requests without special adapters
  • C++ core utilities (bufferlist, config, event loop) — Legacy codebase portions reused for proven stability; gradual Rust migration maintains compatibility
  • Connection pooling + resolver — Reduces backend connection overhead and enables efficient fan-out to multiple backend instances

⚖️Trade-offs already made

  • Translate WebSocket connections into series of HTTP requests rather than native WebSocket backend support

    • Why: Allows any HTTP server to handle WebSocket clients without WebSocket library dependency; universal compatibility
    • Consequence: Slight overhead per frame, but eliminates framework lock-in and simplifies backend development
  • ZeroMQ IPC instead of direct HTTP/REST for inter-component communication

    • Why: Reliable message queuing with automatic retries; multiple handlers can fan-out; decoupled lifecycle
    • Consequence: Adds operational complexity (requires ZMQ broker); higher latency than direct function calls
  • Config-file-based routing instead of dynamic API

    • Why: Prevents accidental routing misconfiguration at runtime; explicit control for ops teams
    • Consequence: Requires reload/restart for routing changes; less flexible than per-request routing
  • Memory pooling and custom buffer management

    • Why: Reduce GC pause latency and allocator pressure under high load
    • Consequence: More complex memory lifecycle; potential for use-after

🪤Traps & gotchas

  1. QMake/C++ toolchain required: the default features include 'do-cpp-build', which invokes QMake. If you're on a system without Qt/QMake, you must disable this feature: cargo build --no-default-features. 2. ZMQ socket lifecycle: inter-process communication via zmq is asynchronous; check that zmq daemons (handler, connmgr, proxy) are running in correct order—proxy won't work if handler isn't listening. 3. GRIP header case-sensitivity: backend responses must use exact casing (Grip-Hold, Grip-Channel, not grip-hold); HTTP/2 normalizes headers, so test with HTTP/1.1. 4. Pinned dependency versions: openssl (0.10.72) and time (0.3.41) are pinned; cargo might fail on certain OpenSSL system library versions. 5. Config file path: pushpin.conf is relative to working directory; run from repo root or provide absolute paths.

🏗️Architecture

💡Concepts to learn

  • HTTP Streaming with chunked transfer encoding — Pushpin's core feature: keeping HTTP responses open indefinitely with Transfer-Encoding: chunked to push data incrementally; you must understand when Content-Length is stripped and chunked encoding is forced
  • Pub-Sub with ZMQ (ZeroMQ) — Pushpin uses ZMQ sockets for inter-process message passing between the proxy, handler, and publisher processes; misunderstanding ZMQ connection patterns (PUB-SUB, REQ-REP) causes race conditions
  • WebSocket protocol upgrade and frame parsing — Pushpin translates WebSocket frames into HTTP requests sent to the backend, then converts backend HTTP responses back into WebSocket frames; you need to understand the handshake and opcode handling
  • Reverse proxy connection pooling and keep-alive — The connmgr module manages TCP connection reuse to backend handlers via src/connmgr/pool.rs; improper tuning causes connection exhaustion or excessive reconnects
  • Long polling vs. Server-Sent Events vs. WebSocket trade-offs — Pushpin supports all three; understanding latency, resource usage, and browser compatibility of each helps you configure Grip-Hold behavior correctly for your use case
  • TLS session resumption and client certificate validation — Pushpin has separate rustls (modern) and OpenSSL (legacy) backends; understanding cipher suite negotiation and cert pinning is critical for security-sensitive deployments
  • Event-driven async I/O with mio — Pushpin uses mio's epoll/kqueue event loop to handle thousands of concurrent connections efficiently; understanding Mio's readiness-based model vs. completion-based async/await clarifies performance bottlenecks
  • sockjs/sockjs-server — Similar realtime transport abstraction (WebSocket + fallbacks), but server-side; Pushpin is a transparent proxy alternative that doesn't require app changes
  • socketio/socket.io — Popular realtime library for bidirectional client-server communication; Pushpin solves the same problem but at the reverse-proxy layer, agnostic to client libraries
  • fastly/pushpin-cli — Command-line tools and client libraries for the Pushpin control API; required for publishing data to Pushpin from your backend
  • caddy/caddy — Reverse proxy written in Go; architectural peer offering similar request routing, TLS, and plugin extensibility but without native realtime push semantics
  • nghttp2/nghttp2 — HTTP/2 and HTTP/3 library used internally by some Pushpin configurations; understanding its frame handling clarifies Pushpin's protocol interop

🪄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 comprehensive unit tests for src/connmgr/zhttpsocket.rs and src/connmgr/zhttppacket.rs

These modules handle the critical zHTTP protocol packet parsing and socket communication at the core of Pushpin's connection management. The file structure shows no corresponding test files (no zhttpsocket_tests.rs or zhttppacket_tests.rs), and given the complexity of protocol parsing and the proxy's reliance on correct packet handling, this is a high-value safety improvement. Tests should cover packet serialization/deserialization edge cases, malformed packet handling, and connection state transitions.

  • [ ] Create src/connmgr/zhttpsocket_tests.rs with tests for ZHttpSocket initialization, read/write operations, and connection state transitions
  • [ ] Create src/connmgr/zhttppacket_tests.rs with tests for packet parsing (ids, body, headers), edge cases (empty packets, oversized bodies), and round-trip serialization
  • [ ] Add integration tests in tests/ directory that verify packet exchange between mock client and server components
  • [ ] Update Cargo.toml dev-dependencies if needed for mock/fixture utilities
  • [ ] Document test coverage in CONTRIBUTING.md (currently missing)

Add GitHub Actions workflow for security audit and dependency updates

The repo has .cargo/audit.toml configured and demonstrates security awareness, but there is no GitHub Actions workflow file to regularly run cargo audit and flag vulnerabilities in CI. With dependencies like openssl, zmq, and rustls that are security-sensitive, automating audit checks prevents vulnerable dependencies from being merged. This complements the existing codeql.yml workflow.

  • [ ] Create .github/workflows/audit.yml with job to run cargo audit --deny warnings on every pull request and push to main
  • [ ] Add job to check for outdated dependencies using cargo outdated or Dependabot integration
  • [ ] Configure workflow to fail if audit finds vulnerabilities or high-risk outdated crates
  • [ ] Document the audit policy in SECURITY.md (which exists but may lack details on CI enforcement)
  • [ ] Add status badge to README.md showing audit pass/fail status

Extract and document the C++ FFI bindings layer (cbindgen integration)

The Cargo.toml shows crate-type includes 'staticlib' and cbindgen.toml exists, indicating Pushpin exposes a C FFI interface. However, there is no dedicated documentation or module-level comments explaining which Rust functions are exported to C++, their safety contracts, or calling conventions. This is critical for maintainability given the mixed Rust/C++ codebase (src/core/ has many .cpp/.h files). A documented FFI module would help contributors understand integration points and prevent unsafe code regressions.

  • [ ] Create src/ffi.rs or src/ffi/mod.rs that aggregates and documents all #[no_mangle] pub extern "C" functions
  • [ ] Add rustdoc comments to each exported function explaining safety preconditions, pointer ownership, and calling conventions
  • [ ] Create docs/FFI_BINDINGS.md documenting the Rust-C++ interface contract, with examples of correct/incorrect C++ usage
  • [ ] Update cbindgen.toml with output path documentation and any generation requirements
  • [ ] Add CI job (to existing suite.yml or new workflow) to verify cbindgen output remains in sync with source

🌿Good first issues

  • Add unit tests for src/connmgr/batch.rs: The batch module has no visible test coverage; write tests for batch message assembly, capacity limits, and timestamp ordering to ensure publish reliability
  • Document GRIP header parsing in src/connmgr/connection.rs: The connection.rs file is a critical state machine but lacks inline documentation on how Grip-Hold values (stream, response, poll) map to connection states and which response codes are allowed
  • Add integration test for WebSocket -> HTTP streaming failover: No visible test in benches/ or src/ that verifies a WebSocket client can gracefully degrade to long-polling if the backend sends appropriate GRIP headers; this is a key feature

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 385b173 — handler: fix authority matching for internal routing (#48342) (jkarneges)
  • a91ca7e — remove c++ logging system and always log to the rust logger (#48339) (jkarneges)
  • f326bdc — add ffi for rust's std thread api and use it from c++ (#48338) (jkarneges)
  • f14fdd8 — log: add log_init wrapper, plus minor refactoring (#48340) (jkarneges)
  • d38f553 — reflow c++ comments using 100 char line limit, plus corrections (#48337) (jkarneges)
  • a78d17c — add ffi and rust wrapper for domainmap (#48336) (jkarneges)
  • 78db26e — filewatcher: support relative paths (#48335) (jkarneges)
  • 902f5fb — proxy/handler: send logs via the rust log crate (#48334) (jkarneges)
  • 48c9c7c — proxy: don't crash if startup fails (#48333) (jkarneges)
  • dbc99d5 — check c++ formatting in CI and add "make fmt" (#48330) (jkarneges)

🔒Security observations

Failed to generate security analysis.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · fastly/pushpin — RepoPilot