Skip to content

Releases: HackStrix/herd

Adding process security using cgroups and namespaces

13 Mar 02:26
dd632b8

Choose a tag to compare

What's Changed

  • Linux sandbox with cgroup support [ignoring sandbox in MacOS] by @HackStrix in #5
  • namespace isolation and new integration tests for CI by @HackStrix in #6
  • Feature/observer and polish by @HackStrix in #8
  • feat: extract SessionRegistry interface and technical debt cleanup by @HackStrix in #9

Full Changelog: v0.2.0...v0.3.1

v0.2.0

10 Mar 03:33
b932308

Choose a tag to compare

Release Notes:

  • Adding support for websockets with passive tracking and minimal overhead
  • Allow tracking multiplexing connection to the same worker session
  • Worker Reuse is now a configurable option.

What's Changed

  • fix(ttl): correctly track active connections for sweeper by @HackStrix in #3
  • ci: add GitHub Actions workflow for tests and linting by @HackStrix in #4

Full Changelog: v0.1.4...v0.2.0

v0.1.4

09 Mar 05:29
e3b5e4c

Choose a tag to compare

What's Changed

  • updating module api, fixing naming convention, and removing race cond… by @HackStrix in #2

New Contributors

Full Changelog: v0.1.2...v0.1.4

v0.1.2: The Session-Affine Process Pool

07 Mar 21:42

Choose a tag to compare

Release Notes: v0.1.2

Release Date: March 07, 2026

This release focuses on critical concurrency bug fixes, API improvements for worker health checks, and comprehensively expanding documentation and examples.

🐛 Bug Fixes

  • Pool Scaling Race Condition: Fixed a critical bug in Pool.maybeScaleUp() where highly concurrent Acquire calls could circumvent the max worker limit, causing the pool to spawn excessive workers and crash when attempting to release them back into a full channel. We introduced a pendingAdds concurrency counter to strictly bound scaling.

✨ New Features & API Improvements

  • ProcessFactory Enhancements: Added flexible configuration options for ProcessFactory:
    • WithEnv(kv string): Inject custom environment variables (supports {{.Port}} templating).
    • WithHealthPath(path string): Customize the HTTP path polled for liveness (defaults to "/health").
    • WithStartTimeout(duration): Configure the maximum time allowed for the process to successfully start and report healthy.
  • Robust Health Polling: Migrated health check polling from a static 30-retry limit to a clean, context-aware timeout mechanism.

📚 Documentation & Examples

  • Comprehensive README: Added a complete README.md covering Herd's core invariants, architecture, and feature set.
  • Ollama Multi-Agent Gateway: Introduced a fully fleshed-out example in examples/ollama demonstrating how to use Herd as a session-affine LLM proxy.
    • The README was heavily updated to showcase this runnable code, along with curl usage queries.
    • Added a concurrency stress-test suite (TestConcurrentChat) for the Ollama integration.

⚙️ Chores

  • Go Version Compatibility: Updated go.mod files to ensure backward compatibility with Go 1.21 / 1.22.

v0.1.0: The Session-Affine Process Pool

07 Mar 19:06

Choose a tag to compare

Pre-release

v0.1.0: The Session-Affine Process Pool

The Pain

Running stateful binaries — Ollama, headless Chromium, Python REPLs — behind a shared gateway usually means one of two terrible options: build complex container orchestration to isolate users, or share a single process and leak state across sessions. Neither is acceptable.

The Fix

Herd gives you a strict 1:1 session-to-worker mapping, automatic crash recovery, and a built-in reverse proxy — in 10 lines of Go.

factory := herd.NewProcessFactory("./my-binary", "--port", "{{.Port}}")

pool, _ := herd.New(factory,
    herd.WithAutoScale(2, 10),
    herd.WithTTL(5 * time.Minute),
    herd.WithCrashHandler(func(sid string) { log.Printf("session %s lost", sid) }),
)

http.ListenAndServe(":8080", proxy.NewReverseProxy(pool, func(r *http.Request) string {
    return r.Header.Get("X-Session-ID")
}))

Every request with X-Session-ID: abc123 always routes to the same OS process — no external coordination, no sticky sessions, no Redis. When a process crashes, only that one session is affected; the pool spawns a replacement automatically.


What's in the Box

WorkerFactory[C] Spawn any OS binary with OS-allocated ports and /health polling
Pool[C].Acquire Singleflight-safe session affinity with zero race conditions
Session.Release Explicit lifetime management — workers are never leaked
WithAutoScale(min, max) Elastic pool sizing; blocks Acquire at ceiling
WithTTL(d) Idle session eviction — reclaim workers from abandoned sessions
WithCrashHandler(fn) Hook into crash recovery — clean up DB state, alert users
NewReverseProxy Full HTTP gateway in one call; session acquire + proxy + release

The NewReverseProxy 5-liner

pool, _ := herd.New(herd.NewProcessFactory("./ollama", "serve", "--port", "{{.Port}}"))

http.ListenAndServe(":8080", proxy.NewReverseProxy(pool, func(r *http.Request) string {
    return r.Header.Get("X-Session-ID") // route by any header, cookie, or path param
}))

NewReverseProxy returns a standard http.Handler. Wire it into any existing net/http multiplexer — no framework lock-in.


Architecture

See docs/ARCHITECTURE.md for the Mermaid request-lifecycle diagrams, component breakdown, and full design rationale.


Installation

go get github.com/hackstrix/herd@v0.1.0

Requires Go 1.21+.


Herd is intentionally small. The surface area is three types — WorkerFactory, Pool, and ReverseProxy — and one rule: a session always talks to the same process. That's it.