oni
A static Git browser for debuman's public repositories. The Rust generator,
sqzer-stagit, produces the repository catalog, commit history, diffs, file views,
branches, tags, rendered READMEs, and Atom feeds. It reimplements stagit's feature
set without invoking the C stagit executable.
This project maintains its own HTML templates and CSS. Local build artifacts and
repository snapshots are kept in .cache/.
The site is published at oni.sqzer.com through GitHub Pages. The source repository is debuman/oni.
All project documentation, code comments, and docstrings are written in English. Mirrored repository content and Unicode test fixtures retain their original text.
Running locally
For a fresh checkout, install the dependencies listed below and run these setup commands from the project directory in WSL Ubuntu:
cargo fetch --locked --manifest-path generator/Cargo.toml
python3 scripts/bootstrap_git.py
Then build the initial repository snapshot in Windows PowerShell:
.\build.ps1
python .\scripts\serve.py
Preview: http://127.0.0.1:4174/
For later rebuilds, use .\build.ps1 -Offline to reuse the local repository
snapshot. Omit -Offline to refresh the public repository list and Git data from
GitHub. The generator itself is always compiled offline from the locked local
dependency cache.
These setup commands require network access. The Git bootstrap verifies the pinned
upstream SHA-256 and installs under .cache/secure-git/; it does not replace system
Git. Builds select that installation and require Git 2.56.1 or newer.
Feed URLs use the local preview address by default. The build script accepts
--base-url https://oni.sqzer.com when generating files for that hostname.
CI and deployment
CI and deploy runs on
pull requests to main, pushes to main, and manual workflow dispatches.
- Every run tests Rust, generator behavior, security boundaries, and deployment packaging, then builds all public repositories and checks the generated site.
- A successful push to
maindeploys the verified artifact to GitHub Pages. Manual runs onmainalso deploy. Pull requests never deploy. - Repository snapshots are fetched fresh. Changes made only in other repositories appear on the next build; use Run workflow to refresh them without a source change in this repository.
- The build job has read-only repository permissions. Only the separate deployment job can write to Pages and request an OIDC token. Action versions are pinned to commit hashes, and checkout does not persist credentials.
- The CI runner uses Ubuntu 26.04, Python 3.14, Rust 1.99.1, and the checksum-pinned local Git build. Cargo dependencies and the patched Git installation are cached.
GitHub Pages is configured for GitHub Actions and the custom domain oni.sqzer.com,
with HTTPS enforced. The DNS CNAME points oni.sqzer.com to debuman.github.io.
Generated HTML and repository archives are deployment artifacts, not source commits.
The packager preserves dot-prefixed repositories such as .github while rejecting
actual Git metadata directories, symlinks, and special files.
The hosted site retains HTML sanitization, meta CSP, and referrer policy. GitHub Pages does not apply this project's custom HTTP response headers; see SECURITY.md for that hosting boundary.
Structure and dependencies
generator/: Rust generator with a locked Ammonia HTML5 sanitizer dependency.generator/src/repo.rs: Repository access through the Git CLI. Repository code is not executed.generator/src/html.rs: HTML documents, navigation, catalog, and shared layout.generator/src/main.rs: File, commit, diff, reference, and feed generation.generator/src/markdown.rs: Rust bindings to the system MD4C library's public API.theme/style.css: Locally maintained CSS with no JavaScript, web fonts, or CDN dependencies.scripts/build.py: GitHub API requests, bare repository synchronization, compilation, generation, and archives.scripts/readme_assets.py: Relative README image and file link resolution.scripts/benchmark.py: Scaling benchmarks and output equivalence checks.scripts/package_site.py: Validated GitHub Pages artifact packaging..github/workflows/deploy.yml: CI checks and deployment to GitHub Pages.security/csp.txt: Shared browser content policy for generated HTML and preview responses.SECURITY.md: Trust boundaries, security review findings, limits, and deployment requirements.public/: Generated site, including license notices underlicenses/..cache/repos/: Local Git inputs with remote push URLs disabled..cache/cargo/: Compilation output..cache/previous-*/: Previous local previews.
Builds run in WSL Ubuntu and require Rust/Cargo, Git, Python 3, a system linker,
libmd4c-dev, libmd4c-html0-dev, and prlimit from util-linux. The Git bootstrap
also needs make, a C compiler, zlib/OpenSSL headers, and curl development headers
(downloaded locally from Ubuntu when absent). Running the generator requires Git and
libmd4c-html. The generated site contains static HTML, CSS, and images, so the
hosting server does not need to run the generator.
MD4C handles Markdown parsing. Page generation and styles are maintained in Rust
and CSS; no separate C page generator or libgit2 is used.
Ammonia 4.2.4 sanitizes the resulting HTML with an HTML5 parser. It is the only
direct Cargo dependency; Cargo.lock pins its transitive dependencies.
Behavior
- The catalog is sorted by the latest commit date.
- Log shows the latest 50 commits; detail pages cover all commits reachable from HEAD.
- Rename and copy detection recognizes files with identical content. Diff graphs preserve insertion/deletion proportions.
- Tags outside the default branch receive feed entries and commit detail pages.
- The README navigation link opens the rendered
README.htmlpage. - README selection order is
README,README.md,README.txt,readme,readme.txt, thenREADME.markdown. - The Files view's
file/README.md.htmlshows the original text with line numbers. - Raster images with expected file signatures are copied into
raw/. Repository SVG images use commit-pinned URLs on GitHub's separate raw-content origin. - README HTML retains safe formatting, tables, details, tasks, and fenced code; executable content, inline styles, forms, and unsafe URL schemes are removed.
archive.tar.gzcontains the default branch's HEAD source tree.- Diffs for deleted files remain readable and do not link to nonexistent current file pages.
- Repositories made private or deleted are excluded from the next online build.
- The generator also supports empty repositories.
Verification
From the project directory in WSL:
cargo test --offline --locked --manifest-path generator/Cargo.toml --target-dir .cache/cargo
cargo build --release --offline --locked --manifest-path generator/Cargo.toml --target-dir .cache/cargo
python3 scripts/test_generator.py
python3 scripts/test_security.py
python3 scripts/test_package_site.py
python3 scripts/audit_site.py
Tests create temporary local Git repositories to exercise Markdown, history
limits, complete commit pages, Unicode paths, binary and deleted files, renames
and copies, README selection, unmerged release tags, feed links, and empty
repositories. They also cover empty, mode-only, and merge diffs, navigation,
and README directory links. Stream tests cover marker-like filenames and source
lines, binary payloads larger than a pipe buffer, empty blobs, and child process
cleanup after success or failure.
Security tests cover independent XSS payloads, unsafe assets, metadata and output
symlinks, path traversal, size limits, preview host/origin checks, and HTTP headers.
Run audit_site.py after building to check every page, local link, feed, and archive.
The preview server requires Python 3.12 or newer.
Performance
Let F be the file count, C the generated commit count, and L the README link count. For fixed filename lengths and path depths, the following repeated scans have been removed. HTML output and Git's own diff computation costs are separate.
| Operation | Before | Current |
|---|---|---|
| README, license, and submodule navigation selection | O(F × (F + C)) | O(F) setup, then O(1) per page |
| README directory link lookup | O(F × L) | O(F) setup, then average O(1) per link |
| Catalog input loading | Full history and file tree per repository | Latest commit only per repository |
Commit numstat and patch data are calculated together, eliminating duplicate diff
calculations. A persistent git diff-tree --stdin process handles all commits
in each repository, reducing diff process launches from C to one. Diff computation
still scales with the commits and their changes; replies are consumed one commit
at a time. Tags already covered by HEAD do not require another history query.
Output directories are prepared once per distinct parent directory instead of
checked for every page. README images share one lazy git cat-file --batch
process instead of launching Git for each image. Source lines are HTML-escaped
directly into the destination string, and patch rendering borrows its input buffer
without shifting the patch bytes. The build script also avoids copying the log
over an index page the generator has already written.
The benchmark script creates temporary repositories and checks that generated files and catalog HTML are byte-identical between versions. It warms each workload and reports the median of three runs by default. Compilation, network access, and archive generation are excluded. Git process counts are recorded separately from timing runs, and the optional asset comparison includes image extraction.
python3 scripts/benchmark.py --baseline /path/to/previous/sqzer-stagit \
--baseline-assets /path/to/previous/readme_assets.py \
--report .cache/performance-scaling.json
--baseline-assets is optional. Use --sizes, --history, --images, and
--repeats to adjust file, commit, and image counts and repetitions. Compare
binaries with the same site identity so the output equivalence check measures
implementation changes. Security sanitization intentionally changes HTML; do not
use a pre-sanitization binary as a byte-equivalence baseline.
Attribution and licenses
Project code is licensed under MIT; see LICENSE. Upstream stagit is a functional
reference, and its notice is retained in third-party/stagit-LICENSE.
MD4C's notice is retained in
third-party/md4c-LICENSE. License files are copied into the generated site;
no generator credit is displayed in the page footer.
Cargo dependency license texts are retained in third-party/cargo-LICENSES.txt;
regenerate them after lockfile changes with python3 scripts/license_notices.py.
- Upstream stagit: https://codemadness.org/stagit.html
- MD4C: https://github.com/mity/md4c