gunnar-ui
The gunnar console: a passkey-guarded web UI (Leptos, WebAssembly) and its backend-for-frontend, which speaks sshsig-signed gRPC to a gunnar appliance.
- Anonymous browsing of public repositories.
- Mirrors from GitHub, Codeberg, Forgejo and GitLab.
- Accounts, keys, groups and grants.
- Usage and paid signup through a monetize server, compiled in by the
meteredfeature; the open build has no money code. - The exact same code is hosted at https://gunnar.rs
Building
git clone ssh://git.gunnar.rs:2222/nordisk/gunnar-ui
cd gunnar-ui
cargo build --release -p gunnar-ui-server
The console itself is a WebAssembly bundle, built by trunk, which is required:
cd crates/gunnar-ui-web
trunk build --release
This page documents gunnar-ui 1.5.0 — the version gunnar-ui-server --version prints.
License: AGPL-3.0-only, © 2026 Vetra Automation AB and Ignalina ApS (Rickard Ernst Björn Lundin). Commercial
terms: COMMERCIAL-LICENSING.md.
Routes
The console's pages, routed in the browser. A visitor has no session; signed in is any passkey session; admin is the role a session carries.
| page | who may open it |
|---|---|
/repos | visitor |
/repo/{namespace}/{name} | visitor |
/repo/{namespace}/{name}/files · /graph · /diff · /blame · /search · /chain | visitor |
/repo/{namespace}/{name}/pulls · /pulls/{n} · /issues · /issues/{n} | visitor |
/ | signed in |
/storage · /usage · /usage/{namespace} · /events | signed in |
/account | signed in |
/access | signed in; every mutation on it is admin |
/mirrors · /mirrors/import | signed in; every mutation on it is admin |
The server's own routes, read off serve.rs. Everything under /api/ that is
not listed as a visitor's is refused without a session; the role floor is
decided by method and prefix in one place (auth::middleware::role_floor).
| route group | who may open it |
|---|---|
/health · /login · /login/repos · /auth/status · /auth/logout | visitor |
/auth/passkey/login/* · /auth/passkey/register/* | visitor |
/welcome · /auth/signup/status (a metered build that sells) | visitor |
/signup — the ONE sign-up page: ?plan=private a new private Gunnar, ?plan=10gb an account here (or, signed in, growing it); /private redirects to it · /auth/private/ladder · /auth/private/{quote,start,status} · /auth/signup/{ladder,name,start,rename} (a metered build that sells) | visitor; a signed-in buyer's order is theirs (the session's name, locked) |
GET /api/repos | visitor |
GET /api/repos/{namespace}/{name} and its refs · head · log · browse · diff · blame · search · object · chain leaves | visitor |
every other GET/HEAD under /api/, including /api/events | signed in |
/api/account/keys — a person's own keys | signed in |
every other mutation under /api/ | signed in, operator |
/api/principals · /api/keys · /api/grants · /api/namespaces · /api/accounts · /api/forges · /api/mirrors, mutating | admin |
/api/namespaces/{name}/increase (a metered build holding a monetize bearer) | signed in to read, admin to spend |
/auth/passkey/invites · /auth/passkey/credentials | signed in, a fresh ceremony to mint or revoke; gunnar decides who may invite whom and whose passkeys a person may see |
/auth/sessions/revoke-all | admin |
Passkeys, invites and sessions live in gunnar
The console keeps no passkey, no invite and no key of anybody's. gunnar
issues the WebAuthn challenge, verifies every login against the passkey it
stores on the person's principal (Access.WebLogin), and enrols a short-lived
session key the console minted for that login; everything the session does is
signed with that key, as the person, and gunnar's grants decide. Logging out
revokes the session key. Registration (Access.WebRegister) redeems an invite
gunnar minted (Access.CreateInvite, asked as the person who invites), or is
the operator's first passkey through the installer's break-glass token. A
passkey this console registered before 2026-10-04 moves to gunnar when the
console starts. The console's own store keeps cookie sessions, preferences and
caches only.
Thanks
- Its inventors: Vetra Automation AB and Ignalina ApS (Danskjävlarna).
- Greg Johnston and Ben Wishovich — Leptos.
- Anthony Dodd and Jens Reimann — Trunk.
Sponsors
None so far — we would love to have one. Contact gunnar@gunnar.rs.