Skip to content

Understanding Nostr CI

Nostr CI is a protocol, not a product. It defines the signed events that let a maintainer ask for service and let anyone publish and verify a result. Any service that speaks the protocol can take part, and any client can read the outcome and decide how much it means.

The protocol separates the coordinator that watches a repository and schedules its workflows from the compute providers that execute each job. One coordinator can therefore spread work across machines and operators it does not run itself.

It says nothing about how a workflow must be written or executed. GitHub Actions syntax through act works today, and a Nix-native runner or another workflow style can join without changing the events.

CI compute costs money, and the model is built for that. The intent is that a repository's maintainers or benefactors pay its coordinator, and the coordinator pays the compute providers, whether those are its own machines or a pay-per-use compute market. Today billing is arranged out of band; Who pays has the detail.

This page explains the roles, walks through one run, and separates what the protocol promises from what ngit-ci does today.

The roles

RoleWhat it doesWhat it signs
MaintainerKeeps workflow files in the repository, chooses coordinators, and provisions secretsService Request and Stop, Manual Trigger, Repository Secret Update
CoordinatorWatches repositories, matches workflows to pushes and PRs, applies policy and secrets, chooses a compute provider for each job, and assembles the resultCoordinator Advertisement, Request-Readiness List, Repository Status, Workflow Progress, Workflow Result
Compute providerExecutes one job and reports what happenedJob Result
RunnerThe software that understands a workflow format, act for GitHub Actions syntax todayNothing; it is a capability the coordinator advertises
SandboxWhere a job executes and how isolated it is: host containers, or a fresh microVM per jobNothing; it is the operator's choice
ClientReads the events, checks them, and shows you results with their trust contextNothing; ngit and GitWorkshop are clients

A coordinator and a compute provider can be one key or two. In ngit-ci today they are the same key. The split exists so a coordinator can later hand jobs to machines or providers it does not run itself, choosing among them by what the job needs and what the maintainer prefers. The coordinator owns that choice: its Workflow Result records which provider it delegated each job to. It does not thereby vouch for the provider's work. Each Job Result names who actually executed it, and that provider's signature is the execution claim.

"Runner" and "sandbox" are easy to confuse. A runner turns a workflow file into processes: act today, possibly a Nix-native runner or a Forgejo-style runner later. A sandbox decides where those processes live. Any runner should work in any sandbox. Neither is a Nostr identity; the coordinator advertises runner families and the runs-on selectors it accepts, such as act:ubuntu-latest.

One run, end to end

Every event below is signed by the party that publishes it and read from relays by everyone else.

  1. A coordinator advertises
    • A general capabilities advert, with a heartbeat every 30 minutes.
    • A list of repositories it could serve if requested.
  2. A maintainer requests service
    • One Service Request names one coordinator and one repository, and stands until a Stop.
    • The coordinator answers with a Repository Status: acting, which workflow paths it will run, which secret names it holds.
  3. Something triggers a run
    • A push, a PR, or a maintainer's Manual Trigger.
    • The coordinator reads the workflows at that commit and claims the ones it can run.
  4. Jobs go to compute providers
    • One provider per job. In ngit-ci today that is the coordinator itself.
    • Workflow Progress tracks the run: queued, in progress, concluded.
  5. Each provider reports: a Job Result with conclusion, logs, artifacts, and public outputs.
  6. The coordinator concludes: a Workflow Result quoting each Job Result and, when a request gated the run, that request.
  7. Anyone reads the result: through a client such as ngit or GitWorkshop, which attaches trust context to each signer.

Use Nostr CI shows how to do each of these as a maintainer, from choosing a coordinator to gating a merge on the result.

Every result names the repository coordinate, the commit, the workflow path together with the SHA-256 of its content at that commit, and why it ran: push, pull_request, or manual. A client can therefore check that the workflow the coordinator claims to have run is the one in your Git history.

Why a signature is not trust

Anyone can run a coordinator, and any key can publish a Workflow Result for any repository. A valid signature proves who made a claim, not that a maintainer wanted it or that the outcome is right. Clients therefore attach trust context to every signer, built from the provenance the events carry:

