Install
The fastest way to see nit working is the development stack: PostgreSQL, a Gitea acting as the forge, the control plane, a worker and the web console — with a seeded repository and three accounts whose access deliberately differs.
The development stack
Section titled “The development stack”git clone https://github.com/NitScm/nitcd nit/deploy/dev
docker compose up -ddocker compose logs -f bootstrapThe bootstrap service creates the Gitea account and repository, seeds it,
writes the policy bundle, applies the database schema, issues tokens, and prints
what to do next. Give it a minute the first time — it builds the images.
When it finishes:
| Web console | http://localhost:4200 |
| API | http://localhost:8080 |
| Gitea | http://localhost:3000 — nit-admin / nit-admin-password |
The three accounts
Section titled “The three accounts”| User | Group | Can see |
|---|---|---|
alice | platform | Everything, including secrets/, infra/production/ and CI |
bob | backend | src/server, src/shared, docs — no secrets |
carol | frontend | src/ui, src/shared, docs — no secrets |
Their tokens are in the bootstrap output, and afterwards:
docker compose exec nitd cat /var/lib/nit/tokensSigning in to the console
Section titled “Signing in to the console”Open http://localhost:4200, leave Server empty, and paste alice’s token.
Alice is in the platform group, which is what the server’s NIT_ADMIN_GROUPS
names. Bob and carol sign in successfully but every page reports “not found” —
the operations API is invisible to a non-operator, and that is deliberate.
Getting the CLI
Section titled “Getting the CLI”The nit binary is inside the image; copy it out, or build it (below).
docker compose cp nitd:/usr/local/bin/nit ./nitsudo mv nit /usr/local/bin/Then follow Your first workspace.
Stopping
Section titled “Stopping”docker compose down -v # -v also removes the volumesReleased binaries
Section titled “Released binaries”Every release publishes signed-by-checksum archives and Linux packages at github.com/NitScm/nit/releases. The binaries are static and need no runtime beyond git.
Ubuntu and Debian
Section titled “Ubuntu and Debian”VERSION=0.1.0curl -LO "https://github.com/NitScm/nit/releases/download/v${VERSION}/nit_${VERSION}_linux_amd64.deb"sudo apt install "./nit_${VERSION}_linux_amd64.deb"
nit versionThe package installs all four binaries into /usr/bin and declares a
dependency on git, so apt refuses rather than leaving you with a worker that
fails on its first task.
On an ARM machine — a Raspberry Pi, an AWS Graviton instance — replace amd64
with arm64.
Fedora, RHEL and openSUSE
Section titled “Fedora, RHEL and openSUSE”VERSION=0.1.0sudo rpm -i "https://github.com/NitScm/nit/releases/download/v${VERSION}/nit_${VERSION}_linux_amd64.rpm"Any Linux, or macOS
Section titled “Any Linux, or macOS”Two archives, split by who runs what. nit_… carries nit and nitctl —
the tools a person runs. nit-server_… carries nitd and nit-worker.
Neither carries the engineering documents from the repository’s docs/. Those
are written for people modifying nit, and a copy on disk drifts from this site
and from the binary it shipped with. What you are reading is the current one.
VERSION=0.1.0OS=linux # or: darwinARCH=amd64 # or: arm64
curl -LO "https://github.com/NitScm/nit/releases/download/v${VERSION}/nit_${VERSION}_${OS}_${ARCH}.tar.gz"curl -LO "https://github.com/NitScm/nit/releases/download/v${VERSION}/checksums.txt"
sha256sum --check --ignore-missing checksums.txt
tar xzf "nit_${VERSION}_${OS}_${ARCH}.tar.gz"sudo install -m 0755 nit nitctl /usr/local/bin/For a server, swap nit_ for nit-server_ — or install the .deb or .rpm
above, which carry all four binaries and declare the dependency on git.
Check the checksum before running anything. It takes one command, and it is the only step that distinguishes the archive you meant to download from one you did not.
Windows
Section titled “Windows”Developers on Windows get nit and nitctl. Download
nit_<version>_windows_amd64.zip from the releases page, or:
$Version = '0.1.0'$Arch = 'amd64' # or: arm64 on a Surface Pro X or similar
Invoke-WebRequest -Uri "https://github.com/NitScm/nit/releases/download/v$Version/nit_${Version}_windows_$Arch.zip" -OutFile nit.zipInvoke-WebRequest -Uri "https://github.com/NitScm/nit/releases/download/v$Version/checksums.txt" -OutFile checksums.txt
# Verify before extracting.(Get-FileHash nit.zip -Algorithm SHA256).Hash.ToLower()Select-String -Path checksums.txt -Pattern "windows_$Arch.zip"
Expand-Archive nit.zip -DestinationPath "$env:LOCALAPPDATA\nit"Then put it on your PATH, for this session and the next:
$env:Path += ";$env:LOCALAPPDATA\nit"[Environment]::SetEnvironmentVariable( 'Path', [Environment]::GetEnvironmentVariable('Path', 'User') + ";$env:LOCALAPPDATA\nit", 'User')
nit versionYou also need git for Windows — nit produces and applies patches, it does not reimplement git.
With Go
Section titled “With Go”If you already have Go 1.25, this is the shortest route on any platform, and the binary still reports its version — the toolchain stamps the module version and the revision even without a release build.
go install github.com/NitScm/nit/cmd/nit@latestgo install github.com/NitScm/nit/cmd/nitctl@latestConfirming what you installed
Section titled “Confirming what you installed”nit versionnit v0.1.0 (a1b2c3d4e5f6) built 2026-08-22T21:00:00Z go1.25.11 linux/amd64Quote that line in a bug report. A build that cannot say which build it is
turns every report into a guess — which is why dev (unknown) appears instead
of nothing when a binary was built outside a release.
From source
Section titled “From source”You need Go 1.25 and git.
git clone https://github.com/NitScm/nitcd nit
make build # bin/nit bin/nitd bin/nit-worker bin/nitctlmake test # needs no infrastructureexport PATH="$PWD/bin:$PATH"Four binaries come out:
| Binary | Runs where |
|---|---|
nit | A developer’s machine |
nitd | The control plane — the API |
nit-worker | Anywhere with git, disk and access to the forge |
nitctl | An operator’s machine, or the server |
To bring up a server you also need a database and a policy bundle. PostgreSQL 13+ is recommended; MySQL 8.0.16+ and MariaDB 10.6+ are supported too, and Configuration covers what differs. The shortest path:
createdb nit
nitctl config init # writes /etc/nit/nit.yaml, mode 600openssl rand -base64 32 > /etc/nit/sync.key && chmod 600 /etc/nit/sync.key$EDITOR /etc/nit/nit.yaml # database.url, policy.dir, admin_groups
nitctl config show # every value, and where it came fromnitctl migratenitd &nit-worker &See Configuring the server for what goes in that file, and Writing a policy bundle for the bundle.
Containers, for a real deployment
Section titled “Containers, for a real deployment”deploy/production/ carries a forge-agnostic Compose base plus one overlay per
forge:
cd nit/deploy/productioncp .env.example .env && chmod 600 .env$EDITOR .env
docker compose -f compose.base.yaml -f compose.gitea.yaml up -ddocker compose -f compose.base.yaml -f compose.github.yaml up -ddocker compose -f compose.base.yaml -f compose.gitlab.yaml up -dRead Going to production before you do — there are three things about the forge that have to be true, or nit’s guarantees do not hold.
Verifying an installation
Section titled “Verifying an installation”docs/VALIDATION.md in the nit repository is a step-by-step walkthrough that
proves each property in turn — read filtering, refused pushes, the CI guard, the
audit trail, recovery from a dead worker — with the output you should see at each
step.
- Your first workspace — clone, change something, push it.
- Your first policy — write the rules from scratch.