Skip to content
CAI
Software that uses CAICheck a score

contribsys/einhorn

70.4

Strong · 19 September 2026

2.7k

lines of production code

Ruby

primary language

1

measurement over time

CAI band scale
CAI lens gauges

What this system is

Einhorn is a process manager for Ruby applications that handles worker lifecycle, socket binding, and graceful upgrades. It exposes bound sockets and command interfaces to workers via environment variables and supports non-interactive scripting. The system ensures reliability through robust signal handling, state migration, and configurable timeout mechanisms for stuck processes.

How it got here

2012 — Einhorn 1.0 modernization and stability

12 changes.

This period focused on modernizing the Einhorn codebase to require Ruby 2.5+, introducing explicit socket binding APIs, and migrating the internal command protocol from JSON to YAML. Significant efforts were made to improve reliability through robust signal handling, safe deserialization, and comprehensive unit testing with Minitest.

2014–2016 — integration test suite expansion

6 changes.

This period focused on building a comprehensive integration test suite for Einhorn, specifically targeting the worker upgrade process, environment variable handling, and signal management. The work involved creating helper modules and various fixture scripts to simulate complex scenarios such as re-execution, process death signals, and environment cleanup during upgrades.

Behavioural changes

Einhorn 1.0.0: Modernized codebase, Ruby 2.5+ requirement, and updated socket API

This release updates Einhorn to version 1.0.0, dropping support for Ruby versions below 2.5 and requiring Ruby 2.5.0 for code formatting via the \standard\ gem. The project now uses \YAML.safe\_load\ for compatibility with Ruby 3.1+, removes the \fiddle\ gem as a hard dependency (making it optional for the \-k\ flag), and adds \logger\ and \readline\ for Ruby 4.0 support. For users, the socket binding API has changed: the \-b\ flag now explicitly binds addresses (e.g., \-b 127.0.0.1:1234\), and file descriptors are exposed via \EINHORN\_FD\_N\ and \EINHORN\_FD\_COUNT\ environment variables instead of the previous \EINHORN\_FDS\ format. The command socket file descriptor is now passed via \EINHORN\_SOCK\_FD\ (using the \-g\ flag) rather than \EINHORN\_FD\. Additionally, new options include \--reexec-as\ for custom upgrade commands, \--drop-env-var\ to clean the environment, and \--nice\ for process priority control.

(repo-wide) · high confidence

Einhorn 1.1.1: Protocol migration, Linux process management, and upgrade reliability

Einhorn upgrades to version 1.1.1, introducing several behavioral changes to improve reliability and compatibility. The command-socket protocol switches from JSON to YAML, requiring clients to use the new \send\_command\ and \receive\_message\ methods. On Linux, Einhorn now uses \prctl\ to set the parent-death signal for workers, ensuring they are cleaned up if the master process dies. Upgrade behavior is enhanced with a configurable \--signal-timeout\ that escalates to SIGKILL for stuck workers, and the system now tracks consecutive unacked worker deaths to detect spin-up failures. Additionally, workers receive a unique index via \EINHORN\_CHILD\_INDEX\, and the environment variable for the command socket has changed from \EINHORN\_FD\ to \EINHORN\_SOCK\_FD\.

lib/einhorn · high confidence

Einhorn introduces explicit socket binding, renames command-socket flag, and adds non-interactive shell execution

The Einhorn process manager now uses a new \-b ADDR\ (or \--bind ADDR\) option to explicitly specify server socket addresses and ports, replacing the previous implicit parsing of command arguments like \srv:IP:PORT\; bound sockets are exposed to workers via \EINHORN\_FD\_N\ environment variables. The existing flag to expose the command socket as a file descriptor has been renamed from \-b\ to \-g\ (or \--command-socket-as-fd\), and the corresponding environment variable is now \EINHORN\_SOCK\_FD\. Additionally, \einhornsh\ now supports a new \-e CMD\_LINE\_SEQ\ (or \--execute\) option, allowing users to run command sequences non-interactively, which facilitates scripting and automation outside of the interactive REPL.

bin · high confidence

Improved socket error handling and subscription tracking in event connections

The event handling layer now robustly manages unexpected socket errors (such as ECONNRESET) by logging them and closing connections, rather than failing silently or crashing. Additionally, client connections now track subscriptions, allowing them to be restored during upgrades for backward compatibility. Logging for client connection/disconnection events has been demoted from info to debug level to reduce noise.

lib/einhorn/event · high confidence

Signal handling moved to event loop and command parsing hardened

Signal handlers (INT, TERM, QUIT, HUP, USR2) now run asynchronously via the event loop instead of directly in the signal context, preventing race conditions during worker signaling and state changes. Additionally, the command interface now validates incoming messages using the transport layer's deserializer, returning a 'Could not parse command' error for invalid input instead of crashing.

