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
- 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.
- Plan. On a trigger, sparse-fetch only the CI paths at the exact commit and match the files in
.ngit/act/workflows/against it. - 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. - 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.
| Topic | Specification | ngit-ci today |
|---|---|---|
Admission (M) | operator-selected, maintainer-request, or open | Always operator-selected; repositories come from the operator's allowlist |
Execution (X) | automatic or request-required | request-required by default |
Runner families (W) | Any; act and nix are named as examples | act only, so jobs run in Linux containers and macOS or Windows labels cannot be served |
| Where jobs run | Coordinator or separate compute providers | The coordinator's own host, or a sandbox adapter it operates |
| Job Result signer | May differ from the coordinator | Same key as the coordinator |
o values | push, pull_request, schedule, manual | schedule is never produced |
in-progress job tag on Progress | Defined | Not emitted |
| Secret sealing to a NIP-46 bunker | Defined through the reserved name | Implemented, 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
| Environment | Starting point | Why |
|---|---|---|
| Fresh VPS or any Docker host | Docker or Podman | The recommended layout, with a Docker-in-Docker sidecar or a host daemon socket |
| NixOS | Flake modules | Declarative coordinator and adapter services with protected credentials |
| Proxmox | Unprivileged LXC | Fits an existing virtualisation host; KVM sandboxing needs a VM instead |
| Untrusted workflows | microVM adapter | A fresh QEMU/KVM microVM per job, destroyed afterwards |
| Custom host | Static release archives or cargo install ngit-ci | Tagged 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 -dThat 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
| Runner | Isolation | Use when |
|---|---|---|
Embedded act | Per-job container on the coordinator's host | You control the repositories and the host runtime |
| Socket adapter | Adapter-defined | You already operate a sandbox that speaks the Loom execution-adapter contract |
| First-party microVM adapter | Fresh QEMU/KVM VM per job | You 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_RELAYSto 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
- Use Nostr CI: what maintainers do once you are listed
- act workflow files: what the coordinator accepts and refuses
- Understanding Nostr CI
- Host ngit-grasp
- Self-hosting overview