ModernCIPush. Green. Merged.

Production readiness

ModernCI is pre-1.0 (release 0.4.28) and self-hosted only, with no public download yet. Two organizations use it: ModernCI itself, and busbar as a pilot. The core loop is live and runs every day on those installs: testing pull requests, cancelling stale work, the merge queue, sending failures to agents over MCP, and usage records. There is no SLA, no hosted service, no SSO and no compliance certification. Checks run as untrusted code under a separate unprivileged user. Per-tenant check users on shared machines are in preview and being provisioned on our own install now.

Marks: live is in release 0.4.28 on main; preview is built in an open pull request or on dev, not released; planned is decided, not built. The canonical list is facts.

Maturity

State
Version0.4.28. Versions before 1.0 may change configuration and APIs.
DistributionBinaries, not yet published. Release signing and self-updating installs from a signed release channel are in preview (PR #40).
Who runs itModernCI's own repository and busbar (pilot), both on ModernCI's own install.
Hosted servicePlanned. No sign-up today.
SupportNo SLA. Direct contact with the developers.
Release processChanges go through ModernCI itself (branch → dev → qa → main). Fully hands-off releasing is in preview (PR #40).

Enterprise readiness

RequirementState
Self-hosted, air-gappedlive: no outside service is needed at run time, and nothing phones home (no licence check exists yet)
SAML SSO, SCIMnot built (decided for Enterprise)
Sign-inlive: local accounts with Argon2id-hashed passwords, and scoped API tokens (read, operate, agent, admin). GitHub and Google sign-in are in preview
Roleslive: token scopes. Roles per project are planned
Audit traillive: one immutable usage record per check, written before any status is posted. No separate audit log of operator actions
Secretslive: envelope-encrypted under the install's master key, injected at run time, redacted from output, never in argv. Vault and cloud KMS are planned
Backupsyour responsibility: everything is under MODERNCI_HOME, including master.key, which opens every secret
High availabilityone daemon per install. A supervisor restarts it if it exits or stops answering live, and a release swaps in without stopping running checks live. No multi-node failover
Upgradeslive: hot swap with rollback to the previous binary if the new one isn't healthy
Data retentionlive: results and logs older than 30 days are pruned
Compliance (SOC 2, ISO 27001)none

The security boundary: how untrusted code runs

A check is code from a pull request, so ModernCI treats it as untrusted.

  • Local backend live: every check runs as run_as, an unprivileged user that is neither root nor the daemon's user, through sudo -n -u <run_as> -- bash --noprofile --norc -s. The script, its environment and any secret travel on stdin, never in argv. The environment is an allowlist. Files are written with umask 077, and the daemon's home is mode 0700. File permissions keep the check away from the daemon's key, store, secrets and other tenants' work. The daemon verifies all of this at startup, every 10 minutes and before each job, and a failed check stops jobs there (backend.isolation).
  • Process groups live: each job is its own process group. A cancel sends TERM, then KILL after 5 seconds.
  • Elastic runners live Latchkey: a fresh runner per job. The commit is sent as a git bundle, so no Git provider token reaches the runner.
  • Containers live: a check with image: runs in a container.
  • Configuration from the target branch live: a pull request can't change the checks it is tested with.
  • Forks live: pull requests from forks are never tested or pushed.
  • Per-tenant isolation preview PR #133: on a machine shared by tenants, each tenant's checks run as that tenant's own user, in its own work directory. On ModernCI's own install these users are being provisioned now. A backend upgraded from before this rule still runs checks as the daemon's user, and backend.unisolated fires until it names run_as.
  • Not isolated today: checks on one local backend share the host kernel (separate users, not separate VMs), and their network access isn't restricted. A read recorder and blocked network by default are planned.

Known limitations

  • One pull request's checks run one after another in 0.4.28 (parallel checks are planned). Measured on busbar (2026-10-12 to 13, one 8-vCPU host): the median push to green result was 43 min, 37 min of it queued.
  • The current dev branch carries a known failing test in the combination of the merge chain (#84) and environment-keyed reuse (#56). It is a speed regression (extra re-tests), not a wrong result, and it blocks promotion until fixed.
  • Flaky-test handling covers cargo-nextest only. Per-test results cover cargo-nextest and libtest.
  • GitHub and Gitea only (Forgejo through the Gitea API). No GitLab, no Windows.

See also: facts, benchmarks.