nitper-file permissions for one repository

The product

The whole authorization layer.

Free, self-hosted, no seat count and no key to buy. This page is the complete thing — there is no second page describing the part you would have to pay for, because there is not one.

Install it Read the source

01 Where it sits

In front of the forge, and the only thing that writes to it.

Developers push and pull through nit. It is not a plugin inside your forge and not a hook that runs after a change has landed — which is what makes a refusal a refusal.

Where nit sits between a developer and a forgeA developer's push goes to the nit control plane, which authorizes the patch and fails closed. Allowed changes go through a worker that clones, applies and pushes to the forge. A pull runs the other way: the worker diffs against the forge and the control plane filters the result before it reaches the developer. The forge only ever hears from nit.developernit push / nit pullcontrol planeauthorize, fail closedworkerclone, apply, filteryour forgeGitHub · GitLab · Giteaa push, on its way outa pull, filtered on the way backrefused here
The forge only ever hears from nit, which is what makes a refusal a refusal rather than a revert. The developer's clone is a filtered projection, so its commit hashes do not exist upstream; every exchange is expressed against a sync point the server recorded.

02 What it does, and what it guarantees

Filtered clones

A person receives the files they may read, as a working tree that builds. Not a placeholder, not a redacted file: what they may not read is not in what they received. The clone is a projection, so its commit hashes do not exist upstream, and every exchange is expressed against a sync point the server recorded.

Refused pushes

A change touching a path somebody may not write is refused at the boundary, naming the rule. nit is the only writer of the upstream, so a refusal happens before the forge has heard of the change — there is nothing to revert and nothing in anybody's history.

Rules in the repository

A YAML bundle: users, groups, repositories, and rules per repository. Reviewed in a pull request, versioned with everything else. Who changed a permission and who approved it is the same question as who changed any other line.

Deny wins, and unmatched is denied

Evaluation is order-independent. Every matching rule is considered, a deny always beats an allow, and a path no rule mentions is refused. Rules can be reordered or regrouped without changing behaviour — and an exemption is written with "except" rather than a competing allow, because listing every non-exempt group fails open the day somebody adds one.

Write implies read

An allow granting write, create or delete must also grant read. A subject writing a file it cannot see would overwrite content blind, and could not produce a diff against a file absent from its workspace.

Your forge underneath

GitHub, GitLab or Gitea. nit is in front of it, not instead of it. Remove nit and what is left is a git repository, on the forge you already pay for.

03 What it looks like to use

Ordinary git, with one extra verb.

nit clone, nit push, nit pull. Between them it is git commit and your editor, because what you have is a real working tree.

nit push --check answers before you submit anything, which is the difference between finding out now and finding out in a pull request.

A sessionbob, frontend group
$ nit login http://nit.internal
$ nit clone backend-api
$ cd backend-api

# Files bob may not read are simply not here.
$ ls
README.md  docs/  src/

$ vim src/ui/App.tsx
$ git commit -am "tidy the header"

# Authorize without submitting anything.
$ nit push --check
  ALLOW  src/ui/App.tsx   frontend-owns-ui-code

$ nit push -m "tidy the header"
$ nit pull

04 Running it

A service in front of your forge, and a database.

Four binaries — the CLI, the control plane, a worker and an administration tool — written in Go, with PostgreSQL underneath. There is a compose file that stands the whole thing up on one machine with a seeded repository and three accounts whose access deliberately differs, so you can watch a refusal happen before you commit to anything.

Nobody has to talk to us to do any of it. There is no licence server to reach, and nothing phones home. That is the whole of the product and it stays that way — the one thing on this site that involves talking to a person is theaudit vault, which is licensed rather than free, and it is optional in the strong sense: turn it off and nothing above changes.

Every binary, every setting

The whole thing, on one machinedeploy/dev
$ cd deploy/dev && docker compose up -d
$ docker compose logs -f bootstrap
  # prints the tokens, and what to try with them
Installing the toolsreleases
# Ubuntu / Debian — all four binaries
$ sudo apt install ./nit_0.2.0_linux_amd64.deb

# Anywhere, with Go 1.25
$ go install github.com/NitScm/nit/cmd/nit@latest

$ nit version

05 What it is worst at

Published, and worth reading before you adopt it.

A monorepo where a large team pushes to a single trunk is the shape nit handles worst, and the limit is architectural rather than a matter of tuning.

nit writes its decisions to its own database, which its own administrators can change. For most teams that is fine. If somebody outside your company will ask you to prove otherwise, there is a service for that.