gitoria
All repositories: gitoria
10.3 KB
# How Gitoria uses git — researchResearch for ticket [gitoria.worldapi.org#5](https://tickets.worldapi.org/projects/gitoria.worldapi.org/5).Blocks #7 (push and pull) and feeds #9 (browse code), #11 (pulls), #12 (releases).Checked 2026-09-24 on Loreana (reworked 2026-09-25 after hybriel#42, stdin write and raw bytes in `hl:proc`, was fixed) with git 2.55.0, the vendored `bin/hybriel` and the plugin sources in`hybriel/plugins/`.## Verdict**Use the `git` binary. Do not build an `hl:git` plugin on a git library.**- Every hub feature (clone/push over HTTPS and SSH, log, diff, blame, merge, archive, gc, grep) already exists in thebinary, is stable and is what every developer's client speaks. A library re-implements a subset.- **libgit2** (installed on Loreana, 1.9) is a client/object library. It has **no server side**: no `upload-pack` /`receive-pack`, so it cannot answer `git clone` / `git push`. That was the same gap as with nodegitserver.Writing our own pack negotiation in Hybriel is a large, security-relevant project with no gain.- Split by job:1. **Reading for the web UI** (#8 #9 #10 #11 #12): Hybriel spawns `git -C <repo>.git …` with `hl:proc` (argv list,no shell). Works today, see "What hl:proc can and cannot do".2. **Transport (clone / fetch / push)**: **HTTPS is served by Hybriel itself**: the smart-HTTP endpoints spawn`git upload-pack` / `git receive-pack --stateless-rpc` with `hl:proc` (`stdin = 'pipe'`, `binary = true`) andpipe the request body in and the packfile out through `hl:http1` (byte-safe). **SSH** needs `sshd` (a Hybrielprocess cannot be an ssh server): a small sshd container whose forced command is `git-upload-pack` /`git-receive-pack`. Hybriel decides *who may*, git moves the bytes.## What was tested| Claim | Result ||---|---|| (2026-09-24, kept as proof the protocol works) `git http-backend` as CGI behind a Basic-auth check serves clone **and push** to a bare repo (`http.receivepack=true`) | works: clone, push, second clone, wrong password → `Authentifizierung fehlgeschlagen` (test wrapper in Python, 30 lines) || `hl:http1` request and response bodies are byte-safe (packfiles) | works: 3000 random bytes POSTed and echoed, `cmp` identical || `hl:proc` output is text lines only (2026-09-24) | **was lossy; fixed by hybriel#42.** Retested 2026-09-25: `run([...'cat-file','blob'…], { binary = true })` returns `ff 00 0d 0a 65 6e 64` for a file `\xff \0 \r\n end` — byte exact, `.data` is a Bytes || `hl:proc` can write to the child's stdin | **yes now** (`{ stdin = 'pipe' }`, `write()` String or Bytes, `end()`). Retested: `git upload-pack --stateless-rpc r.git` fed a `want <sha>` + `done` request answers `0008NAK` and the packfile as Bytes, exit 0 || `hl:proc` can set cwd / env | **yes now** (`cwd`, `env` options, hybriel#37) — `git -C <dir>` still fine || SSH path (sshd `AuthorizedKeysCommand`) | designed, **not run** (no sshd on Loreana for this); standard OpenSSH feature |Hybriel can now pipe a packfile through a child process, so "Hybriel itself answers `git upload-pack` / `receive-pack`"works; no CGI wrapper and no separate HTTP transport container are needed. Only SSH still needs a sshd container.Points found while retesting: a child's `stderr` also arrives as `data` events — keep only `stream == 'stdout'` for thepack; `'\n'` in a string is not a newline (hybriel#44) — write a real newline when building pkt-lines.## Architecture```developer ── https ──> nginx (*.gitoria.worldapi.org) ──> Hybriel (one app: web UI, API and git)/repo.git/info/refs?service=… auth check, then hl:procgit upload-pack|receive-pack --stateless-rpc --advertise-refs/repo.git/git-upload-pack request body ─> child stdin, child stdout ─> response/repo.git/git-receive-pack same; repos at repos/<slug>.gitdeveloper ── ssh ──> sshd (small container, own port)AuthorizedKeysCommand ─> Hybriel /__git/keys (key → user, may they?)forced command: git-upload-pack / git-receive-pack '<slug>.git' onlygit hooks in every bare repo: post-receive ─> Hybriel /__git/pushed (refs, old/new ids)Hybriel ── hl:proc ──> git -C repos/<slug>.git log|show|ls-tree|diff-tree|blame|merge-tree|… (reads)```- One volume `repos/` holds `<slug>.git` bare repositories (slug unique in the whole system, as in the concept).Hybriel and the sshd container mount it; the mpackdb store stays in `storage/mpackdb/`.- **Authorisation lives only in Hybriel** (ident login → users, repo access, keys, tokens). The sshd containerasks Hybriel over an internal address with a shared secret; it keeps no user list of its own.- Hybriel reads the Basic credentials and the `service` of the request itself and answers "read" vs "write" before itspawns git. Public repos: read without credentials, write always with.- `post-receive` is how Hybriel learns about pushes: it scans new commits for `|||PR …` and `|||RL …` (#11, #12), fillscaches and can create the tickets project. Do this in the hook, not by polling.- Not yet tried: a full clone and push through a running Hybriel HTTP endpoint (the child-process part is tested, theendpoint is built in #7). Watch large pushes: stream the request body into the child, don't hold it whole in memory.- Pass `Git-Protocol` to the child as env `GIT_PROTOCOL` (the `env` option) so clients use protocol v2.## Credentials — what git accepts today- **HTTPS with user + password/token is not deprecated by git.** Git still uses HTTP Basic through its credentialhelpers. (GitHub removed *account passwords* for its own service; that is not a git rule.) The dev CLI cannot doan interactive ident login, so Gitoria issues **personal access tokens** (per user, named, revocable, stored hashed)that go in the password field: `git clone https://slug.gitoria.worldapi.org/repo.git` → user name + token, stored by`git credential-store`/`cache`/OS keychain. HTTPS only, never plain HTTP.- **SSH keys**: the user pastes public keys in the settings page; `AuthorizedKeysCommand` looks the key up in Hybriel,so a new key works instantly and no `authorized_keys` file is edited. Supports ed25519/rsa/ecdsa; the forced commandrefuses shell and any command except the two git ones.## What hl:proc can and cannot do (for the reading side)Text output comes as lines (`run(...).lines`, `spawn` `line` events); binary output as Bytes (`binary = true`). Fine aslines, because it is text: `log`, `show --stat`, `diff`, `ls-tree`, `for-each-ref`, `rev-list`, `blame--porcelain`, `merge-tree`, `grep`, `shortlog`. Rules for the code that wraps it:- argv list only (`spawnArgs`), never a shell string with user input; slug from a whitelist `[a-z0-9-]`; refs/pathsafter `--`; refuse names starting with `-`.- Machine-readable formats with a separator that cannot occur (`--format='%H%x1f%an%x1f%at%x1f%s'`), one record perline; take bodies with a separate call. No `-z` (lines only).- **Blobs** (raw file view, images, downloads): `run([...'cat-file','blob', '<ref>:<path>'], { binary = true, cwd })`returns the exact bytes in `.data` (tested, incl. `\xff`, NUL and CRLF). No base64 detour. Text views can use thesame call and decode with `.toString()` (a located error on invalid UTF-8: check first, show "binary" instead).- **Archives** (`git archive` zip/tar.gz for releases): the same, `binary = true`, answer the Bytes as the response.- Limit each call (timeout via `kill`, cap on lines) so one huge repo cannot stall the server.## What else a hub needs (beyond clone/push/browse)Needed for a GitHub/GitLab-like experience, with the git command behind each. **MVP** = needed for #7–#12.| Feature | git mechanism | MVP ||---|---|---|| Create empty repo, default branch | `git init --bare -b <main>`; change via `git symbolic-ref HEAD` | yes || Rename / delete repo | move / remove directory | yes || Clone URLs (HTTPS, SSH) on the repo page | — | yes || Branch and tag lists, default branch | `for-each-ref` | yes || Commit list per branch / per file, paging | `log --format=… -n -skip <ref> -- <path>` | yes || Commit page with unified diff + stats | `show`, `diff-tree -p --numstat` | yes || Tree and file view, raw, README | `ls-tree`, `cat-file` | yes || Whole project at a commit / branch | `ls-tree -r`, `archive` | yes || Push hook → PR and release detection | `post-receive` | yes || Compare two refs, PR diff | `diff a...b`, `rev-list a..b` | yes (#11) || Merge a PR (fast-forward, merge commit, squash) | `merge-tree --write-tree`, `commit-tree`, `update-ref` in the bare repo, no worktree | later (merging is not decided by the creator) || Tags for releases (`|||RL`) | annotated tag via `mktag` / `tag -a` with `GIT_*` identity (`env` option) | yes (#12) || Release archive | `git archive --format=zip\|tar.gz <tag>` | later (content not decided) || Blame, file history | `blame --porcelain`, `log --follow` | nice || Code search | `git grep` on a ref | nice || Contributors / activity | `shortlog -sne`, `rev-list --count` | nice || SSH keys, access tokens, per-repo collaborators | Hybriel data + transport auth | yes (#7) || Protected branches / no force push | `update` hook asks Hybriel, or `receive.denyNonFastForwards`, `receive.denyDeletes` | later || Size limits, garbage collection | `receive.maxInputSize`, scheduled `git gc --auto` | later || Webhooks / CI triggers | fan-out from `post-receive` | later || Commit signatures ("Verified") | `%G?` in log format | later || Forks | `git clone --bare` + alternates | later || Git LFS | separate protocol | not planned |Nothing needs a working tree on the server: keep bare repos only.## Issues found (Hybriel)- hybriel#42 (stdin write, raw bytes) and #37 (cwd, env): fixed and merged; the workarounds (shell + base64 for blobs,transport container for HTTPS) are removed from this document.- hybriel#44: `'\n'` is not a newline escape — matters when writing pkt-lines.## Small choices made here- Clone address: `https://<slug>.gitoria.worldapi.org/repo.git` (path `/repo.git/…` goes to the transport, no clash with`/code`, `/commit`, …); SSH: `[email protected]:<slug>.git` on a separate port.- Repos are stored as `repos/<slug>.git`, bare only.- HTTPS git is served by Hybriel; only SSH gets a container.- Tokens, not account passwords, go in the HTTPS password field.
Branches
- mainmain branch
Latest commits
- 5602ff41gitoria: Hybriel master 190aa11d (fc838894 GC correctness, #127 mountKids by reference, #126, #48) — tracker README flat; gates 200/0, 46/0, 44/0mre
- e85eaf01gitoria: 069 round 2 — hybriel 1a096ad3 not adopted (Markdown SSR still grows); browser gate waits for the server-side logout before restartmre
- 09ce4f3fgitoria: mission 069 re-vendor hybriel 8efba065 stopped (big SSR pages grow + slow down); lambda audit clean; old vendor keptmre
- 3dc43108antcolony#40: mission references point to the moved missionsmre
- 8d9450fdantcolony#40: history (LOG.md), worker briefs (missions/) and reports moved here from antcolony, numbered per project; old numbers in antcolony docs/mission-map.mdmre
- 205d5fe4gitoria: Hybriel master ff51cf46; ssh keys/tokens no double rows (session sync); gates follow #20mre
- 9b27cb26gitoria#21: installable app (manifest, service worker, offline start page), own iconmre
- 68dcb603deploy.sh: back up live storage/.sessions/.env before every deploy (newest 5 kept)mre
- e2deed6dgitoria#20: "Add code" only on the Code page of an empty repository, no collapsiblemre
- 8bb97ffddeploy.sh: never send .git or .gitignore to Byrodinmre
- fd981932State of 2026-09-27; bin/ no longer tracked (Hybriel commit is in README)mre
- 4a2d7125initial commitmre