summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--forgejno.html148
-rw-r--r--index.html99
-rw-r--r--prose.css119
-rw-r--r--style.css32
-rw-r--r--writing.html18
5 files changed, 416 insertions, 0 deletions
diff --git a/forgejno.html b/forgejno.html
new file mode 100644
index 0000000..71246e7
--- /dev/null
+++ b/forgejno.html
@@ -0,0 +1,148 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>You don't need Forgejo</title>
+
+ <link rel="stylesheet" href="style.css">
+ <link rel="stylesheet" href="prose.css">
+ <style>
+ summary {
+ font-family: 'Gelasio';
+ color: #888;
+ }
+ summary::marker {
+ color: #888;
+ }
+
+ code {
+ font-size: 1rem;
+ color: #00ff78;
+ }
+ </style>
+ <script>
+ function htmlTableOfContents() {
+ var toc = document.getElementById('toc-list');
+ var headings = document.body.querySelectorAll('h1, h2, h3, h4, h5, h6');
+ headings.forEach(function (heading, index) {
+ var link = document.createElement('a');
+ link.setAttribute('href', '#'+heading.id.toLowerCase());
+ link.textContent = heading.textContent;
+
+ var li = document.createElement('li');
+ li.appendChild(link);
+
+ toc.appendChild(li);
+ });
+ }
+ </script>
+ </head>
+ <body onload="htmlTableOfContents();">
+ <div class="toc">
+ <ul id="toc-list"></ul>
+ </div>
+
+ <div class="content">
+ <h1 id="title">You Don't Need Forgejo</h1>
+ <p><a href="https://mitchellh.com/writing/ghostty-leaving-github">People</a>
+ <a href="https://vinyl-cache.org/organization/moving.html">ditching</a>
+ GitHub after <a href="https://github.blog/news-insights/company-news/an-update-on-github-availability">recent issues</a>
+ are looking to self-hosting Forgejo as an alternative.</p>
+ <p>Despite some (poorly disclosed) <a href="https://dustri.org/b/carrot-disclosure-forgejo.html">security issues</a>, I think there's a more fundemental reason why you shouldn't use Forgejo.</p>
+ <p>If you're just a single person trying to publish your independent coding projects, why do you need: CI, Issue Tracking, Pull Requests, Multi-user account management, etc?</p>
+ <p>just use <a href="https://git.zx2c4.com/cgit">cgit</a>.</p>
+
+ <h2 id="forge">How do git forges (like GitHub) even work?</h2>
+ <p>A git forge is basically just a directory of git repositories, served over http or ssh, with a fancy web frontend.</p>
+ <p>On the server, since the repos don't need to store mutable state (local commits, staged files, etc) they're stored in a special format that's basically just the <code>.git</code>
+ directory of a normal git repository.</p>
+ <p>This is called a bare repository.</p>
+
+ <h2 id="cgit">What is cgit?</h2>
+ <p>cgit is essentially just a web-based frontend for these bare repos.</p>
+ <p>While full-on software forges (like GitHub or Forgejo) give you many extra features, cgit is just the read-only frontend, with nothing else included.</p>
+ <p>Interested?</p>
+
+ <h2 id="guide">cgit Setup Guide</h2>
+ <p>The cgit setup process is fairly involved, but if you were planning on self-hosting Forgejo, then you can handle it.</p>
+ <p class="comment">I'll be using Caddy as the webserver for this setup, but I'll link tutorials for NGINX and Apache if prefer xml configuration files and manual https certificates ;&rpar;</p>
+
+ <p>To keep everything organized, I'll run cgit under a user called "git". Our git repos will be stored in this directory.</p>
+ <p><code>$ useradd -r -d /var/lib/cgit -s /usr/bin/git-shell git</code></p>
+
+ <h3 id="cfg-fcgiwrap">fcgiwrap</h3>
+ <p>cgit is served as a <a href="https://en.wikipedia.org/wiki/Common_Gateway_Interface">cgi application</a>.</p>
+ <p>A long time ago in a galaxy far far away, web servers had builtin support for cgi, but now its often delegated to <a href="https://github.com/gnosek/fcgiwrap">fcgiwrap</a>.</p>
+ <p>So our first step will be setting up fcgiwrap.</p>
+
+ <ol>
+ <li><p>Install fcgiwrap with a package manager. This will create two systemd files: fcgiwrap.service and fcgiwrap.socket.</p></li>
+ <li><p>Open fcgiwrap.service in a text editor (<code>$ systemctl edit fcgiwrap.service</code>) and set the User and Group to be our "git" user.</p></li>
+ <li><p>Open fcgiwrap.socket (<code>$ systemctl edit fcgiwrap.socket</code>) and add "SocketUser=caddy" and "SocketGroup=caddy" under the [Socket] section. (replace with the correct Caddy user for your system)</p></li>
+ </ol>
+
+ <h3 id="cfg-cgit">cgit</h3>
+ <p>Since cgit is a cgi application, it does not run as its own service. It is simply a script that gets executed by Caddy and piped into fcgiwrap. As such, the setup is pretty simple:</p>
+ <ol>
+ <li><p>Install cgit with a package manager. This will place the cgit files at /var/www/htdocs/cgit</p></li>
+ <li>
+ <p>Make sure that location is owned by "git", but is readable by all users:</p>
+ <p><code>$ chown -R git /var/www/htdocs/cgit && chmod -R 764 /var/www/htdocs/cgit</code></p>
+ </li>
+ </ol>
+
+ <h3 id="cfg-caddy">Caddy</h3>
+ <p>To complete the set up, add this block to the Caddyfile to serve cgit.</p>
+ <pre><code>git.my.tld {
+ @assets path /cgit.css /cgit.js /favicon.svg /robots.txt
+ handle @assets {
+ root * /var/www/htdocs/cgit
+ file_server
+ }
+
+ reverse_proxy unix//run/fcgiwrap-cgit.sock {
+ transport fastcgi {
+ env SCRIPT_FILENAME /var/www/htdocs/cgit/cgit.cgi
+ }
+ }
+}</code></pre>
+
+ <h3 id="cfg-shell">Lock down SSH access</h3>
+ <p>Using <code>git-shell</code>
+ blocks normal shell access and allows only git-style SSH commands.</p>
+ <ol>
+ <li><p>Create an SSH directory for the <code>git</code>
+ user: <code>$ install -d -o git -g git -m 700 /var/lib/cgit/.ssh</code></p></li>
+ <li><p>Add your public key to <code>/var/lib/cgit/.ssh/authorized_keys</code>
+ with mode <code>600</code>.</p></li>
+ </ol>
+
+ <h3 id="cfg-repos">Create some repos</h3>
+ <p>Create a bare repository:</p>
+ <p><code>$ sudo -u git git init --bare /var/lib/cgit/example</code></p>
+ <p>Push to it from your local machine:</p>
+ <pre><code>$ git remote add origin git@git.my.tld:example
+$ git push -u origin main</code></pre>
+
+ <h3 id="cfg-cgitrc">cgitrc</h3>
+ <p>A clean default /etc/cgitrc file might look like this:</p>
+ <pre><code>css=/cgit.css
+logo=
+virtual-root=/
+
+enable-index-owner=0
+enable-http-clone=1
+scan-path=/var/lib/cgit
+
+root-title=git.my.tld
+root-desc=Personal git repositories
+snapshots=tar.gz zip</code></pre>
+
+ <h2 id="tradeoffs">Tradeoffs</h2>
+ <p>You lose integrated issues, pull requests, built-in CI, orgs, and a social graph.</p>
+ <p>In return, you get a tiny stack, fewer moving parts, lower attack surface, lower resource use, and a setup you can understand.</p>
+ <p>For my personal projects, that trade is acceptable.</p>
+ </div>
+ </body>
+</html>
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..5a7ac62
--- /dev/null
+++ b/index.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Collin Williams</title>
+
+ <link rel="stylesheet" href="style.css">
+ <style>
+ body {
+ align-items: center;
+ }
+
+ .content {
+ width: 47ch;
+ }
+
+ h1 {
+ text-align: center;
+ }
+
+ #bg {
+ position: fixed;
+ inset: 0;
+ pointer-events: none;
+ }
+
+ .star {
+ position: absolute;
+ width: 3px;
+ height: 3px;
+ border-radius: 50%;
+ background: white;
+ box-shadow: 0 0 4px white;
+ }
+
+ .cards {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-around;
+ width: 100%;
+ }
+
+ .card {
+ font-family: 'Roboto Mono';
+ color: white;
+ text-decoration: none;
+ transition: color 0.1s ease;
+ }
+
+ .card:hover {
+ color: #4DA3FF;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="bg"></div>
+ <script>
+ const bg = document.querySelector("#bg");
+ for (let i = 0; i < 100; i++) {
+ const star = document.createElement("div");
+ star.classList.add("star");
+
+ star.x = Math.random() * window.innerWidth;
+ star.y = Math.random() * window.innerHeight;
+
+ star.vx = (Math.random() - 0.5) * 0.1;
+ star.vy = (Math.random() - 0.5) * 0.1;
+
+ star.style.left = `${star.x}px`;
+ star.style.top = `${star.y}px`;
+
+ bg.appendChild(star);
+ }
+
+ function animate() {
+ for (const s of bg.children) {
+ s.x += s.vx;
+ s.y += s.vy;
+
+ s.style.left = `${s.x}px`;
+ s.style.top = `${s.y}px`;
+ }
+
+ requestAnimationFrame(animate);
+ }
+
+ animate();
+ </script>
+ <div class="content">
+ <h1>Collin Williams</h1>
+ <div class="cards">
+ <a class="card" href="https://bsky.app/profile/cwilliams1221.bsky.social">Bluesky</a>
+ <a class="card" href="https://tangled.org/cwilliams1221.bsky.social">Tangled</a>
+ <a class="card" href="./writing.html">Writing</a>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/prose.css b/prose.css
new file mode 100644
index 0000000..a9f6dc1
--- /dev/null
+++ b/prose.css
@@ -0,0 +1,119 @@
+@import url('https://fonts.googleapis.com/css2?family=Gelasio:ital,wght@0,400..700;1,400..700&display=swap');
+
+html,
+body {
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+body {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) minmax(0, 75ch) minmax(0, 1fr);
+ align-items: start;
+ column-gap: 2rem;
+ padding: 2rem 1.25rem 4rem;
+}
+
+.toc {
+ grid-column: 1;
+ width: min(17rem, 22vw);
+ position: fixed;
+ top: 32px;
+ left: max(0.75rem, calc(50vw - 37.5ch - 20rem));
+ max-height: calc(100vh - 2rem);
+ overflow-y: auto;
+ padding-right: 0.75rem;
+ z-index: 1;
+}
+
+.toc ul {
+ margin: 0;
+ padding-left: 1rem;
+}
+
+.toc li {
+ margin-bottom: 0.35rem;
+}
+
+.content {
+ grid-column: 2;
+ width: 100%;
+ margin: 0 auto;
+ padding: 0 0 2rem;
+ max-width: 75ch;
+ height: auto;
+ flex-grow: 0;
+ flex-shrink: 1;
+ overflow: visible;
+}
+
+p {
+ color: #e6e6e6;
+ font-family: 'Gelasio';
+ line-height: 1.3;
+ font-size: 1.1rem;
+}
+
+a {
+ color: #4DA3FF;
+ transition: color 0.2s ease, background-color 0.2s ease;
+}
+
+a:hover {
+ background-color: #4DA3FF;
+ color: #121212;
+ text-decoration: none;
+}
+
+li::marker {
+ color: #e6e6e6;
+}
+
+h1 {
+ color: #e6e6e6;
+ line-height: 1;
+ margin-bottom: 0;
+ font-family: 'Gelasio';
+ font-weight: 600;
+}
+
+h2 {
+ color: #e6e6e6;
+ line-height: 1;
+ margin-top: 3rem;
+ margin-bottom: 0;
+ font-family: 'Gelasio';
+ font-weight: 600;
+}
+
+h3 {
+ color: #e6e6e6;
+ line-height: 1;
+ margin-top: 2rem;
+ margin-bottom: 0;
+ font-family: 'Gelasio';
+ font-weight: 600;
+}
+
+p.comment {
+ color: #888;
+}
+
+@media (max-width: 1000px) {
+ body {
+ display: block;
+ padding: 1rem 1rem 3rem;
+ }
+
+ .toc {
+ position: static;
+ width: auto;
+ max-height: none;
+ margin-bottom: 1.25rem;
+ padding-right: 0;
+ }
+
+ .content {
+ padding: 0;
+ }
+}
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..43a6973
--- /dev/null
+++ b/style.css
@@ -0,0 +1,32 @@
+@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap');
+
+*, *::before, *::after {
+ box-sizing: border-box;
+}
+
+html, body {
+ background-color: #121212;
+ margin: 0;
+ padding: 0;
+ min-height: 100vh;
+ overflow: hidden;
+}
+
+body {
+ display: flex;
+ justify-content: center;
+ align-items: flex-start;
+}
+
+h1 {
+ font-family: 'Roboto Mono';
+ color: #e6e6e6;
+ line-height: 1;
+ margin: 0;
+ margin-bottom: 1.5rem;
+}
+
+p {
+ font-family: 'Roboto Mono';
+ color: #e6e6e6;
+}
diff --git a/writing.html b/writing.html
new file mode 100644
index 0000000..d95ba16
--- /dev/null
+++ b/writing.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Writing</title>
+
+ <link rel="stylesheet" href="style.css">
+ <link rel="stylesheet" href="prose.css">
+ </head>
+ <body>
+ <div class="content">
+ <ul>
+ <li><p><a href="./forgejno.html">You Don't Need Forgejo</a></p></li>
+ </ul>
+ </div>
+ </body>
+</html>