In the age of infrastructure as code, knowing the exact state of a machine before making changes is as vital as a medieval justice surveying a shire before rendering judgment. The concept of an eyre—a traveling royal court that assembled facts about crimes, taxes, and landholdings—provides a compelling metaphor for modern configuration management. Before Puppet applies a manifest, Chef converges a node, or Ansible plays a task, each tool first probes the host to build a detailed inventory of its operating system, hardware, network interfaces, and file systems. This fact‑gathering step ensures that subsequent automation decisions are grounded in reality rather than assumption. Without this baseline, drift can go unnoticed, leading to configuration errors, security gaps, or costly downtime. Recognizing this timeless need, the author of the Spire project has released a petite Clojure library named Eyre that focuses solely on collecting system facts through a shell interface. By delegating the actual command execution to a caller‑supplied function, Eyre eliminates external dependencies and remains lightweight enough to embed in any Clojure‑based automation pipeline. The following sections explore how Eyre works, the breadth of environments it supports, and the practical advantages it brings to teams seeking reliable, observable infrastructure automation.

Eyre’s design hinges on a single abstraction: a function that accepts a shell command and returns a map with three keys—:exit for the process status, :out for standard output, and :err for standard error. Because the library does not invoke any shell directly, it has zero compile‑time or runtime dependencies; the caller injects whatever executor best fits the deployment context, whether that is a local Bash process, an SSH tunnel, or a container runtime. To illustrate, a developer would place a small Clojure snippet in a file such as gather.clj, define the executor (for example, using Babashka’s built‑in shell runner), and then call eyre/gather-facts with that function. The result is a nested data structure where each fact—ranging from kernel version to mounted filesystems—is keyed under descriptive names like :os/version, :memory/total, or :network/interfaces. This approach yields several benefits: first, it keeps the core library tiny and easy to audit; second, it allows the same fact‑gathering logic to be reused across disparate environments without modification; third, it encourages explicit handling of executor failures, making error paths visible in automation logs. By separating the description of what to collect from how to collect it, Eyre embraces the functional principle of dependency injection while staying true to Clojure’s emphasis on data‑driven programming.

One of Eyre’s strengths lies in its extensive shell and operating system coverage. The library detects the :type of the shell supplied by the executor and selects the appropriate set of probing scripts accordingly. Supported shells include the ubiquitous Bash and Zsh, the minimalist Dash and SH, the KornShell variants Ksh and Busybox, the user‑friendly Fish, the emerging Nushell, and on Windows platforms both PowerShell and the classic CMD.exe. This breadth means that whether you are managing a fleet of Linux servers, a heterogeneous BSD network, macOS workstations, or Windows workstations, Eyre can adapt without requiring a separate fact‑gathering tool for each platform. On the operating system side, the library has been tested on recent releases of Linux distributions (Ubuntu, Debian, CentOS, Fedora), FreeBSD, NetBSD, macOS Ventura and later, and Windows 10/11 Server editions. Because each probe script is written as a portable shell snippet, the same logical fact—such as the total amount of RAM—can be expressed in Bash‑specific syntax for Linux and in PowerShell cmdlets for Windows, yet Eyre presents the outcome through a uniform Clojure map. This uniformity simplifies downstream processing: playbooks, policies, or alerting rules can rely on consistent fact names regardless of the underlying host, reducing the cognitive overhead of writing platform‑specific conditionals.

Running Eyre locally with Babashka offers an immediate, zero‑setup way to see the library in action. After installing Babashka—a fast, script‑friendly Clojure interpreter—you create a file named gather.clj that requires the eyre namespace, defines a simple executor function that shells out via bb/sh, and then invokes eyre/gather-facts. Executing bb gather.clj prints a rich map to the console, revealing dozens of facts collected from the user’s current Bash session. Typical output includes the login shell path (e.g., /bin/bash), the effective shell being used for the probes (which may differ if you have overridden $SHELL), the operating system name and release, CPU model and core count, total and available memory, disk usage for each mounted filesystem, and a list of active network interfaces with their IPv4 and IPv6 addresses. Because the executor runs with the privileges of the invoking user, the facts reflect exactly what that user can see, making it straightforward to test permission‑sensitive scenarios such as sudo‑restricted commands or SELinux contexts. This local mode is invaluable for quick validation during development, for generating baseline snapshots before applying changes, and for educational purposes when learning how different shells expose system information. Moreover, the output is pure Clojure data, so it can be piped directly into other functions for filtering, transformation, or storage in a Datomic or SQLite store for later trend analysis.

