Skip to content

Run ngit-ci ​

ngit-ci is the reference coordinator for Nostr CI, the CI extension to NIP-34 published as NIP-C1. Start by reading Understanding Nostr CI. Run ngit-ci to provide CI for the repositories you point it at. It watches them over Nostr, runs their workflows when a push, pull request, or manual trigger arrives, and publishes signed results that ngit and GitWorkshop show against commits and pull requests with the trust context the protocol carries.

There is no forge server and no runner registration. Several coordinators can serve the same repository, and trust is decided at the edges: every result is an attestation signed by the coordinator's key, and clients decide which keys mean something. The protocol separates the coordinator that schedules work from the compute providers that execute it. Today ngit-ci fills both roles on one host.

What it does ​

  1. Watch. Advertise itself on index relays, list the repositories it is ready to serve so maintainers find it in GitWorkshop's coordinator directory, and subscribe to repository state, pull requests, and manual triggers on each repository's own relays.
  2. Plan. On a trigger, sparse-fetch only the CI paths at the exact commit and match the files in .ngit/act/workflows/ against it.
  3. Run. Execute each claimed workflow with act, every job in its own container, or hand each job to a sandbox adapter such as the first-party microVM adapter.
  4. Publish. Sign a Job Result per job, Workflow Progress while the run is live, a Workflow Result that quotes every Job Result, and a Repository Status that confirms what it is acting on.

What ngit-ci implements ​

ngit-ci implements the request-gated, operator-selected corner of the specification. Maintainers and client authors integrating against a live coordinator should expect the right-hand column.

TopicSpecificationngit-ci today
Admission (M)operator-selected, maintainer-request, or openAlways operator-selected; repositories come from the operator's allowlist
Execution (X)automatic or request-requiredrequest-required by default
Runner families (W)Any; act and nix are named as examplesact only, so jobs run in Linux containers and macOS or Windows labels cannot be served
Where jobs runCoordinator or separate compute providersThe coordinator's own host, or a sandbox adapter it operates
Job Result signerMay differ from the coordinatorSame key as the coordinator
o valuespush, pull_request, schedule, manualschedule is never produced
in-progress job tag on ProgressDefinedNot emitted
Secret sealing to a NIP-46 bunkerDefined through the reserved nameImplemented, with an operator fallback bunker that is outside the protocol

Workflows are claimed as whole files, so a file whose jobs span architectures runs only on a coordinator that supports every label in it. Routing jobs to independent providers and a compute market are direction, not protocol.

Choose a deployment ​

EnvironmentStarting pointWhy
Fresh VPS or any Docker hostDocker or PodmanThe recommended layout, with a Docker-in-Docker sidecar or a host daemon socket
NixOSFlake modulesDeclarative coordinator and adapter services with protected credentials
ProxmoxUnprivileged LXCFits an existing virtualisation host; KVM sandboxing needs a VM instead
Untrusted workflowsmicroVM adapterA fresh QEMU/KVM microVM per job, destroyed afterwards
Custom hostStatic release archives or cargo install ngit-ciTagged releases publish statically linked x86_64 Linux binaries as NIP-82 assets; the host still needs git, act 0.2.86 or newer, and a container daemon or an adapter

Container quick start ​

From a clean checkout of the repository:

bash
NGIT_CI_REPOS=npub1.../my-repo docker compose up --build -d

That starts the coordinator plus a dedicated Docker-in-Docker daemon for job containers, watching the repositories listed in NGIT_CI_REPOS. The coordinator generates its signing key on first start and keeps all state in the coordinator-data volume. The Docker guide covers prerequisites, persistence, the host-socket variant, and upgrades.

A running coordinator serves nothing yet. Maintainers of the listed repositories must request service, as described below.

Configure the coordinator ​

Configure ngit-ci is the imported reference for every option, the Service Request policy, host requirements, the build cache, identity keys, multi-architecture layouts, resource limits, and per-repository secrets. Every option is accepted as a CLI flag, an NGIT_CI_* environment variable, or a .env entry.

The generated reference is the machine-checked authority for names, defaults, validation rules, and secret precedence. The coordinator and the microVM adapter are separate processes, so their pages stay separate and options cannot be applied to the wrong service:

The reference is labelled upcoming because it follows an immutable commit selected from the source repository’s current HEAD during sync. The exports and operator guides share that commit. Its package version does not imply that this snapshot is a stable release; each page records its exact provenance.

Choose an execution boundary ​

RunnerIsolationUse when
Embedded actPer-job container on the coordinator's hostYou control the repositories and the host runtime
Socket adapterAdapter-definedYou already operate a sandbox that speaks the Loom execution-adapter contract
First-party microVM adapterFresh QEMU/KVM VM per jobYou run workflows you do not fully trust

The socket contract is Loom's, adopted unchanged, so the existing Loom adapters work as they are. Run the microVM adapter covers the first-party one.

Execution policy and getting listed ​

By default, push and pull request workflows wait for a maintainer's standing Service Request addressed to this coordinator. Automatic execution is an explicit trust and resource decision, not a quick-start default, and runs it produces can never be rated maintainer-directed by clients. See the Service Request policy.

Listing a repository in NGIT_CI_REPOS discovers it and advertises the coordinator as ready for it, which is what puts you in GitWorkshop's coordinator directory for that repository. The maintainer then requests service from GitWorkshop or with ngit ci request, as in Request service. Manual triggers bypass the standing gate for one run. Any payment or terms are settled between you and the maintainer out of band; the advertisement only says whether you bill.

Secrets ​

Secrets reach only runs whose trigger a confirmed maintainer authored. 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 Repository Status lists only the names in use.

Values arrive by two paths:

  • Operator-provisioned. Environment variables or systemd credentials keyed to a repository alias, described under per-repo secrets.
  • Maintainer-provisioned over Nostr. Set NGIT_CI_NOSTR_SECRET_RELAYS to at least one inbox relay and the coordinator advertises a secrets key. Maintainers then provision values from GitWorkshop, encrypted to that key, and can bind their repository to their own NIP-46 bunker so the coordinator never holds the values at rest.

The generated secret resolution page owns the precedence between the two. The authorization rule, its decision sequence, and its regression coverage are in the repository's secret-authorization.md.

Protect the important boundaries ​

Identity ​

The coordinator's key is its reputation: clients attach trust context to it, and maintainers request service from it by pubkey. Supply it through a protected environment variable or a service credential, never on the command line, and back up the generated key from the data volume before you rely on it. See coordinator identity key.

Container runtime ​

CI executes repository code

The coordinator never runs workflow steps on its host, but its container runtime is still a high-value boundary. Job containers get no daemon socket by default, workflows that declare their own containers are refused under the default policy, and the operator dictates per-job resource caps. Keep those defaults unless you trust every workflow author, and use the microVM adapter for the rest.

See job container policy.

Storage and uploads ​

State lives in the work directory: the identity key, queue state, processed event ids, and the trust-scoped build cache. Logs and artifacts leave the host only when you enable Blossom uploads, and the uploaded files are then public by content hash. Enable uploads deliberately and cap them.

Network ​

The coordinator needs outbound access to relays, Git servers, and Blossom. It exposes nothing inbound. Job containers get outbound access too; if that is too much for the workflows you serve, the microVM adapter's outbound-only networking is the stronger boundary.

Next ​