Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 36 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
<div align="center">

<img src="docs/assets/mark.png" alt="" width="96" height="96">

# RunnerSlice

A systemd-native manager for self-hosted GitHub Actions runners.
**A systemd-native manager for self-hosted GitHub Actions runners.**

[Installation](docs/installation.md) ·
[Operations](docs/operations.md) ·
[Threat model](docs/threat-model.md) ·
[Verification](docs/verification.md)

[![check](https://github.com/swarea/runner-slice/actions/workflows/check.yml/badge.svg?branch=main)](https://github.com/swarea/runner-slice/actions/workflows/check.yml?query=branch%3Amain)
![status: public alpha](https://img.shields.io/badge/status-public%20alpha-0f6b7d)
[![MIT](https://img.shields.io/badge/licence-MIT-1c1e22)](LICENSE)

</div>

RunnerSlice runs persistent, repository-scoped GitHub Actions runners on a
Linux host you already operate. Each runner has its own Unix account and
Expand All @@ -11,7 +26,8 @@ the running host, that the boundary and limits are what you declared.
It is Bash and systemd. It has no daemon, no database, no Kubernetes and no
web server.

> **Status: public alpha (0.1.0-dev). There is no release or tag yet.**
> **Status: public alpha (0.1.0-dev). Development has ended at this stage.
> There is no release or tag.**
>
> - **Every push and pull request** runs shellcheck and every drill on
> GitHub-hosted runners (the `check` workflow). The drills exercise each
Expand Down Expand Up @@ -94,6 +110,22 @@ need to bound and verify what CI can take from it.

## How it works

```text
one host
┌─────────────────────────────────────────────────┐
│ ci.slice CPUQuota, MemoryMax, TasksMax │
│ IPAddressDeny (optional) │
│ ┌───────────────────┐ ┌───────────────────┐ │
│ │ runner-app │ │ runner-web │ │
│ │ own account, root │ │ own account, root │ │
│ │ peers' roots, all │ │ peers' roots, all │ │
│ │ homes, Docker │ │ homes, Docker │ │
│ │ socket: masked │ │ socket: masked │ │
│ └───────────────────┘ └───────────────────┘ │
└─────────────────────────────────────────────────┘
plan / apply / retire change it; check reads it back
```

```text
/etc/runner-slice/fabric.conf slice limits, prefixes, masks for the whole host
/etc/runner-slice/runner-release.conf the actions/runner release to install, by SHA-256
Expand Down Expand Up @@ -124,8 +156,8 @@ what has been verified.

## Getting started

1. Read [Installation](docs/installation.md): requirements, and how to
install from a checkout until the first release exists.
1. Read [Installation](docs/installation.md): requirements, and how to build
the archive from a commit and install it. No release is published.
2. Write the inventory ([Configuration](docs/configuration.md)), then follow
[Operations](docs/operations.md) from `plan` to the first registered runner.
3. Run `sudo runner-slice check` after every change and every reboot.
Expand Down
5 changes: 3 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ operator has to do.

## Supported versions

There is no release yet. Until there is, fixes land on `main` only. Once
there is, only the latest release receives fixes.
There is no release. RunnerSlice is a public alpha whose development has
ended. A report is still read, and a confirmed fix lands on `main`, which is
the only supported version.

## In scope

Expand Down
11 changes: 11 additions & 0 deletions docs/assets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Assets

`mark.png` is the repository mark the README displays at 96×96.
`mark-source.png` is the card it is cut from: `swarea-cyan-deep-0f6b7d`,
from the `ground-f7f7f6` set.

The shape follows
[the organization's mark rule](https://github.com/swarea/.github/blob/main/docs/repository-mark.md):
512 × 512, ground `#f7f7f6`, and a 75px corner radius. The colour is
`make-mark.html`'s draw of this card. The corner's alpha channel is identical
to the other swarea marks, which is the check the rule asks for.
Binary file added docs/assets/mark-source.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/mark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ actually been verified, and at what level ([verification.md](verification.md)).

| Distribution | Architecture | systemd | Status |
|---|---|---|---|
| Ubuntu 24.04 | x86_64 | 255 | **Planned** for the first verification VM. Not yet verified. |
| Ubuntu 24.04 | x86_64 | 255 | **Not verified.** Preflight accepts it; no x86_64 host has run RunnerSlice. |
| Ubuntu 24.04 | aarch64 | 255 | **Verified** on a disposable VM (Lima, Apple Virtualization), at RUNTIME VERIFIED and REBOOT VERIFIED. See [acceptance/2026-10-arm64.md](acceptance/2026-10-arm64.md). The lifecycle was run with two real GitHub runners, ending in deregistration and retirement. One VM; no other aarch64 host verified. |
| Ubuntu 22.04 | aarch64 | 249 | The code RunnerSlice was extracted from has run here in production (see [field-report.md](field-report.md)). **This code** has not been run there. Not supported. |
| Any other distribution | | | Refused by preflight. |
Expand Down
10 changes: 6 additions & 4 deletions docs/contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ run against one recorded commit.

The version follows the evidence. No stage is skipped to reach a number.

**Development ended at Alpha on 2026-10-17.** The Beta and 1.0 rows define
what those stages would have required. They are kept as definitions, and no
work towards them is planned. The project is a public alpha, at
`0.1.0-dev`, with no release or tag.

## Open decisions (owner)

Decided:
Expand All @@ -197,7 +202,4 @@ Decided:
- on 2026-10-16: public visibility. Hosted CI for a public repository needs
no paid minutes.

Still open, each a separate approval:

- **the first release and tag**;
- **an x86_64 verification host**, and its cost.
Not pursued: a release or tag, and an x86_64 verification host.
4 changes: 2 additions & 2 deletions docs/field-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ repository names, network details and account identifiers are left out.
finished along a critical path of 14.8 minutes, against 17.9 minutes run one
after another. Jobs running at the same time each took 1.4 to 1.8 times as
long as alone.
- One sample is not a performance result. A 7-day before-and-after
comparison is planned, and nothing more is claimed until it exists.
- One sample is not a performance result, and no longer comparison is
recorded here. Nothing more is claimed.
- This experience is why pools exist in RunnerSlice.
- **What did not fit.** The mobile-app repository's Android build needed
x86_64-only tools under emulation: 18 to 26 times slower per core on
Expand Down
6 changes: 3 additions & 3 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Which combinations have actually been verified is recorded in

## Get the release archive

There is no published release yet. Build the archive from a commit you have
No release is published. Build the archive from a commit you have
reviewed:

```sh
Expand All @@ -37,8 +37,8 @@ bash scripts/release.sh --out ../dist --snapshot # -dev versions need --snapsh
```

The build is reproducible: the same commit gives the same bytes and the same
`runner-slice-<version>.tar.gz.sha256`. When releases are published, verify the
download against the published checksum:
`runner-slice-<version>.tar.gz.sha256`. To check an archive someone else
built, rebuild it from the same commit and compare the checksum:

```sh
sha256sum -c runner-slice-<version>.tar.gz.sha256
Expand Down
6 changes: 4 additions & 2 deletions docs/threat-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,10 @@ inventory and the library, then reads the host:
unknown CPU quota format.

The fixtures in the drills encode what systemd is expected to print. Each of
those assumptions is listed in [verification.md](verification.md) and has to be
observed on a real host before it counts.
those assumptions is listed in [verification.md](verification.md) with what a
real Ubuntu 24.04 aarch64 host with systemd 255 showed. Two of them were wrong
until that host corrected them: the archive's modes and how `RestrictNamespaces`
is printed. On any other host they are still assumptions.

## Handling of registration

Expand Down
27 changes: 8 additions & 19 deletions docs/verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,21 +143,11 @@ Each run records:

### Environments

Creating either environment needs owner approval, and so does any cost.

- **ARM64:** an Ubuntu 24.04 aarch64 VM on the maintainer's Apple-silicon
laptop.
- Candidate tools are UTM or Lima with Apple's Virtualization framework,
2 vCPU and 4 GiB.
- Before relying on it, check: systemd PID 1 and cgroup v2 with the
`cpu`, `memory` and `pids` controllers; outbound access to GitHub;
reboot behaviour; and the laptop's free memory while it runs.
- **x86_64:** a short-lived Ubuntu 24.04 x86_64 cloud VM.
- About 2 vCPU and 4 GiB, created for the run and deleted after it.
- It must be in an account or project separate from production, with no
access to production networks.
- The provider, cost cap, run time and deletion procedure are decided before
it is created.
laptop: Lima with Apple's Virtualization framework, 2 vCPU, 3 GiB, NAT, no
shared folders. It was used for every real-host result above. It was
deleted on 2026-10-17, after its results were recorded.
- **x86_64:** none. No x86_64 host has run RunnerSlice, and none is planned.

### Dogfood

Expand All @@ -169,9 +159,8 @@ retired when the acceptance ended.
and runs `tests/run.sh` on the VM's runners.
- **Each run records** the commit, the tree, and the runner that ran it.
- **These results never appear as checks on RunnerSlice pull requests.** A
non-sensitive summary goes into the release record.
non-sensitive summary is in the acceptance record.

The README may say RunnerSlice is validated on runners it manages only after
a dogfood run and the lifecycle acceptance have both been recorded for one
commit. The dogfood runners are retired when the acceptance ends. That
retirement is item 15.
Both were recorded on the ARM64 VM. The dogfood runners were retired when the
acceptance ended (item 15), and the dogfood repository is archived, with its
run history kept.