SelfCI: A Minimalist Local-First CI System Embracing the Unix Philosophy
#Regulation

SelfCI: A Minimalist Local-First CI System Embracing the Unix Philosophy

Tech Essays Reporter
4 min read

SelfCI reimagines continuous integration by moving the entire process back to the developer's machine, rejecting the cloud-centric model of traditional CI systems in favor of a composable, local-first workflow.

The current state of continuous integration has settled into a familiar pattern: push code to a remote repository, wait for a cloud provider to provision virtual machines, and hope the network and build queues cooperate. This architecture, dominated by services like GitHub Actions and Circle CI, treats the local development machine as merely a source of code, while the real work happens "elsewhere." SelfCI challenges this assumption entirely. It proposes that for many projects, the most efficient path to reliable builds isn't more infrastructure, but rather a return to the local machine and the principles that made Unix tools enduring.

Featured image

The Philosophy of Local-First Verification

SelfCI operates on a simple but potent observation: most developers already possess the ability to compile source code, run tests, and verify changes on their own hardware. The problems of reproducibility and isolation are largely solved through tools like Nix or Docker. Yet, the industry has largely accepted that continuous integration requires a separate, always-on server farm managed by a third party. This introduces latency, cost, and a layer of abstraction that distances the developer from the verification process.

The core design principle of SelfCI is that it should be used primarily on local machines, much like git or cargo. It is a single binary, selfci, that behaves as a standard Unix tool. The initialization process, selfci init, mirrors git init or cargo init, creating a minimal configuration file that delegates the actual logic to user-defined scripts. This approach prioritizes composition over configuration. Instead of learning a proprietary YAML domain specific language, developers implement CI rules in the scripting or programming language of their choice. The selfci command acts as an API to control execution, allowing jobs to be started dynamically and steps to be tracked, all orchestrated from within a single bash script or a custom binary.

Dynamic Job Orchestration and the Merge Queue

Unlike traditional CI systems that require defining a static graph of jobs and dependencies upfront, SelfCI allows for dynamic, conditional execution. A CI run begins with a "main" job, which can spawn additional parallel jobs at any time using selfci job start <name>. Jobs can wait for others to complete using selfci job wait <name>, enabling complex workflows without the rigidity of predefined pipelines. Each job is composed of steps, tracked with selfci step start <name>, which can be marked as failed or ignored, giving fine-grained control over what constitutes a successful run.

For maintainers, SelfCI offers a local merge queue daemon. A contributor's change can be added to the queue with selfci mq start <id>. The daemon then runs the necessary checks against the base branch and, if they pass, merges the change into trunk without requiring manual intervention. This provides the automation of a cloud-based merge queue but keeps the entire process on hardware the maintainer controls.

Security Through Control and Isolation

A critical aspect of any CI system is security, and SelfCI addresses this by inverting the typical trust model. In cloud systems, the CI runner executes code defined by the candidate (the proposed change). SelfCI starts with the command defined by the base branch—the vetted, trusted source of truth. This means the CI logic itself cannot be tampered with by a malicious pull request. The base defines the rules, and the candidate is merely the subject of those rules. This subtle shift prevents a common attack vector where CI configuration is modified to bypass checks or exfiltrate secrets.

Furthermore, SelfCI adopts a "bring-your-own-isolation" model. It does not bundle a virtual machine or container runtime. The responsibility for protecting the host system and ensuring parallel jobs do not interfere is left to the user. This might involve using Docker, Nix, or other sandboxing technologies, but the choice is explicit and composable. For teams collaborating with trusted colleagues, the overhead of full VM isolation is often unnecessary. By leveraging reproducible environments like Nix, developers can run identical CI checks locally that would otherwise require a cloud runner, eliminating the need for dedicated servers and the associated maintenance burden.

The Unix Philosophy in Practice

SelfCI is a single command that does one thing well: orchestrating the execution of verification steps. It does not prescribe a language, a container format, or a specific execution environment. It provides the primitives—jobs, steps, and a merge queue—and expects the user to compose them with existing tools. This aligns with the Unix philosophy of building simple, clear, and composable tools that work together. The example output demonstrates this: a series of parallel checks (lint, cargo, main) running quickly and reporting clear, concise results.

The project acknowledges that for very large, high-velocity projects with many external contributors, a server-side component may eventually be desirable. The roadmap includes plans for "scaling out" using P2P networking via Iroh, allowing for pooled resources while preserving the local-first core. But even without that, SelfCI presents a compelling alternative for small to medium-sized projects. It suggests that the path to confidence in code changes isn't necessarily more abstraction and remote infrastructure, but rather better tools for local verification and a willingness to trust the developer's own environment.

Comments

Loading comments...