LevelEvidence
Maintainer-directedA confirmed maintainer's Service Request covered the run when it started, or a maintainer's Manual Trigger asked for it
Operationally associatedThe signer's verified identity belongs to infrastructure the repository lists, or a maintainer-recognised coordinator delegated the job to this provider
Seen in your networkSomeone you follow requested this coordinator, or it has run CI for repositories they maintain. GitWorkshop only, because it depends on who is looking
No known contextAn absence of evidence, not a finding against the signer

ngit ci status --require-ci-trust and ngit pr merge --require-ci-trust accept the first two as floors. Separately from trust, ngit reports integrity: whether it holds the commit locally and whether the workflow file at that commit hashes to what the coordinator signed. A result can be maintainer-directed and still fail integrity, or the reverse.

None of this proves a build is correct. It tells you why a result deserves attention and who to hold responsible.

Why nothing runs until you ask

By default, a coordinator runs nothing for a repository until a maintainer asks. That request is what makes a run maintainer-directed in the trust context above. A self-hosted coordinator set to automatic produces the same runs without it, so they can never be rated maintainer-directed. Request service even from your own coordinator.

Operators can offer unrequested CI by switching to automatic, and the advertisement says which policy applies. Running CI for a repository you do not maintain is still possible; the guide outlines some example use cases and how to set it up.

How secrets stay with maintainers

A workflow can read secrets, but the coordinator decides when to supply them. A maintainer provisions values for their repository, either encrypted to the coordinator's key over Nostr or handed to the operator out of band. The coordinator injects them only into runs whose trigger a confirmed maintainer authored, such as a maintainer's push. A third-party pull request always runs with empty secrets, so a contributor cannot lift a deploy token by editing a workflow. Values are never published. The coordinator's Repository Status lists only the secret names in use, so a maintainer can confirm an update landed.

Who pays

Someone has to pay for the machines that run CI, and the design says who. None of this has shipped yet; it is the direction the protocol is built to support.

  • Funding a repository. Maintainers or benefactors pay a coordinator either as a subscription or by topping up a per-repository CI funding pot. Because the pot belongs to the repository, a sponsor need not be a maintainer.
  • Sourcing compute. A coordinator can run jobs on its own machines, buy them job by job from a pay-per-use compute market such as Loom, or combine the two, choosing by availability and how far it trusts each provider with a given job. The CI protocol is agnostic about how a coordinator and a compute provider coordinate and settle payment; Loom is one candidate for that layer, not part of the protocol.
  • Keeping trust separate. Paying for a run buys compute, not credibility. Each Job Result still names the provider that executed it, and clients rate it by the same provenance as any other result.

Today, billing is arranged out of band: an operator agrees to serve your repository for free or on terms settled directly, and the coordinator's advertisement says whether it bills. The roadmap covers the market side in more depth.

Protocol, implementation, and direction

Protocolngit-ci todayDirection
Runner familiesAny; advertised per coordinatoract on Linux containersA Nix-native runner across platforms and architectures
Who executesCoordinator or separate providersThe coordinator's own host or attached microVM adapterOne coordinator routing jobs to several machines, then independent providers
AdmissionOperator-selected, maintainer-request, or openOperator allowlist onlyProviders a maintainer can select without an operator conversation
BillingNot required or out of bandNot requiredMaintainers or benefactors fund a repository by subscription or a funding pot; coordinators buy compute from a market such as Loom
TrustProvenance in events; no global scoreSameSame principle; richer client evidence

The imported NIP-C1 Nostr CI Specification owns the left column. Run ngit-ci owns the middle. The roadmap owns the right, and it is direction rather than promise.

Prior art and acknowledgements

The protocol was developed in collaboration with Arjen (Origami74), and it builds on earlier work:

  • Hive CI: Arjen's earlier protocol for running GitHub Actions workflows on Loom workers. Nostr CI takes its place. The event vocabulary stays close to Hive CI's, with coordinator-signed results in place of Loom job envelopes.
    • Budabit: an early adopter of Hive CI, in its Git extension for Flotilla, the Discord-like Nostr client.
  • Loom: Arjen's decentralised compute market on Nostr. ngit-ci adopts Loom's execution-adapter contract unchanged as its sandbox boundary, so Loom's adapters work with it as they are, and Loom remains a candidate market for coordinators buying compute.
  • rith: thesimplekid's CI runner, which validated the job shape ngit-ci uses: act executing GitHub Actions-format workflows inside a fresh sandbox per job.

Read the specification

Next

Git collaboration, without the platform.