# gitweb Serves git repositories stored in [upspin](https://upspin.io) over HTTPS at `https://git.cosmocr.at`: a read-only browse UI **and** the git smart-HTTP protocol for `clone`/`fetch`/`push` with stock `git` (no git extensions). gitweb runs as one upspin user (`git@cosmocr.at`) and reads/writes repos on the owner's behalf. All access control lives in upspin **Access files** — gitweb never has its own permission database. The URL path is the upspin path, so a bare repo at upspin path `u@example.com/proj.git` is served at `https://git.cosmocr.at/u@example.com/proj.git`. ## Public vs private The repo's **governing Access file** (the nearest `Access` file at or above the repo directory) decides visibility: - **Public** — grants read to all (`read: all`). Anyone may browse and clone, no credentials. - **Private** — readable by `git@cosmocr.at` but not by all. Browsing and cloning require authentication; pushing requires `write`. Because gitweb reads as `git@cosmocr.at`, a private repo's Access file must grant that user read (and, for push, `create,write,delete`) in addition to the human collaborators. Example Access files (in the repo directory, e.g. `u@example.com/proj.git/Access`): ``` # public, read-only to the world; owner keeps full control read,list: all *: u@example.com ``` ``` # private; alice can push, bob can only read; the service can serve+write *: u@example.com read,list,create,write,delete: alice@example.com, git@cosmocr.at read,list: bob@example.com ``` ## Authenticating (clients) Credentials go over HTTPS Basic auth. There are two ways to get them. ### Passkey login via the credential helper (recommended) Install the helper and point git at it once: ``` # clone the repo somewhere: # $ git clone https://git.cosmocr.at/git@cosmocr.at/gitweb.git $REPO && cd $REPO $ make helper && cp bin/git-credential-cosmocr ~/bin/ # anywhere on $PATH $ git config --global credential.https://git.cosmocr.at.helper cosmocr ``` Now `git clone https://git.cosmocr.at/u@example.com/proj.git` opens your browser to `vault.cosmocr.at`, you approve with your passkey, and git receives a short-lived token automatically. On a headless machine (no browser) the helper prints a URL and code to approve from another device (device-code flow); force it with `COSMOCR_DEVICE=1`. The token is a `cosmocr.at` SSO token minted by vault and signed with vault's upspin key; gitweb verifies it against the upspin keyserver. Tokens live ~12h and are cached in `~/.config/cosmocr/tokens.json` (mode 0600). ### Personal access token (CI / automation) For non-interactive use, generate a token and store its SHA-256 hash in your own upspin tree: ``` tok=$(openssl rand -hex 32) printf '%s laptop-ci\n' "$(printf %s "$tok" | sha256sum | cut -d' ' -f1)" \ | upspin put u@example.com/gitweb/tokens # let the service read your token file printf 'read: git@cosmocr.at\n' | upspin put u@example.com/gitweb/Access echo "$tok" # use as the Basic-auth password ``` Then clone with the token as the password (username = your upspin user): ``` git clone https://u%40example.com:$tok@git.cosmocr.at/u@example.com/proj.git ``` Revoke by deleting the line from `u@example.com/gitweb/tokens` (effective within ~1 minute). ## Creating a repo gitweb serves existing bare repos; it does not create them. Bootstrap one, then push to it: ``` git init --bare proj.git upspin mkdir u@example.com/proj.git upspin cp -R proj.git/ u@example.com/proj.git/ printf '*: u@example.com\nread,list,create,write,delete: git@cosmocr.at\n' \ | upspin put u@example.com/proj.git/Access git remote add origin https://git.cosmocr.at/u@example.com/proj.git git push -u origin main ``` ## Markdown & diagrams READMEs and `.md` files are rendered (GitHub-flavored markdown, class-based syntax highlighting, sanitized with bluemonday). Fenced ` ```mermaid ` blocks are drawn client-side: the block becomes `
` and a small
same-origin script (`web/mermaid-init.js`) lazy-loads a **vendored** mermaid
bundle (`web/mermaid.min.js`, no CDN) only on pages that contain a diagram, then
renders it with mermaid's `securityLevel: "strict"`.
This is why the Content-Security-Policy allows `script-src 'self'` (our own
scripts only — no `'unsafe-inline'`, so markdown-injected inline script still
cannot run) and `style-src 'self' 'unsafe-inline'` (mermaid injects diagram
styles at render time). See `docs/ARCHITECTURE.md`, whose diagrams render when
this file is browsed through gitweb itself.
## Limitations
- `git clone --depth N` (shallow) is not supported by the underlying go-git
server; clone fully.
- `fetch` re-sends the full history (the server ignores client "have" lines);
fine for clone, wasteful for incremental fetch.
- Push is not atomic across refs; each ref updates independently.
- A repo/push is capped at 256 MiB.
## Running
- `make run-dev` — local file store (`./devdata`), no upspin/TLS, for hacking.
- Production: `-upspin_config`, `-tls_cert`, `-tls_key`, listens on `:8446`.
See `DEPLOY.md`. SSO token verification is enabled by `-sso_issuer`
(default `vault@cosmocr.at`); set it to `""` to accept only PATs. The token's
signature is checked against the issuer's upspin keyserver public key, so
gitweb never calls the issuer at runtime.
- Browser SSO ("Sign in" for private repos): additionally set `-sso_authorize_url`
(default `https://vault.cosmocr.at/authorize`) and `-base_url` (default
`https://git.cosmocr.at`). The browser is redirected there to log in with a
passkey; the issuer returns a token in the callback URL fragment, which gitweb
verifies and stores in an HttpOnly session cookie. The issuer must allow-list
`/_auth/callback` (for vault: `-sso_redirect gitweb=…/_auth/callback`).
## Layout
- `internal/store` — read/write access to upspin (or a local dir in dev).
- `internal/gitrepo` — materialize bare repos into memory, cache, push write-back.
- `internal/githttp` — git smart-HTTP wire protocol over go-git sessions.
- `internal/auth` — Access-file authorization; PAT and vault-token verification.
- `internal/server` — routing, browse UI, git endpoints, auth gating.
- `cmd/git-credential-cosmocr` — the passkey/device git credential helper.