frierena personal git archive

frieren

a self-hosted git server in one binary — everyone reads, only the owner writes

docs: README with pixel banner

d549b6ca66a8370f2d43c1e28c789c6c7e3e4ff6

justin06lee · Aug 18, 2026, 10:11 PM (5h ago)

README.md          | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 assets/frieren.svg | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 174 insertions(+)
README.md+870
@@ -0,0 +1,87 @@
1+<div align="center">
2+
3+<img src="assets/frieren.svg" alt="frieren" width="460" />
4+
5+# frieren
6+
7+**A self-hosted git server in one binary — everyone can read, only you can write.**<br>
8+*Your code, on your machine, outliving every platform.*
9+
10+</div>
11+
12+---
13+
14+frieren is a small alternative to GitHub for people who want their repositories to live on hardware they own. It serves two audiences at once: git clients speak the smart HTTP protocol against it (`git clone`, `git fetch`, `git push`), and browsers get a read-only web UI — repository list, file trees, blobs with line numbers, commit log, diffs, branches and tags.
15+
16+The access model is deliberately tiny. Anyone who can reach the server can browse and clone everything. Pushing requires a single owner token, sent as the password over HTTP basic auth. There are no accounts, no signup, no permissions matrix — one writer, the world as readers.
17+
18+It is built from fundamentals: the Go standard library and the `git` binary, nothing else. frieren owns HTTP, authentication, and rendering; the wire protocol and object storage are delegated to git's own `upload-pack` and `receive-pack` plumbing — the same architecture real forges use.
19+
20+## Quick start
21+
22+Requires Go 1.22+ and git.
23+
24+```sh
25+make # builds and installs frieren to ~/.local/bin
26+
27+frieren token # generate an owner token, keep it secret
28+FRIEREN_TOKEN=<token> frieren serve
29+```
30+
31+The server listens on `:7420` and stores bare repositories under `./repos`. Publish a project to it:
32+
33+```sh
34+git remote add frieren http://localhost:7420/myproject.git
35+git push frieren master
36+```
37+
38+git asks for credentials: any username, the token as the password. A push to a repository that doesn't exist yet creates it — `frieren init <name> [description]` also works on the server. Then open http://localhost:7420 in a browser.
39+
40+To avoid retyping the token, let git store it once: `git config credential.helper osxkeychain` (macOS) or `git config credential.helper store` (Linux).
41+
42+## Configuration
43+
44+Flags to `frieren serve`, each with an environment fallback:
45+
46+| Flag | Env | Default | |
47+|---|---|---|---|
48+| `-addr` | `FRIEREN_ADDR` | `:7420` | listen address |
49+| `-root` | `FRIEREN_ROOT` | `./repos` | directory of bare repositories |
50+| `-token` | `FRIEREN_TOKEN` | *(unset)* | owner token; without one the server is read-only for everyone |
51+
52+Repository descriptions shown in the UI come from the standard `description` file inside each bare repository.
53+
54+## Deploying on your own machine
55+
56+Build for the target machine and copy the binary over — it's fully static:
57+
58+```sh
59+GOOS=linux GOARCH=amd64 go build -o frieren .
60+```
61+
62+`deploy/frieren.service` is a hardened systemd unit (dedicated user, read-only filesystem except the repo root, token loaded from an env file its comments show how to create). `deploy/Caddyfile` puts automatic HTTPS in front:
63+
64+```
65+git.example.com {
66+ reverse_proxy 127.0.0.1:7420
67+}
68+```
69+
70+Point DNS at the machine, run Caddy, and `https://git.example.com` is your forge. If you'd rather not expose it publicly, run it inside a Tailscale network instead — every device of yours can reach it, nobody else can.
71+
72+Since this machine becomes the source of truth, back the repo root up somewhere else on a schedule, e.g. a nightly cron:
73+
74+```sh
75+rsync -a /srv/frieren/repos/ backup-host:frieren-repos/
76+```
77+
78+## What it deliberately isn't
79+
80+No issues, no pull requests, no user accounts, no markdown rendering yet — it hosts and shows git repositories, and stops there. The single-writer model is the point: if you need collaborators with write access, you want a full forge like Forgejo.
81+
82+## Development
83+
84+```sh
85+make test # end-to-end suite: real clone/push roundtrips against a live server
86+make build # binary in dist/
87+```
assets/frieren.svg+870
@@ -0,0 +1,87 @@
1+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 660 440" role="img" aria-label="frieren">
2+ <style>
3+ @media (prefers-reduced-motion: no-preference) {
4+ .tw { animation: tw 3.6s ease-in-out infinite }
5+ .t1 { animation-delay: -0.6s }
6+ .t2 { animation-delay: -1.2s }
7+ .t3 { animation-delay: -1.8s }
8+ .t4 { animation-delay: -2.4s }
9+ .t5 { animation-delay: -3.0s }
10+ .glow { animation: tw 2.4s ease-in-out infinite }
11+ @keyframes tw {
12+ 0%, 100% { opacity: 1 }
13+ 50% { opacity: 0.3 }
14+ }
15+ }
16+ </style>
17+ <rect width="660" height="440" fill="#0C0C0F"/>
18+
19+ <!-- constellation edges: a git graph drawn in the night sky -->
20+ <g stroke="#2E3644" stroke-width="2">
21+ <line x1="305" y1="165" x2="385" y2="145"/>
22+ <line x1="385" y1="145" x2="465" y2="125"/>
23+ <line x1="465" y1="125" x2="545" y2="105"/>
24+ <line x1="385" y1="145" x2="445" y2="195"/>
25+ <line x1="445" y1="195" x2="525" y2="175"/>
26+ <line x1="525" y1="175" x2="545" y2="105"/>
27+ </g>
28+
29+ <g shape-rendering="crispEdges">
30+ <!-- commit stars: mainline in gold, a branch in frost, merging back -->
31+ <rect x="300" y="160" width="10" height="10" fill="#E8C170"/>
32+ <rect x="380" y="140" width="10" height="10" fill="#E8C170"/>
33+ <rect x="460" y="120" width="10" height="10" fill="#E8C170"/>
34+ <rect x="540" y="100" width="10" height="10" fill="#E8C170"/>
35+ <rect x="440" y="190" width="10" height="10" fill="#5EEAD4"/>
36+ <rect x="520" y="170" width="10" height="10" fill="#5EEAD4"/>
37+
38+ <!-- faint field stars -->
39+ <rect class="tw" x="230" y="40" width="4" height="4" fill="#39404E"/>
40+ <rect class="tw t1" x="280" y="90" width="4" height="4" fill="#39404E"/>
41+ <rect class="tw t2" x="350" y="60" width="4" height="4" fill="#39404E"/>
42+ <rect class="tw t3" x="420" y="40" width="4" height="4" fill="#39404E"/>
43+ <rect class="tw t4" x="500" y="50" width="4" height="4" fill="#39404E"/>
44+ <rect class="tw t5" x="580" y="60" width="4" height="4" fill="#39404E"/>
45+ <rect class="tw t2" x="610" y="140" width="4" height="4" fill="#39404E"/>
46+ <rect class="tw t4" x="260" y="140" width="4" height="4" fill="#39404E"/>
47+ <rect class="tw t1" x="60" y="70" width="4" height="4" fill="#39404E"/>
48+ <rect class="tw t3" x="120" y="130" width="4" height="4" fill="#39404E"/>
49+
50+ <!-- ground -->
51+ <rect x="0" y="380" width="660" height="60" fill="#13161C"/>
52+ <rect x="60" y="370" width="10" height="10" fill="#1C2F2B"/>
53+ <rect x="120" y="370" width="10" height="10" fill="#1C2F2B"/>
54+ <rect x="190" y="370" width="10" height="10" fill="#1C2F2B"/>
55+ <rect x="470" y="370" width="10" height="10" fill="#1C2F2B"/>
56+ <rect x="560" y="370" width="10" height="10" fill="#1C2F2B"/>
57+
58+ <!-- the mage, watching the history hold -->
59+ <!-- hair -->
60+ <rect x="120" y="250" width="30" height="10" fill="#E6E9F2"/>
61+ <rect x="110" y="260" width="50" height="10" fill="#E6E9F2"/>
62+ <rect x="100" y="270" width="70" height="10" fill="#E6E9F2"/>
63+ <rect x="100" y="280" width="20" height="10" fill="#E6E9F2"/>
64+ <rect x="150" y="280" width="20" height="10" fill="#E6E9F2"/>
65+ <rect x="100" y="290" width="20" height="10" fill="#E6E9F2"/>
66+ <rect x="150" y="290" width="20" height="10" fill="#E6E9F2"/>
67+ <rect x="100" y="300" width="10" height="50" fill="#E6E9F2"/>
68+ <rect x="160" y="300" width="10" height="50" fill="#E6E9F2"/>
69+ <!-- face -->
70+ <rect x="120" y="280" width="10" height="10" fill="#3E8E7E"/>
71+ <rect x="130" y="280" width="10" height="10" fill="#F2D8C2"/>
72+ <rect x="140" y="280" width="10" height="10" fill="#3E8E7E"/>
73+ <rect x="120" y="290" width="30" height="10" fill="#F2D8C2"/>
74+ <!-- cloak -->
75+ <rect x="110" y="300" width="50" height="30" fill="#2E4A5C"/>
76+ <rect x="110" y="330" width="10" height="10" fill="#2E4A5C"/>
77+ <rect x="120" y="330" width="30" height="10" fill="#E8C170"/>
78+ <rect x="150" y="330" width="10" height="10" fill="#2E4A5C"/>
79+ <rect x="110" y="340" width="50" height="20" fill="#2E4A5C"/>
80+ <!-- boots -->
81+ <rect x="110" y="360" width="20" height="20" fill="#191D26"/>
82+ <rect x="140" y="360" width="20" height="20" fill="#191D26"/>
83+ <!-- staff -->
84+ <rect x="180" y="240" width="10" height="140" fill="#6B4A35"/>
85+ <rect class="glow" x="180" y="220" width="10" height="20" fill="#E8C170"/>
86+ </g>
87+</svg>
088