Gunnar

nordisk/gunnar

.nornir/README.md

<!-- nornir:class: readme -->

# gunnar

<img src=".nornir/assets/gunnar.webp" alt="gunnar" width="320">

**Fast setup:** [the guide](.nornir/gunnar-quickstart.md) · [PDF](docs/gunnar-fast-setup.pdf) — four lists of commands, one picture per setup.

**Features:** [what is new, with status, source path and commit](docs/FEATURES.md).

Git server in Rust, with two engine variants: znippy-based (Apache Arrow IPC in
a znippy archive) or gix-based (traditional filesystem).

* Built-in replication to a twin gunnar: self-hosted, the twin is any box that reaches the primary (`gunnar control twin join`); a gunnar.rs instance's twin runs in the same zone as its main.
* Vetra hardware appliance variant: OS-less, hardened kernel.
* Free to host your own.
* The exact same code is hosted at https://gunnar.rs
* NVMe, io_uring, XFS on raid0, OS-less appliance kernel, WireGuard, Prometheus, Git LFS, smart HTTP, OCI, webhooks, replication to a twin, sshsig/ed25519 signed pushes, SLSA-shaped provenance, transparency log.

## Building

Anyone can clone the repository read-only, with no registered SSH key:

```
git clone ssh://anonymous@git.gunnar.rs:2222/nordisk/gunnar.git
cd gunnar
cargo build --release -p gunnar-cli
```

A lone clone builds the server by itself, with nothing beside it:

```
git clone ssh://git.gunnar.rs:2222/nordisk/gunnar && cd gunnar && cargo build --release
./target/release/gunnar        # serves on loopback; http://127.0.0.1:8080/healthz answers ok
```

`cargo build --release` at the root builds the default member, `gunnar` (the
server: engine-znippy, SSH and smart HTTP with TLS via `--http-tls-*`; no mimalloc, no company SSO, no monetize: `--features mimalloc,sso` adds those). The committed
`Cargo.lock` is the crates.io lock, so `cargo build --locked` and `cargo install --locked
--path crates/gunnar-cli --features client` work from a lone clone too (`cargo xtask
lone-clone` proves it on every push). The root takes no sibling patch in any checkout.
In an estate checkout, with `../edda`, `../znippy`, `../Skidbladnir`, `../tunnr`,
`../draupnir` and `../monetize` beside it, the detached estate workspaces
(`gunnar-front`, `gunnar-monetize-guest`, the benches, `installtest`) build against
those siblings through the `[patch.crates-io]` in their own manifests, with
`--manifest-path`.

This page documents **gunnar 1.5.1** — the version `gunnar --version` prints.

License: AGPL-3.0-only, © 2026 Vetra Automation AB and Ignalina ApS (Rickard Ernst Björn Lundin). Commercial
terms: `COMMERCIAL-LICENSING.md`.

## Install the server with cargo

```
cargo install gunnar
gunnar
```

A bare `gunnar` serves on loopback with no configuration: SSH on `127.0.0.1:2222`,
smart HTTP on `127.0.0.1:8080`, and health at http://127.0.0.1:8080/healthz. The
default build compiles no monetize crate; the other variants are opt-in cargo
features, listed in [`crates/gunnar/README.md`](crates/gunnar/README.md).

## Release provenance (SLSA Build L3)

Releases are built by dwarves, the build server, in its isolated release-build mode and carry signed SLSA v1 provenance:
one in-toto statement per release, every artifact by its sha256, signed on the owner's
YubiKey. The bundles are in [`attestations/`](attestations/), the release key in
[`slsa/`](slsa/); how to verify (cosign, dwarves or Holger) and what still keeps us below
L3: [docs/SLSA.md](docs/SLSA.md).

## Complexity and measurements

`n` objects, `r` refs, `e` refs in a batch, `k` batch size, `s` object size,
`|W|`/`|H|` want/have closure. Read off the znippy engine's code.

| operation | znippy |
|---|---|
| `put_pack` | `O(bytes)`, index deferred |
| `put_refs` | `O(r) + O(e·log n)` |
| `get` | `O(log n) + O(s)` |
| `has` | `O(log n)` |
| `size` | `O(log n)` |
| `extents` | `O(k·log n)` |
| `refs` | `O(r)` |
| `update_ref` | `O(log r)` |
| `head` | `O(log r)` |
| `header` | `O(log n)` |
| `reachable` | `O(1)` map lookup + bitmap OR |
| `gc` | `O(n)` compact |

Measured. Each row names its date. A dash means no measurement exists.

| measurement | gitea | gix | znippy | date |
|---|---|---|---|---|
| clone, serve wall, 16384 commits | — | 17.7 s | 7.23 s | 2026-09-01 |
| push, receive-pack, 16384 commits | — | 0.80 s | 1.54 s | 2026-09-01 |
| RSS floor, 16384 commits | — | 13.6 MB | 29.2 MB | 2026-09-01 |
| `reachable`, 16384 commits | — | 14.9 s | 5.6 ms | 2026-09-01 |
| `refs` | — | — | 30–50× faster than gix | 2026-09-01 |
| `header` | — | ~5× faster than znippy | — | 2026-09-01 |
| durable 8 KiB push, index included | — | 50 573 µs (library writer, no fsync) | 147.7 µs | 2026-08-07 |
| provision 100 000 users | 77 min, projected | — | 6.1 s | 2026-08-12 |
| ls-remote p50 @ 100 000 users | 27.0 ms | — | 10.0–10.5 ms | 2026-08-12 |
| ls-remote p99 @ 100 000 users | 39–43 ms | — | 102–168 ms | 2026-08-12 |
| ls-remote throughput | 1 140 req/s | — | 2 060–2 320 req/s | 2026-08-12 |
| clone (small), net CPU | 0.033 s | — | 0.010 s | 2026-08-13 |
| clone (small), peak RSS | 113 MB | — | 2 308 MB | 2026-08-13 |
| push (base), net CPU | 0.944 s | — | 0.302 s | 2026-08-13 |
| push (base), server→client bytes | 2.1 kB | — | 1.4 kB | 2026-08-13 |

Conditions: 2026-09-01 rows, the desktop, 5 repetitions plus warm-up, load-1
mean 13.4, both engines in one binary. 2026-08-13 rows, the desktop, 32 cores,
3 interleaved rounds, 8 CPUs and 8192 MB per engine, byte counts taken by a
counting relay. 2026-08-12 and 2026-08-07 rows, the desktop.

## Thanks

* Its inventors: Vetra Automation AB and Ignalina ApS (Danskjävlarna).
* Ragnar Groot Koerkamp — the ridiculously fast static search tree.
* Christopher Berner — the neat redb.

## Contributing, security and changes

- [CONTRIBUTING.md](CONTRIBUTING.md): how to get the code, report a bug and propose a change.
- [CLA.md](CLA.md): the Contributor License Agreement (version 1.1) that every outside contribution needs; accept it with one line in [CLA-SIGNATURES.md](CLA-SIGNATURES.md).
- [SECURITY.md](SECURITY.md): how to report a vulnerability privately (gunnar@gunnar.rs).
- [CHANGELOG.md](CHANGELOG.md): what changed in each release, and the vulnerabilities each one fixed.

## Sponsors

None so far — we would love to have one. Contact gunnar@gunnar.rs.

AI-generated details: [.nornir/detail.md](.nornir/detail.md)