romele / git-trace public
62 commits · 1 branch · 1 tag · 1 contributor (top: Romele) · first commit 3d ago · last push 18m ago
doc/architecture.md
# Architecture
```
browser / git client / MTA / sshd
|
v
+-------------+ +--------+
| git-trace |------->| SQLite | users, ACLs, issues, MRs,
| (one proc, | | file | patch sets, mirrors,
| threads) | +--------+ webhooks, CI runs, audit
+-------------+
| | |
fork+exec | \__ in-process, server-rendered HTML
| | (no JS framework, no templates)
v v
git http-backend bare repos repos/<owner>/<name>.git
hooks (update, filesystem-isolated, quota-checked
post-receive)
```
Everything that is not a SQL row or an HTML string is delegated to
the system `git` — transport is `git http-backend`, policy is a
per-repo `update` hook, mirroring is `git fetch`/`git push`.
## Design principles
1. **Zero dependencies** — libc, SQLite3, pthreads; `git`/`tar`/`sh`
as processes. Nothing else links, nothing else runs.
2. **GNU-style workflows** — patch sets and email review are
first-class, not an afterthought.
3. **No JavaScript** — the UI is HTML + a little inline CSS; Atom
feeds come with an XSL so browsers render them.
4. **Readable machinery** — plain C11, one concern per file, parsers
you can audit in an afternoon.
5. **Security first** — see `security.md`.
## Design decisions
- **Protocol**: we do not reimplement pkt-line negotiation. Requests are
bridged to `git http-backend` (the CGI shipped with Git) via
fork/exec with a scrubbed environment (`GIT_PROJECT_ROOT`, `PATH_INFO`,
`QUERY_STRING`, `GIT_PROTOCOL`, `REMOTE_USER`, ...). We get Smart HTTP,
dumb protocol and protocol v2 for free.
- **Concurrency**: one pthread per connection; git work happens in child
processes with `RLIMIT_CPU`/`RLIMIT_NOFILE`/`RLIMIT_NPROC` ceilings.
- **Safety**: repo components are whitelisted (`[A-Za-z0-9][A-Za-z0-9._-]*`,
`..` forbidden), resolved with `realpath()` and confined to the repos
root; CGI path tails pass a per-segment whitelist; user refs/paths are
never interpolated into shell commands (argv-only exec, leading `-`
rejected). Unauthenticated requests to protected endpoints get a
proper `401` challenge so git credential helpers work.
## Source layout
```
src/
main.c CLI (serve|mkrepo|useradd|passwd|mktoken) and routing
httpd.c HTTP/1.1 server: sockets, request parsing, thread-per-connection
cgi.c bridge to `git http-backend` (the protocol engine)
auth.c users, sessions, HTTP tokens, SSH keys, repo ACLs
crypto.c SHA-256, HMAC, PBKDF2, scrypt (RFC 7914), base64 —
implemented from scratch; password formats are versioned
(`scrypt$…` current, `pbkdf2$…` legacy) with transparent
rehash-on-login; secret buffers wiped with gt_bzero()
ssh.c forced-command shell, authorized_keys generation
gitx.c subprocess helpers + ref/path argument validation
repo.c repo name policy and canonical-path confinement
web.c server-rendered pages (tree, blob, log, commit, refs, login)
collab.c issues, comments, merge requests, repo settings, CSRF
patchset.c GNU workflow: mbox parse, series preview, index-only apply
wcommit.c browser commits: plumbing-only blob/tree/commit/CAS
mirror.c pull + push mirroring, trigger endpoint, validators
ops.c doctor / backup / restore
ratelimit.c per-IP auth throttling (exponential backoff + ban)
db.c SQLite open + schema bootstrap + column migrations
util.c buffers, url/html codecs
tests/
e2e.sh full matrix harness: spins a fresh container, asserts the
whole access/collab/security surface
fuzz.sh ASan/UBSan campaign (`make fuzz [iters]`)
fuzzdrv.c unit-level fuzz driver linked against ASan objects
fuzzrace.pl concurrent web-commit hammer (CAS verification)
fuzznet.pl socket-level HTTP request fuzzer
sql/
schema.sql relational schema (embedded at build time)
hooks/
update per-ref policy hook installed into every repo (embedded)
deploy/ production bundle: systemd units, forge sshd conf,
Caddy snippet, backup cron, one-shot install.sh
```