Why Jenkins won
Before Jenkins, continuous integration was a practice teams described and rarely ran. Jenkins made it a thing you could install on a spare machine on a Friday afternoon. It ran inside your network, with your credentials, against your repositories. It did almost nothing on its own; plugins did everything. When it needed to do something new, someone wrote a plugin, and then everyone had it.
Twenty years later the shape still holds up: a small core that schedules and records work, a plugin ecosystem that does the work, a UI that lists both, and an update centre that keeps them current. It is the shape an autonomous DevOps platform needs too, because autonomous agents raise exactly the questions Jenkins answered for builds: where does this run, who controls it, and how do I add the thing my team needs?
The same architecture, a different kind of job
Compass maps an AWS account to the Terraform and repositories that own it, then acts on the map: it imports unmanaged resources into Terraform, plants decoy credentials that trip when used, and keeps the pull requests it opens alive. Underneath, it is organised the way Jenkins is.
| Jenkins | Compass | |
|---|---|---|
| Where it runs | Your server, your network | Your machine or network: Docker Compose with a dashboard, an API, a daemon and DynamoDB |
| Licence | MIT | Apache-2.0 |
| Unit of extension | Plugin | Captain: a Python package found by an entry point |
| Unit of work | Build, with a console log | Job, with an event timeline |
| Scheduling | Build queue, executors, triggers | Jobs table, one daemon, captain services |
| Install an extension | Manage Plugins → Available → restart | Settings → Captains → Available → restart |
| Configuration UI | Generated from the plugin's descriptor | Generated from the captain's JSON Schema |
| Commercial edition | CloudBees CI, on Jenkins | Beamreach Compass, on the same open code through the host seams |
Captains are plugins, and they are easy to write
In Jenkins even the features that feel built in, Git and Pipeline among them, are plugins. Compass takes the same view: the map, the decoys, code-as-cloud and the PR watcher are all captains, and they use exactly the interface a third-party captain does. Turn one off per workspace and its tab disappears, its background services stop, and its work is refused. Nothing is deleted.
A captain is a manifest and a commands module. This is the whole manifest of the example captain:
MANIFEST = CaptainManifest(
name="hello",
label="Hello",
summary="Says hello. The example captain: copy it to start your own.",
tier="open",
feature="hello",
commands="captains.hello.commands",
routers=("captains.hello.routes:router",),
route_prefixes=("/api/hello",),
tabs=(TabSpec(key="hello", label="Hello", group="platform", feature="hello"),),
config_schema={
"type": "object",
"properties": {
"greeting": {"type": "string", "default": "hello", "x-help": "What to say."},
"api_key": {"type": "string", "x-secret": True},
},
},
requires_core=">=0.2,<1",
version=__version__,
)
And the line in pyproject.toml that makes Compass find it:
[project.entry-points."compass.captains"]
hello = "captains.hello.manifest:MANIFEST"
From that, Compass gives the captain what a Jenkins plugin gets from Jenkins and a bit more:
- A command line, an API and a job queue for free. Each command is a function from a payload dict to a dict. The CLI, the API and the daemon all go through one dispatcher, so
compass hello greet name=Adaand a click in the dashboard run the same code and are recorded the same way. - A settings form. The
config_schemais drawn as a form under Settings → Captains, validated, andx-secretfields are masked, the way Jenkins handles aSecret. - A page, even without a UI. A captain with no dashboard component gets a generic page: its summary, a form that runs any of its commands, and its recent jobs with results. For many captains that is the whole UI.
- Background services. Declare a loop in the manifest and the daemon runs it while the captain is on, which is how the map re-scan and the PR watcher's five-minute tick work.
- A local dev loop.
pip install -eyour captain next to a checkout of Compass, the waymvn hpi:runstarts a Jenkins with your plugin loaded.compass validate path=.runs the same checks CI runs on the shipped captains: manifest rules, compatibility, routes under declared prefixes, the schema, and name or tab clashes.
The captain development guide goes from cp -R examples/captain-hello to a published captain in nine short sections.
An update centre, with a review
Jenkins' Update Center is a JSON file of plugins that every Jenkins polls. Compass has the same thing: compass_core/catalog.json. Merge an entry there and every Compass lists the captain under Available within ten minutes. An administrator installs it with one click and a restart, or by naming it in .env:
COMPASS_EXTENSIONS=compass-captain-weather==0.1.0
Because a captain runs with the install's AWS and GitHub access, the catalog is stricter than most plugin indexes. The pip line must be pinned (an exact version, a tag or commit, or a wheel with its sha256; a branch is rejected). A reviewer checks that the source is public and matches the tag, that the summary says what the captain does, and that it asks for no more access than it uses. A captain declares the core versions it supports with requires_core; a Compass outside that range lists it as incompatible and loads none of it.
Updates you can leave on
Anyone who ran Jenkins for long remembers the banner: a new version is available. Compass has the banner too. It reads a release feed at most every six hours and shows what changed next to the Update button, with the notes an operator needs after applying it, taken from the changelog.
Applying it is where Compass is more careful than most self-hosted tools. The Compass containers cannot update themselves: they have no Docker socket. An opt-in updater service has one, and it is off by default, because whoever holds the socket holds the machine. When it is on, an update is one click, and the updater:
- refuses to start while a job is running, unless the request says force (Jenkins' "restart when no jobs are running");
- pulls the release's images and rejects any whose digest is not the one the release lists;
- backs up
.env, sets the new version and recreates the services; - waits up to five minutes for the API and the dashboard to answer, and rolls back to the previous version when they do not.
On the next start, Compass runs its data migrations and rebuilds installed captains against the new core. Without the updater it is still two commands: change COMPASS_VERSION in .env, then docker compose pull && docker compose up -d.
Open source with seams, like Jenkins and CloudBees
Jenkins is open source; CloudBees sells an enterprise product built on it. Compass is the open edition of Beamreach Compass, and the hosted product runs this same code. What makes that honest rather than a fork is that the extension points are in the open code and documented in the architecture guide:
| Seam | Open default | Hook |
|---|---|---|
| Job queue | the jobs table, polled by the daemon | set_job_queue |
| Workspace config | a JSON file | set_workspace_config_loader |
| Model | Anthropic, or none | set_model_factory |
| Dashboard tabs | the open tabs | app/lib/extensions.ts |
A host, registered under the compass.hosts entry point, uses these seams to change how a whole install behaves: where workspaces come from, which queue runs jobs, which model answers. It is how a larger deployment, ours or yours, runs on Compass without patching it. If you want Compass on SQS, or on your company's model gateway, you write a host, not a fork.
Where the analogy stops
Jenkins does what a pipeline tells it, when a trigger fires. Compass decides what to do from the map, which means it needs something Jenkins never did: limits on its own judgement. Every change it proposes is a pull request through your normal review and CI. A deterministic risk score and a gate decide what runs unattended. When a model revises a pull request it works inside a checkout with four tools and can only write the files the change already touches, and a guard that never calls a model checks the diff before anything is pushed. How those layers fit together is in Continuous Maintenance: the stage after CI/CD.
The rest is the part Jenkins got right: run it yourself, read the code, add what you need, and share it.
Related: Continuous Maintenance, obstacles for autonomous software, Compass pricing.
Beamreach