1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
# 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 `<pre class="mermaid">` 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
`<base_url>/_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.
|