romele / git-trace public
62 commits · 1 branch · 1 tag · 1 contributor (top: Romele) · first commit 3d ago · last push 17m ago
doc/web.md
# Web interface Server-rendered, zero JavaScript, fully CSP-compliant (`default-src 'none'`). Light/dark follow the OS via `prefers-color-scheme` — pure CSS, no toggle. - `/` — for anonymous visitors a marketing landing: hero (logo, name, version, pitch, CTA) carrying a live "mini-trace" — the newest commits of the most recently pushed public repo rendered with the same timeline markup as the log view — then instance metrics (repos / users / storage), recent activity on public repos and featured repos; for logged-in users a compact dashboard. Underneath, the searchable repo list with public/private badge, cached size, description, ordered by recent activity (relative times). Instance name is set by `GIT_TRACE_NAME`. - `/new` — create a repository from the browser (name, description, private flag); shares `repo_create()` with the `mkrepo` CLI so hooks, description and DB registration are identical. - `/<owner>/<name>` — redirects to the `tree` view. The repo header shows the `owner / name` identity, a public/private badge, clone URLs as readonly inputs (HTTP always; SSH too when `GIT_TRACE_SSH_URL` is set — click → select-all → copy, no JS) and a quota line. Below the file list, a root `README*` is the page's main content: `.md`/`.markdown` files go through a small in-tree renderer (`src/markdown.c`, ~300 lines — headings, fenced code, lists, quotes, rules, `code`/`bold`/`em`, links and bare autolinks restricted to inert schemes; everything else is escaped), other names render as preformatted text. An unborn HEAD renders an "empty repository" quickstart panel with copy-paste `remote add`/`push -u` lines. Every repo page carries a summary line under the `owner / name` header — commits, branches, tags and last-push time — sourced from `for-each-ref` + `rev-list --count`. - **Web commits** — writers get `+ new file` / `⇪ upload` in the tree view and `edit` on the blob view. Each action produces a real commit through bare-repo plumbing (`hash-object` → private `GIT_INDEX_FILE` → `write-tree` → `commit-tree` → the same `branch-check`/`quota-check` gates as `git push` → `update-ref` CAS). Uploads are binary-safe multipart, capped at 4 MiB; the editor caps at 1 MiB. `GT_VERSION` and commit author/committer come from the forge account. - `/<owner>/<name>/log` — commits rendered as a vertical "trace" timeline (connected nodes, relative times, exact UTC in the tooltip) rather than a flat table — the project's namesake view. - `/<owner>/<name>/changes` — unified list of merge requests and patch sets (type badge, state, author, updated), ordered by last update; `/mr/N` and `/patches/N` remain the canonical detail routes. - `settings` and `activity` tabs; `settings` is only rendered for rank ≥ admin and also carries repository deletion: typing the repo name confirms, the on-disk tree is removed (`nftw`, no symlink following) and the DB row cascades to ACLs, issues, MRs, patch sets and branch rules. `repo_delete` lands in the audit log. - `/account` — user self-service (password, email, API tokens, SSH keys; see `security.md`). - `/search?q=` — fixed-string code search across the tree at the current ref, powered by `git grep -n -F` on the bare repo (no worktree, no index, no regex DoS); results link straight into the `blob` view. `i=1` makes it case-insensitive; 200-result cap. - `/feed` — Atom feed of the last 40 commits (`application/atom+xml`), timestamps in RFC 3339, entries deep-link `/commit/<sha>`. Private repos keep the same read gate, so a feed reader only works with a session cookie. - Syntax highlighting — `view_blob` passes text blobs ≤ 256 KiB through a ~200-line in-tree tokenizer (`src/highlight.c`): comments, strings, keywords, numbers and C preprocessor lines get `<span class=hl-*>` styling on a quiet earth-tone palette. Profiles: C/C++-family and script/config (`#` comments). Unknown extensions and oversized files render as plain escaped text. ## Collaboration Under `/<owner>/<name>`: - `issues` — tracker with create/comment/close-reopen (author or write permission), `issues/new` form for logged-in users - `mr` — merge requests between same-repo branches. Merging happens server-side, purely in the bare repo: `merge-tree --write-tree` produces the tree (or reports conflicts back to the page), `commit-tree` creates a two-parent merge commit, `update-ref` applies it with compare-and-swap on the old tip so concurrent pushes cannot be silently overwritten. Merging requires write permission. - `patches` — GNU patch sets, see `patch-workflow.md` - `settings` — description, visibility, ACLs, branch protection, webhooks, mirrors, CI toggle, danger zone ## Static export `git-trace export` renders repositories to a self-contained static site, `stagit`-style — plain HTML with inline CSS, browsable straight from `file://` or behind any dumb web server: ```sh git-trace export # all public repos -> ./export/ git-trace export -o /var/www/site -n 50 # custom dir, cap 50 commits git-trace export alice/demo _/site # explicit selection git-trace export --private # include private repos too ``` Layout: ``` export/index.html repo index export/<owner>/<repo>/index.html file list + README + nav export/<owner>/<repo>/log.html recent commits export/<owner>/<repo>/refs.html branches and tags export/<owner>/<repo>/commit/<sha>.html metadata + diff export/<owner>/<repo>/file/<path>.html highlighted source ``` Discovery mirrors the web index: the filesystem is the source of truth and `repo_perm` filters to what an anonymous reader could see. `--private` lifts that filter — exporting *is* publishing, so private code requires the explicit flag. All links are relative, file pages mirror the repo tree (encoded components, `.`/`..` neutralised), binary blobs are skipped and diffs are capped at 512 KiB. ## SSH access Single OS account model (gitolite-style): every user authenticates via a public key bound to a forge identity. Keys are managed in the DB and rendered as forced commands: ```sh git-trace sshkey add alice --name laptop < ~/.ssh/id_ed25519.pub git-trace sshkey list git-trace sshkey rm 3 git-trace keyfile --write # regenerates <dbdir>/authorized_keys # or self-service: /account -> ssh keys (same table, regenerates live) ``` Each generated line pins identity and locks down the session: ``` command="GIT_TRACE_REPOS=/srv/repos GIT_TRACE_DB=/srv/var/git-trace.db \ /usr/bin/git-trace shell 7",no-pty,no-port-forwarding,... ssh-ed25519 AAAA... ``` `git-trace shell` parses `SSH_ORIGINAL_COMMAND`, whitelists `upload-pack`/`receive-pack`, resolves `owner/name[.git]`, enforces the same `repo_perm` ACL as HTTP, then execs the git binary directly — the login shell never touches user input. Bare `ssh git@host` prints a gitolite-style access report instead of a shell.