A tiny, read-only Git web browser written in Rust.
Explore the repository »
Report Bug
·
Request Feature
Table of Contents
rsgit is a lightweight, cgit-inspired Git browser for repositories that are already meant to be visible. It serves a small HTML interface and read-only dumb HTTP clone endpoints from local Git repositories.
The project intentionally separates responsibilities:
- rsgit: browse and clone repositories over HTTP, read-only.
- Git over SSH: push to bare repositories using
git-receive-packoutside rsgit.
The current reader implements the subset of Git object storage rsgit needs for browsing, searching recent commits, and serving read-only clone endpoints.
- Rust
std::net::TcpListenerfor the minimal HTTP serverflate2with the Rust backend for Git object zlib inflation- A small custom Git object reader for refs, loose objects, pack indexes, pack objects, and deltas
- Repository index
- Repository search on the index page
- Repository summary
- Commit log
- Tree browser
- Blob viewer
- Commit view
- Commit-message search over recent commits
- Dark, cgit-inspired summary page
- Dumb HTTP clone support via
git clone http://host/repo/name - Built-in minimal HTTP server
- No
gitsubprocess execution - No push support by design
- Full cgit compatibility
- Authentication inside rsgit
- Push/write endpoints
- Smart Git HTTP protocol
- Syntax highlighting
- Async runtime or web framework
- Rust toolchain for local builds
- Local Git repositories to browse
- Docker, only if using the container image
Runtime does not require the git command for page rendering.
Clone and build:
git clone https://github.com/whoisclebs/rsgit.git
cd rsgit
cargo build --releaseRun locally:
RSGIT_ADDR=127.0.0.1:9000 \
RSGIT_REPO_ROOT=/path/to/repos \
cargo run --releaseOpen:
http://127.0.0.1:9000/
| Variable | Default | Description |
|---|---|---|
RSGIT_ADDR |
127.0.0.1:8080 |
Socket address for the HTTP server. |
RSGIT_REPO_ROOT |
. |
Directory scanned one level deep for repositories. |
RSGIT_PUBLIC_BASE |
unset | Public base URL used to render clone commands behind a proxy. |
RSGIT_REPO_ROOT is scanned one level deep. Each immediate child is treated as a repository if it is either:
- a normal Git working tree with
.git/, or - a bare repository with
HEADandobjects/.
Browse repositories:
http://127.0.0.1:9000/
Open a repository summary:
http://127.0.0.1:9000/repo/myrepo.git/summary
Clone through rsgit:
git clone http://127.0.0.1:9000/repo/myrepo.gitWhen deployed behind a public reverse proxy:
RSGIT_PUBLIC_BASE=https://git.example.comThe rendered clone command becomes:
git clone https://git.example.com/repo/myrepo.gitPush is intentionally handled by Git over SSH, not by rsgit:
git remote add vps ssh://git@example.com:2222/srv/git/myrepo.git
git push vps mainPull the published image:
docker pull ghcr.io/whoisclebs/rsgit:latestRun with a read-only repository mount and restricted privileges:
docker run --rm \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,nodev,size=16m \
--cap-drop=ALL \
--security-opt no-new-privileges:true \
--memory=128m \
--cpus=0.5 \
--pids-limit=64 \
-e RSGIT_ADDR=0.0.0.0:8080 \
-e RSGIT_REPO_ROOT=/repos \
-v /srv/public-git:/repos:ro \
-p 127.0.0.1:8080:8080 \
ghcr.io/whoisclebs/rsgit:latestOr with Compose:
docker compose up --buildDo not mount /, $HOME, Docker sockets, or private repository trees unless access is intentionally public or protected by external authentication.
rsgit is designed for public, read-only browsing of repositories.
Safeguards:
- repository names are constrained to one URL path segment;
- repository paths are canonicalized under
RSGIT_REPO_ROOT; - symlinked repository directories are rejected;
- revisions and Git tree paths are validated;
- no
gitsubprocess execution; - clone endpoints are limited to Git object and pack files;
- internal
file://clone paths are never displayed; - HTTP responses include basic security headers.
For public deployments, put rsgit behind a reverse proxy with TLS, request timeouts, and rate limiting. If repositories are private, add authentication at the proxy layer.
Measured on Windows/Cygwin with the golpher repository and the release binary:
| Scenario | Working Set | Private Memory | Notes |
|---|---|---|---|
| Idle | ~5 MiB | ~0.7 MiB | Server listening only. |
| After mixed requests | ~6.2 MiB | ~2.0 MiB | Summary, tree, log, blob. |
| Peak observed | ~6.5 MiB | ~2.2 MiB | After additional summary load. |
Sequential curl load of 1000 summary requests averaged about 0.06 vCPU on the test machine.
- Read refs and
packed-refs - Read loose Git objects
- Read pack indexes and pack objects
- Resolve OFS/REF deltas
- Render summary, log, commit, tree, blob, and search
- Serve dumb HTTP clone endpoints
- Native textual diff rendering
- Streaming clone object/pack responses instead of full-file reads
- More integration fixtures for packed and delta-heavy repositories
- Reverse-proxy deployment examples with SSH push container
Validation:
cargo fmt -- --check
cargo check
cargo clippy -- -D warnings
cargo testConventional commits are used:
feat: add new capability
fix: correct broken behavior
refactor: restructure without behavior change
docs: update documentation
Distributed under the MIT License. See LICENSE for details.