Examining the structure of the returned map reveals several noteworthy keys that help operators understand both the environment and the manner in which Eyre gathered the data. The :shell key contains the absolute path to the shell process that executed the fact‑gathering scripts, while :login-shell reflects the shell recorded in the user’s password entry—often the default interactive shell. When you change the executor to use Zsh or Fish, :shell updates accordingly, yet :login-shell remains constant, highlighting the distinction between the user’s configured login environment and the transient shell invoked for a particular task. Another important key is :type, which Eyre uses internally to dispatch the correct set of probe scripts; its value mirrors the shell name (e.g., :bash, :zsh, :fish). Beyond shell metadata, the fact map organizes system information under namespaced keywords such as :os/:family, :os/:name, :os/:version, :cpu/:model, :cpu/:cores, :memory/:total, :memory/:available, :filesystem/:mount-point/:usage, and :network/:interface/:addresses. This hierarchical naming scheme enables straightforward retrieval with Clojure’s get-in function and facilitates schema validation using libraries like Malli or Spec. By keeping the fact hierarchy explicit and stable across platforms, Eyre allows teams to build reusable policies—for example, a rule that triggers only when :memory/:available falls below a threshold—without rewriting the rule for each OS variant.

The true power of Eyre emerges when the executor is redirected over a network, enabling fact collection from remote hosts without installing any agent on the target machine. By plugging in an executor that leverages the clojuressh library, you can establish an SSH connection, run the same shell‑based probes, and receive the result map as if the commands had been executed locally. This agentless approach mirrors the workflow of tools like Ansible while retaining the fine‑grained control and dependency‑free nature of Eyre. In practice, you would define an executor function that opens an SSH session, sends a command via the channel, captures the exit status, stdout, and stderr, and returns them in the expected {:exit :out :err} map. Because the SSH round‑trip introduces latency, each individual shell call can become a performance bottleneck, especially when probing dozens of facts across high‑latency links. Eyre mitigates this issue by grouping related probes into single command invocations wherever possible—for instance, gathering CPU, memory, and disk statistics in one combined script—thereby reducing the number of network round‑trips. Nevertheless, users should remain aware of the trade‑off between granularity and speed, and may choose to tune the probing scripts based on their specific network characteristics and the criticality of real‑time fact accuracy.

During the creation of Eyre, large language models served as a force multiplier, particularly in two areas that often consume disproportionate developer effort. First, LLMs proved adept at translating fact‑gathering snippets from one shell dialect to another. Writing a script that extracts the total memory size, for example, requires different syntax in Bash (/proc/meminfo), PowerShell (Get‑CimInstance Win32_ComputerSystem), and Fish (a custom awk pipeline). Rather than manually researching each variant, the developer could prompt the model with a Bash reference implementation and request equivalents for Zsh, Fish, Nushell, and PowerShell, dramatically accelerating cross‑shell coverage. Second, LLMs assisted in constructing a comprehensive test matrix. Generating Packer scripts to build virtual machines for Ubuntu, FreeBSD, NetBSD, macOS, and Windows, as well as Dockerfiles for container‑based testing, would have been a tedious, error‑prone process. By describing the desired OS version, shell selection, and provisioning steps in natural language, the model produced ready‑to‑use infrastructure‑as‑code snippets that could be dropped into a CI pipeline. This automated test harness enabled the library to be validated against a wide array of shells and operating systems early in development, increasing confidence that the fact maps remain consistent across environments. While AI cannot replace careful review, its ability to handle repetitive boilerplate freed the author to focus on the core logic of fact aggregation and API design.

Network latency presents a distinct challenge for any remote fact‑gathering solution, and Eyre is no exception. When each fact is obtained via an independent shell call over SSH, the cumulative delay can become prohibitive; a round‑trip of 100 ms multiplied by fifty separate probes adds five seconds of pure waiting time before the automation can proceed. Recognizing this, the current implementation of Eyre already employs a strategy of script coalescing: related probes—such as those that read from /proc/stat, /proc/meminfo, and /proc/diskstats—are concatenated into a single shell script that outputs a JSON‑like blob, which the executor then parses into the final Clojure map. This reduces the number of SSH round‑trips from dozens to a handful, markedly improving responsiveness on high‑latency links. However, there remains room for further optimization. Additional groups—like consolidating all network interface queries or merging filesystem usage checks—could be merged similarly. Beyond mere batching, a more intelligent approach would involve the library accepting a selector that specifies only the facts needed for a particular operation, thereby avoiding the collection of superfluous data. Such demand‑driven gathering would not only cut latency but also lessen the computational load on the target host, a consideration that grows increasingly important in large‑scale, ephemeral environments like serverless functions or short‑lived containers.