lib/einhorn/command · high confidence

State schema expansion and safe YAML deserialization

The internal state structure has been expanded to expose bound ports, support smooth upgrades, track last upgrade times, and manage worker nice levels, while the command-socket protocol now uses SafeYAML for deserialization to prevent unsafe object loading. Additionally, the system now automatically migrates state formats across reloads by adding new keys and removing obsolete ones, ensuring compatibility between different Einhorn versions during upgrades.

lib · high confidence

Updated examples to use new Einhorn socket API and EventMachine-LE

The example scripts (thin\_example, time\_server, pool\_worker) have been updated to reflect changes in the Einhorn worker interface. The examples now use the \Einhorn::Worker.socket!\ method and \Einhorn::Worker.einhorn\_fd\_count\ instead of parsing command-line arguments or environment variables directly, and they require \eventmachine-le\ and \thin/attach\_socket\ to handle socket attachment. Additionally, the \pool\_worker\ example now uses \loop do\ instead of \while true\ for its main work loop.

example · high confidence

Test coverage

Added env\_printer fixture for integration testing; Added integration test fixture for server exit during upgrade; Added integration test helpers for Einhorn; Added integration tests for Einhorn upgrade, environment, and signal handling; Added pdeathsig\_printer test fixture; Added test fixture for Einhorn worker upgrade scenarios; Added unit tests for Einhorn client, worker pool, and command logic; Added unit tests for state updates and preload behavior; Migrate command interface tests to Minitest and YAML serialization; Migrate test suite from Test::Unit/Shoulda to Minitest.

Dependencies

Modernize gemspec and update development dependencies

The einhorn gemspec has been updated to require Ruby 2.5.0 or higher, explicitly set the MIT license, and add metadata URIs for bug tracking and documentation. Development dependencies have been refreshed: shoulda and mocha are replaced by minitest (\~\> 5) and mocha (\~\> 2), with rake (\~\> 13) and subprocess (\~\> 1) added. Runtime dependencies now explicitly include the logger and readline gems, while the JSON dependency has been removed. The Gemfile also adds standard and debug gems for the development and test groups.

(dependencies) · high confidence

Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.

How this codebase got here

Baseline

  • First survey — no prior run to compare against. CAI 70.

Lenses

  • Code Health 96
  • Architecture 100
  • Maturity 54
  • Readiness 72
  • Security 99

Changes since last survey

  • 300 commits — 276 feature/other, 24 fixes

By area

  • lib/einhorn — 115 commits
  • (repo) — 51 commits
  • (root) — 47 commits
  • lib/einhorn.rb — 23 commits
  • test/integration — 23 commits
  • bin/einhorn — 22 commits
  • test/unit — 9 commits
  • bin/einhornsh — 5 commits
  • .github/workflows — 2 commits
  • example/plugin.rb — 2 commits
  • example/time_server — 1 commit

Notable commits

  • fix: Add programmatic specification of the license. Fixes #26
  • fix: Add ruby 2.0 fix to bundler
  • fix: Fix --quiet
  • fix: Fix PID rollover bug
  • fix: Fix UNIXSocket leak
  • fix: Fix broken Einhorn image URL
  • fix: Fix deprecation warnings in Ruby and Bundler API usage
  • fix: Fix error message copy
  • fix: Fix invalid file extension
  • fix: Fix rake tests
  • fix: Fix regression with closing file descriptors before exec
  • fix: Fix several testsuite warnings
  • fix: Fix the docs to match actual behavior of EINHORN_FD_N vars
  • fix: Fix the tests on 1.9.2
  • fix: Fix variable name
  • fix: Last round of 1.8.7 fixes: IO::WaitWritable -> Errno::EINPROGRESS
  • fix: Merge pull request #116 from rpeng/rpeng/fix-ruby-34-string-lit-warning
  • fix: Merge pull request #13 from ConradIrwin/bug/--quiet
  • fix: Merge pull request #20 from ebroder/fix-envvar-docs
  • fix: Merge pull request #21 from ConradIrwin/bug/slow-death
  • …and 280 more

Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.

Survey your own repository

contribsys/einhorn was measured the same way every project in this corpus was: the same rubric, at a pinned commit, with the result published in full. Point a surveyor at a repository you know and see whether you agree with it.

About this page

  • The score is its most recent published measurement, taken on 19 September 2026 at a pinned commit. It is not a live figure and does not change until the project is measured again.
  • Measured at commit ac1b7a2a67bfb44d7be0783b3ff55eeea5700148 — the exact code this score is about.
  • Scored under rubric-2026.09.15 — the same rubric and the same method as every other entry in this index.
  • Measured by watchdog.canine.dev using codehealth-analyzer preprod-13a154b7f5d1.