Looking ahead, the author envisions several enhancements that could make Eyre even more versatile for modern infrastructure workflows. One priority is the implementation of a fact‑selection mechanism, where users pass a set of keywords or a schema indicating which pieces of information are required. The executor would then invoke only the minimal subset of probe scripts necessary to satisfy that request, dramatically trimming both execution time and data transfer volume. Another avenue is the adoption of structured output formats such as JSON or MessagePack directly from the probe scripts, reducing the need for post‑processing parsing steps on the caller side. This would be especially beneficial when running over constrained networks where bandwidth is at a premium. Additionally, integrating with popular observability platforms—by emitting facts as Prometheus metrics or OpenTelemetry attributes—would allow teams to correlate configuration state with performance telemetry in real time. Finally, expanding the test automation to include edge‑case shells like rc (from Plan 9) or esoteric embedded environments could further cement Eyre’s reputation as a truly universal fact‑gathering tool. While these features are slated for future releases, the current version already provides a solid, dependency‑free foundation that teams can adopt immediately and extend as their needs evolve.

When placed alongside established fact‑gathering libraries such as Puppet’s Facter, Chef’s Ohai, and Ansible’s setup module, Eyre occupies a distinctive niche. Facter and Ohai are tightly coupled to their respective configuration management agents, often pulling in a substantial set of Ruby‑ or Erlang‑based dependencies that can complicate minimal‑container deployments. Ansible’s setup module, while agentless, is bundled within the larger Ansible engine and may be overkill for users who only need fact data without the full playbook runtime. Eyre, by contrast, is a standalone Clojure library whose sole purpose is to return a map of system facts, leaving the decision of how to act on that data entirely to the caller. This separation of concerns aligns well with the Unix philosophy of doing one thing well and makes Eyre an attractive option for teams that have already standardized on Clojure or Babashka for tooling, or for those seeking a lightweight, embeddable solution for custom automation scripts. Moreover, because the executor is supplied externally, Eyre can be easily adapted to exotic environments—such as privileged containers with restricted syscalls, air‑gapped networks that only allow outbound SSH, or even WASM‑based runtimes where a shell can be simulated—without requiring modifications to the library itself.

From a practical standpoint, integrating Eyre into existing DevOps workflows can yield immediate benefits. For infrastructure‑as‑code pipelines that already use Clojure‑based tools like Jet or Clerk, adding a call to eyre/gather-facts at the start of a deployment stage provides a reliable snapshot of the target node before any changes are applied. This snapshot can be stored in a version‑controlled artifact repository, enabling drift detection by comparing successive deployments. In security‑focused contexts, the fact map can be fed into policy‑as‑code engines such as Open Policy Agent (OPA) to validate that, for instance, no unauthorized kernel modules are loaded or that filesystem permissions meet hardening benchmarks. Operations teams can also leverage Eyre for ad‑hoc troubleshooting: when an incident arises, running a one‑off Babashka script that gathers facts from the affected host yields a concise diagnostic bundle that can be attached to a ticket or shared with a vendor support team. Because the output is plain Clojure data, it can be transformed into Markdown reports, HTML dashboards, or even ingested into a time‑series database for trend analysis of resource utilization over weeks or months. The lack of external dependencies means that the same script can be run from a laptop, a CI runner, or a bastion host with minimal setup, making Eyre a versatile addition to any toolbox.

To get started with Eyre today, clone the repository from the provided URL, install Babashka if you do not already have it, and experiment with the sample gather.clj file to see the fact map appear in your terminal. Try swapping the executor to use Zsh or PowerShell (on a Windows Subsystem for Linux instance) to observe how the :shell and :login‑shell keys shift while the core facts remain consistent. If you manage remote infrastructure, replace the local shell executor with a thin wrapper around clojuressh and run the same gathering routine against a test VM; note the latency difference and experiment with merging probe scripts to see the impact on total execution time. Consider contributing any shell‑specific fact snippets you develop for obscure platforms back to the project, thereby expanding its cross‑platform reach. Finally, keep an eye on the project’s roadmap for upcoming features such as selective fact gathering and structured output formats, and evaluate how they might align with your organization’s automation goals. By adopting Eyre now, you gain a lightweight, observable foundation for fact‑driven decision making that can grow alongside your evolving infrastructure needs.