summary refs log tree commit diff
path: root/www
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--www/causal.agency/.gitignore3
-rw-r--r--www/causal.agency/Makefile31
-rw-r--r--www/causal.agency/catgirl.pty97
-rw-r--r--www/causal.agency/index.html.in125
-rw-r--r--www/causal.agency/index.sed5
-rw-r--r--www/causal.agency/play.pty23
-rw-r--r--www/causal.agency/scheme.pty10
-rw-r--r--www/causal.agency/torus.pty774
-rw-r--r--www/git.causal.agency/.gitignore3
-rw-r--r--www/git.causal.agency/Makefile18
-rw-r--r--www/git.causal.agency/about-filter.sh15
-rw-r--r--www/git.causal.agency/cgit/.gitignore12
-rw-r--r--www/git.causal.agency/cgit/.gitmodules (renamed from .gitmodules)0
-rw-r--r--www/git.causal.agency/cgit/.mailmap (renamed from .mailmap)0
-rw-r--r--www/git.causal.agency/cgit/AUTHORS (renamed from AUTHORS)0
-rw-r--r--www/git.causal.agency/cgit/COPYING (renamed from COPYING)0
-rw-r--r--www/git.causal.agency/cgit/Makefile (renamed from Makefile)0
-rw-r--r--www/git.causal.agency/cgit/README (renamed from README)0
-rw-r--r--www/git.causal.agency/cgit/cache.c (renamed from cache.c)0
-rw-r--r--www/git.causal.agency/cgit/cache.h (renamed from cache.h)0
-rw-r--r--www/git.causal.agency/cgit/cgit.c (renamed from cgit.c)0
-rw-r--r--www/git.causal.agency/cgit/cgit.css (renamed from cgit.css)0
-rw-r--r--www/git.causal.agency/cgit/cgit.h (renamed from cgit.h)0
-rw-r--r--www/git.causal.agency/cgit/cgit.mk (renamed from cgit.mk)0
-rw-r--r--www/git.causal.agency/cgit/cgit.png (renamed from cgit.png)bin1366 -> 1366 bytes
-rw-r--r--www/git.causal.agency/cgit/cgitrc.5.txt (renamed from cgitrc.5.txt)0
-rw-r--r--www/git.causal.agency/cgit/cmd.c (renamed from cmd.c)0
-rw-r--r--www/git.causal.agency/cgit/cmd.h (renamed from cmd.h)0
-rw-r--r--www/git.causal.agency/cgit/configfile.c (renamed from configfile.c)0
-rw-r--r--www/git.causal.agency/cgit/configfile.h (renamed from configfile.h)0
-rwxr-xr-xwww/git.causal.agency/cgit/contrib/hooks/post-receive.agefile (renamed from contrib/hooks/post-receive.agefile)0
-rw-r--r--www/git.causal.agency/cgit/favicon.ico (renamed from favicon.ico)bin1078 -> 1078 bytes
-rw-r--r--www/git.causal.agency/cgit/filter.c (renamed from filter.c)0
-rwxr-xr-xwww/git.causal.agency/cgit/filters/about-formatting.sh (renamed from filters/about-formatting.sh)0
-rwxr-xr-xwww/git.causal.agency/cgit/filters/commit-links.sh (renamed from filters/commit-links.sh)0
-rw-r--r--www/git.causal.agency/cgit/filters/email-gravatar.lua (renamed from filters/email-gravatar.lua)0
-rwxr-xr-xwww/git.causal.agency/cgit/filters/email-gravatar.py (renamed from filters/email-gravatar.py)0
-rw-r--r--www/git.causal.agency/cgit/filters/email-libravatar.lua (renamed from filters/email-libravatar.lua)0
-rw-r--r--www/git.causal.agency/cgit/filters/file-authentication.lua (renamed from filters/file-authentication.lua)0
-rw-r--r--www/git.causal.agency/cgit/filters/gentoo-ldap-authentication.lua (renamed from filters/gentoo-ldap-authentication.lua)0
-rwxr-xr-xwww/git.causal.agency/cgit/filters/html-converters/man2html (renamed from filters/html-converters/man2html)0
-rwxr-xr-xwww/git.causal.agency/cgit/filters/html-converters/md2html (renamed from filters/html-converters/md2html)0
-rwxr-xr-xwww/git.causal.agency/cgit/filters/html-converters/rst2html (renamed from filters/html-converters/rst2html)0
-rwxr-xr-xwww/git.causal.agency/cgit/filters/html-converters/txt2html (renamed from filters/html-converters/txt2html)0
-rw-r--r--www/git.causal.agency/cgit/filters/owner-example.lua (renamed from filters/owner-example.lua)0
-rw-r--r--www/git.causal.agency/cgit/filters/simple-authentication.lua (renamed from filters/simple-authentication.lua)0
-rwxr-xr-xwww/git.causal.agency/cgit/filters/syntax-highlighting.py (renamed from filters/syntax-highlighting.py)0
-rwxr-xr-xwww/git.causal.agency/cgit/filters/syntax-highlighting.sh (renamed from filters/syntax-highlighting.sh)0
-rwxr-xr-xwww/git.causal.agency/cgit/gen-version.sh (renamed from gen-version.sh)0
m---------www/git.causal.agency/cgit/git (renamed from git)0
-rw-r--r--www/git.causal.agency/cgit/html.c (renamed from html.c)0
-rw-r--r--www/git.causal.agency/cgit/html.h (renamed from html.h)0
-rw-r--r--www/git.causal.agency/cgit/parsing.c (renamed from parsing.c)0
-rw-r--r--www/git.causal.agency/cgit/robots.txt (renamed from robots.txt)0
-rw-r--r--www/git.causal.agency/cgit/scan-tree.c (renamed from scan-tree.c)0
-rw-r--r--www/git.causal.agency/cgit/scan-tree.h (renamed from scan-tree.h)0
-rw-r--r--www/git.causal.agency/cgit/shared.c (renamed from shared.c)0
-rw-r--r--www/git.causal.agency/cgit/tests/.gitignore (renamed from tests/.gitignore)0
-rw-r--r--www/git.causal.agency/cgit/tests/Makefile (renamed from tests/Makefile)0
-rw-r--r--www/git.causal.agency/cgit/tests/filters/dump.lua (renamed from tests/filters/dump.lua)0
-rwxr-xr-xwww/git.causal.agency/cgit/tests/filters/dump.sh (renamed from tests/filters/dump.sh)0
-rwxr-xr-xwww/git.causal.agency/cgit/tests/setup.sh (renamed from tests/setup.sh)0
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0001-validate-git-versions.sh (renamed from tests/t0001-validate-git-versions.sh)0
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0010-validate-html.sh (renamed from tests/t0010-validate-html.sh)0
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0020-validate-cache.sh (renamed from tests/t0020-validate-cache.sh)0
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0101-index.sh (renamed from tests/t0101-index.sh)0
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0102-summary.sh (renamed from tests/t0102-summary.sh)0
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0103-log.sh (renamed from tests/t0103-log.sh)0
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0104-tree.sh (renamed from tests/t0104-tree.sh)0
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0105-commit.sh (renamed from tests/t0105-commit.sh)0
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0106-diff.sh (renamed from tests/t0106-diff.sh)0
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0107-snapshot.sh (renamed from tests/t0107-snapshot.sh)0
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0108-patch.sh (renamed from tests/t0108-patch.sh)0
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0109-gitconfig.sh (renamed from tests/t0109-gitconfig.sh)0
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0110-rawdiff.sh (renamed from tests/t0110-rawdiff.sh)0
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0111-filter.sh (renamed from tests/t0111-filter.sh)0
-rwxr-xr-xwww/git.causal.agency/cgit/tests/valgrind/bin/cgit (renamed from tests/valgrind/bin/cgit)0
-rw-r--r--www/git.causal.agency/cgit/ui-atom.c (renamed from ui-atom.c)0
-rw-r--r--www/git.causal.agency/cgit/ui-atom.h (renamed from ui-atom.h)0
-rw-r--r--www/git.causal.agency/cgit/ui-blame.c (renamed from ui-blame.c)0
-rw-r--r--www/git.causal.agency/cgit/ui-blame.h (renamed from ui-blame.h)0
-rw-r--r--www/git.causal.agency/cgit/ui-blob.c (renamed from ui-blob.c)0
-rw-r--r--www/git.causal.agency/cgit/ui-blob.h (renamed from ui-blob.h)0
-rw-r--r--www/git.causal.agency/cgit/ui-clone.c (renamed from ui-clone.c)0
-rw-r--r--www/git.causal.agency/cgit/ui-clone.h (renamed from ui-clone.h)0
-rw-r--r--www/git.causal.agency/cgit/ui-commit.c (renamed from ui-commit.c)0
-rw-r--r--www/git.causal.agency/cgit/ui-commit.h (renamed from ui-commit.h)0
-rw-r--r--www/git.causal.agency/cgit/ui-diff.c (renamed from ui-diff.c)0
-rw-r--r--www/git.causal.agency/cgit/ui-diff.h (renamed from ui-diff.h)0
-rw-r--r--www/git.causal.agency/cgit/ui-log.c (renamed from ui-log.c)0
-rw-r--r--www/git.causal.agency/cgit/ui-log.h (renamed from ui-log.h)0
-rw-r--r--www/git.causal.agency/cgit/ui-patch.c (renamed from ui-patch.c)0
-rw-r--r--www/git.causal.agency/cgit/ui-patch.h (renamed from ui-patch.h)0
-rw-r--r--www/git.causal.agency/cgit/ui-plain.c (renamed from ui-plain.c)0
-rw-r--r--www/git.causal.agency/cgit/ui-plain.h (renamed from ui-plain.h)0
-rw-r--r--www/git.causal.agency/cgit/ui-refs.c (renamed from ui-refs.c)0
-rw-r--r--www/git.causal.agency/cgit/ui-refs.h (renamed from ui-refs.h)0
-rw-r--r--www/git.causal.agency/cgit/ui-repolist.c (renamed from ui-repolist.c)0
-rw-r--r--www/git.causal.agency/cgit/ui-repolist.h (renamed from ui-repolist.h)0
-rw-r--r--www/git.causal.agency/cgit/ui-shared.c (renamed from ui-shared.c)0
-rw-r--r--www/git.causal.agency/cgit/ui-shared.h (renamed from ui-shared.h)0
-rw-r--r--www/git.causal.agency/cgit/ui-snapshot.c (renamed from ui-snapshot.c)0
-rw-r--r--www/git.causal.agency/cgit/ui-snapshot.h (renamed from ui-snapshot.h)0
-rw-r--r--www/git.causal.agency/cgit/ui-ssdiff.c (renamed from ui-ssdiff.c)0
-rw-r--r--www/git.causal.agency/cgit/ui-ssdiff.h (renamed from ui-ssdiff.h)0
-rw-r--r--www/git.causal.agency/cgit/ui-stats.c (renamed from ui-stats.c)0
-rw-r--r--www/git.causal.agency/cgit/ui-stats.h (renamed from ui-stats.h)0
-rw-r--r--www/git.causal.agency/cgit/ui-summary.c (renamed from ui-summary.c)0
-rw-r--r--www/git.causal.agency/cgit/ui-summary.h (renamed from ui-summary.h)0
-rw-r--r--www/git.causal.agency/cgit/ui-tag.c (renamed from ui-tag.c)0
-rw-r--r--www/git.causal.agency/cgit/ui-tag.h (renamed from ui-tag.h)0
-rw-r--r--www/git.causal.agency/cgit/ui-tree.c (renamed from ui-tree.c)0
-rw-r--r--www/git.causal.agency/cgit/ui-tree.h (renamed from ui-tree.h)0
-rw-r--r--www/git.causal.agency/cgitrc27
-rw-r--r--www/git.causal.agency/custom.css87
-rw-r--r--www/git.causal.agency/source-filter.sh3
-rw-r--r--www/temp.causal.agency/.gitignore1
-rw-r--r--www/temp.causal.agency/Makefile16
-rw-r--r--www/temp.causal.agency/up.c156
-rw-r--r--www/text.causal.agency/.gitignore2
-rw-r--r--www/text.causal.agency/001-make.7159
-rw-r--r--www/text.causal.agency/002-writing-mdoc.7138
-rw-r--r--www/text.causal.agency/003-pleasant-c.7120
-rw-r--r--www/text.causal.agency/004-uloc.764
-rw-r--r--www/text.causal.agency/005-testing-c.773
-rw-r--r--www/text.causal.agency/006-some-libs.796
-rw-r--r--www/text.causal.agency/007-cgit-setup.7271
-rw-r--r--www/text.causal.agency/008-how-irc.7193
-rw-r--r--www/text.causal.agency/009-casual-update.7127
-rw-r--r--www/text.causal.agency/010-irc-suite.7409
-rw-r--r--www/text.causal.agency/011-libretls.7220
-rw-r--r--www/text.causal.agency/012-inability.739
-rw-r--r--www/text.causal.agency/013-hot-tips.7156
-rw-r--r--www/text.causal.agency/Makefile31
-rw-r--r--www/text.causal.agency/feed.sh55
135 files changed, 3559 insertions, 0 deletions
diff --git a/www/causal.agency/.gitignore b/www/causal.agency/.gitignore
new file mode 100644
index 00000000..7935a3c1
--- /dev/null
+++ b/www/causal.agency/.gitignore
@@ -0,0 +1,3 @@
+*.html
+scheme.css
+scheme.png
diff --git a/www/causal.agency/Makefile b/www/causal.agency/Makefile
new file mode 100644
index 00000000..fdb1748e
--- /dev/null
+++ b/www/causal.agency/Makefile
@@ -0,0 +1,31 @@
+WEBROOT = /usr/local/www/causal.agency
+
+FILES = index.html scheme.png
+
+all: ${FILES}
+
+install: ${FILES}
+	install -C -m 644 ${FILES} ${WEBROOT}
+
+INCLUDES = scheme.css torus.html play.html catgirl.html scheme.html
+
+index.html: index.html.in index.sed ${INCLUDES}
+	sed -f index.sed index.html.in > index.html
+
+FLAGS.torus.pty = -n -h 25
+FLAGS.scheme.pty = -n -h 10
+FLAGS.play.pty = -h 16
+
+.SUFFIXES: .html .pty
+
+.pty.html:
+	shotty ${FLAGS.$<} $< > $@
+
+scheme.css:
+	scheme -s > scheme.css
+
+scheme.png:
+	scheme -g > scheme.png
+
+clean:
+	rm -f ${FILES} ${INCLUDES}
diff --git a/www/causal.agency/catgirl.pty b/www/causal.agency/catgirl.pty
new file mode 100644
index 00000000..651e83db
--- /dev/null
+++ b/www/causal.agency/catgirl.pty
@@ -0,0 +1,97 @@
+[?1049h(B[?7h[?1h=[?1004h[?2004h(B(B 0 <network> 
(Bcatgirl(B is GPLv3 fwee softwawe ^w^  code is avaiwable fwomhttps://git.causal.agency/catgirl

+Traveling...

+]2;chat.freenode.net <network>
+-adams.freenode.net- *** Looking up your hostname...

+-adams.freenode.net- *** Checking Ident

+-adams.freenode.net- *** Couldn't look up your hostname

+(B
+-adams.freenode.net- *** No Ident response

+(B
+You arrive in freenode

+
+-(B Welcome to adams.freenode.net. Thanks to ATW Internet Kft

+-(B (http://www.atw.hu) for sponsoring this server!

+-
+-(B ADAMS, DOUGLAS (1952-2001).  Author of The Hitch Hikers Guide

+-(B to the Galaxy and many other witty and humourous books,

+-(B portrayed in his uniquely British irony. He is sorely missed

+
+-(B by many millions of devoted fans. "So long and thanks for all

+-(B the books!"

+-
+-(B Welcome to freenode - supporting the free and open source

+-(B software communities since 1998.

+-
+(B
+-(B By connecting to freenode you indicate that you have read and

+-(B accept our policies and guidelines as set out on https://freenode.net

+-
+(B
+-(B In the event that you observe behaviour that contravenes our policies,

+-(B please notify a volunteer staff member via private message, or send us an

+-(B e-mail to complaints@freenode.net -- we will do our best to address the

+
+-(B situation within a reasonable period of time, and we may request further

+-(B information or, as appropriate, involve other parties such as channel

+  operators

+-(B Group Contacts representing an on-topic group.

+
+-
+-(B freenode runs an open proxy scanner.

+-
+-(B If you are looking for assistance, you may be able to find a list of

+-(B volunteer staff on '/stats p' (shows only on-call staff) or by joining

+-(B #freenode and using the '/who freenode/staff/*' command. You may message

+
+-(B any of us at any time. Please note that freenode predominantly provides

+-(B assistance via private message, and while we have a network channel the

+-(B primary venue for support requests is via private message to a member

+-(B of the volunteer staff team.

+-
+(B
+-(B From time to time, volunteer staff may send server-wide notices relating to

+-(B the project, or the communities that we host. The majority of such notices

+-(B will be sent as wallops, and you can '/mode <yournick> +w' to ensure that you

+-(B do not miss them. Important messages relating to the freenode project,

+  including

+
+-(B notices of upcoming maintenance and other scheduled downtime will be issued

+  as

+-(B global notices.

+-
+-(B Representing an on-topic project? Don't forget to register, more information

+-(B can be found on the https://freenode.net website under "Group Registration".

+-
+(B
+-(B Thank you also to our server sponsors for the sustained support in keeping

+  the

+-(B network going for close to two decades.

+-
+-(B Thank you for using freenode!

+ 1 freenode-connect (2) 
(B]2;freenode <network> (+2!) 1 freenode-connect (2) (B 2 #ascii.town 

+(B
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+catgirl(B arrives in #ascii.town(B

+The sign in #ascii.town(B reads: https://ascii.town public SSH services and IRC

+things <3 AGPL

+In #ascii.town(B are catgirl, gjabell, danopia, larbob, ep, nonlinear, epilys,

+benharri, june, yourfate, josuah

+<catgirl> (B]2;freenode #ascii.town (+2!)
/close 1(B 1(B
<catgirl> (B]2;freenode #ascii.townhello, world!
+ 

+<catgirl> (B(B(1) 
(B* june(B waves
+(B]2;freenode #ascii.town (1)]2;freenode #ascii.town
\ No newline at end of file
diff --git a/www/causal.agency/index.html.in b/www/causal.agency/index.html.in
new file mode 100644
index 00000000..82f2457e
--- /dev/null
+++ b/www/causal.agency/index.html.in
@@ -0,0 +1,125 @@
+<!DOCTYPE html>
+<title>Causal Agency</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<style>
+html {
+	font-family: monospace;
+	color: var(--ansi15);
+	background-color: var(--ansi0);
+}
+body {
+	max-width: 80ch;
+	margin: 2em auto;
+	padding: 0 1ch;
+}
+h1 {
+	font-size: inherit;
+	font-weight: inherit;
+	margin: 1em 0 0;
+}
+p {
+	margin: 0 0 1em 4ch;
+}
+a {
+	color: var(--ansi12);
+	text-decoration: none;
+}
+a:visited {
+	color: var(--ansi13);
+}
+pre {
+	border: 1px dashed var(--ansi8);
+}
+hr {
+	visibility: hidden;
+}
+/* include:scheme.css */
+</style>
+
+<p>
+Hi.
+I make mostly IRC software in C.
+I like FreeBSD and OpenBSD
+but also the GPL.
+You can find me in
+<a href="ircs://chat.freenode.net:6697/#ascii.town">#ascii.town</a>
+on freenode
+or send mail to june@.
+
+<p>
+<a href="https://git.causal.agency">code</a>
+--
+<a href="https://text.causal.agency">words</a>
+
+<h1><a href="https://git.causal.agency/pounce/about">pounce</a></h1>
+<p>
+multi-client IRC bouncer
+
+<h1><a href="https://git.causal.agency/catgirl/about">catgirl</a></h1>
+<p>
+artisanal IRC client
+<p>
+demo: <a href="ssh://chat@ascii.town">ssh chat@ascii.town</a>
+<p>
+<!-- include:catgirl.html -->
+
+<h1><a href="https://git.causal.agency/litterbox/about">litterbox</a></h1>
+<p>
+full-text search IRC logger
+
+<h1><a href="https://git.causal.agency/scooper/about">scooper</a></h1>
+<p>
+web interface for litterbox
+
+<h1><a href="https://git.causal.agency/catsit/about">catsit</a></h1>
+<p>
+process supervisor
+
+<hr>
+
+<h1><a href="https://git.causal.agency/imbox/about">imbox & git-fetch-email</a></h1>
+<p>
+IMAP to mbox
+
+<h1><a href="https://git.causal.agency/notemap/about">notemap</a></h1>
+<p>
+mirror notes to IMAP
+
+<hr>
+
+<h1><a href="https://ascii.town/explore.html">torus</a></h1>
+<p>
+collaborative ASCII art
+<p>
+<a href="ssh://torus@ascii.town">ssh torus@ascii.town</a>
+--
+<a href="https://git.causal.agency/torus">src</a>
+<p>
+<!-- include:torus.html -->
+
+<h1>play</h1>
+<p>
+2048 over SSH
+<p>
+<a href="ssh://play@ascii.town">ssh play@ascii.town</a>
+--
+<a href="https://git.causal.agency/play">src</a>
+<p>
+<!-- include:play.html -->
+
+<hr>
+
+<h1><a href="https://git.causal.agency/cards/about">cards</a></h1>
+<p>
+CARDS.DLL loader for SDL
+
+<h1><a href="bin/scheme.html">scheme</a></h1>
+<p>
+earthy terminal colours
+<p>
+<a href="scheme.png">palette</a>
+<!-- include:scheme.html -->
+
+<h1><a href="bin/">bin</a></h1>
+<p>
+other little tools
diff --git a/www/causal.agency/index.sed b/www/causal.agency/index.sed
new file mode 100644
index 00000000..ea32675f
--- /dev/null
+++ b/www/causal.agency/index.sed
@@ -0,0 +1,5 @@
+/include:scheme[.]css/r scheme.css
+/include:torus[.]html/r torus.html
+/include:play[.]html/r play.html
+/include:catgirl[.]html/r catgirl.html
+/include:scheme[.]html/r scheme.html
diff --git a/www/causal.agency/play.pty b/www/causal.agency/play.pty
new file mode 100644
index 00000000..3da44fb7
--- /dev/null
+++ b/www/causal.agency/play.pty
@@ -0,0 +1,23 @@
+[?12l[?25h[?1h=[?25l[?1h=0

+
+                              Use the arrow keys to

+     .      .      .      .   slide and merge tiles.

+                              Press q to quit.

+                              
+   .      .      .      .   
+                            
+                            
+   .      .      2      2   
+                            
+                            
+   .      .      .      .   
+                            4          2          
+                     
+   4      .      .   
+                               2                    .          
+       
+   .   
+       
+              
+   4      2   
+              
\ No newline at end of file
diff --git a/www/causal.agency/scheme.pty b/www/causal.agency/scheme.pty
new file mode 100644
index 00000000..74be2196
--- /dev/null
+++ b/www/causal.agency/scheme.pty
@@ -0,0 +1,10 @@
+                                                                                

+                                                                                

+                                                                                

+                                                                                

+                                                                                

+                                                                                

+                                                                                

+                                                                                

+                                                                                

+                                                                                
diff --git a/www/causal.agency/torus.pty b/www/causal.agency/torus.pty
new file mode 100644
index 00000000..1e147970
--- /dev/null
+++ b/www/causal.agency/torus.pty
@@ -0,0 +1,774 @@
+[?1h=[?25l┌──────────────────────────────────────────────────────────────────────────────┐│ Welcome to ascii.town!          q quit  Q teleport  m mini-map  ? help       ││                                                                              ││    k       0 1 2 3 4 5 6 7    esc navigation mode     s copy                 ││  y ↑ u                          i insert mode         p paste                ││ h ←∙→ l    ) ! @ # $ % ^ &      a insert after        ~ paint color          ││  b ↓ n                          I insert direction    * paint bright         ││    j      8 bright  9 invert    R draw mode           ( paint invert         ││               ` pipette         . line mode         C-a increment            ││ \ fast                          r replace           C-x decrement            ││                                 x erase        HJKLYUBN swap cell            ││                                                                              ││ F1 @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmno                          ││ F2  ☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼                                          ││ F3 αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■                                           ││ F4 ░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀                          ││ F5 ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»                          ││                                                                              ││                                                                              ││ This is AGPLv3 Free Software!                                                ││ Code is available from <https://code.causal.agency/june/torus>.              ││                                                                              ││                                                                              ││ Press ? to open this help again. Press any key to continue...                │└──────────────────────────────────────────────────────────────────────────────┘─[?12l[?25hPress ? for help!│   this space now frees     \ y \ CP437 ☻ ♥♦♣♠ ░▒▓█  (000,000)─────────────────┘   It's Free Real Estate     \ y \  ╒══════════════════════╕

+For a good   (0_0) | y | │ ascii.town guestbook │ 

+time, go in                   a                 | y | ╞══════════════════════╡

+any direction.     RR w         | y | │ - june         

+Pls don't publicg@conferencRRRRuRRRRR.forsale   | y | │ - scott      

+vandal. ThereRRRRRRRRRR           |   | │ - cjm        

+are huuuuuge    helo woldr  RRRRRRRR    r a     | l?| │ - erik       

+tracts of       aaaac  RRRRRR     What's up?j/| │ - swgillespie

+unused land.RRRR       poop   | a▼| │ - dikaiosune

+if u do vandal                 RR       test    | o     - danopia    

+make it funny  but maybe dont   hey  000 sdddddd|   | │ - Connor_____

+PROTIP: Press ESC to escape input modes  q,,,,,,| a   │ - tokenrove          │

+2048 clone: ssh play@ascii.town  <3     aa      | y | │ - mykey      │

+    G O O D B Y E  W I T C H E S . T Osjjj :(   | y | │ - dmrd               │ 

+vim controls, eh?.__________________. Conjor    | y | │ - hiya               │

+ . . . . . . . . |   Out of space?  | Wasj whut?| y | │ - arke 

+. Welcome to. . .|Try typing 1Q, 2Q,| Herj      | y | │ - cmr

+ . . . the TOWN. |3Q or 4Q to travel|   wjoft   | y | │ - Grissess (:D)      │

+  . . . . . . . .|to far-away lands |   ajoo    | y | │ - eternaleye (ipv6)  │

+  .. . . . . . . '------------------'    j test | y | │ - meena      

+  sad   hi friends  hello  cybre! ^Yes?  j ni   | y | │ - b0rk      boy __   │

+ nethack    yo how do i select black??? aj sxD  | y | │ - fbernier     /  '  │

+   boi    clown townjj this is as wired 0j dasdksajklj│ - mguaypaq     |     │

+  *   hot tip: don't ruin shit    *    * j *  / yq/ * │ - rkallos      V     │
+
+
+
+
+
+
+
+
+
+
+
+
(The client tries to check if you'll     j    | y |///│ - fsj         │1

+be able to see all the colours now.)     j    | y |\\\│ - Sylvhem     └──────┐//                                         j    | y |///│ - iliana <3          │\\         ] gas pedal ┐ this is just \    j    | y |\\\│ - tjk                │//  .__    [ brakes    ┘ to toggle fast nowj    | y |///│ - duckinator   \\  |\     } curly gas pedal   :)          j  elo y |\\\│ (pup@mastodon.social)│//    \    { curly brakes                  j    | y |///│ - bug(@chitter.xyz)  │\\ Looking for something interesting?      j    | y |\\\│ - peidran    // Aside from scattered pages all over,    j    | y |///│ - KitRedgrave\\ there's some development left and up froj    | y |\\\│ - vimtingu  // here.  The bulletin is over to the rightj->  | y |///│ - thomas@touhey.org  │\\ somewhere.  And there's stuff all along j    | y |\\\│ - frewsxcv    \.fr   │// the torus if you follow it around.      j    | y |///│ - NecroTechno <3     │\\                                    \    j    | y |\\\│ - er1n       │// NEW: press m to open the mini-map  _\|       | y |///│ - quantified         │\\      and see what's nearby                   | y |\\\│   @cybre.space       │//                        yoooooo yw!           | y |///│ - nightpool (cybre)  │\\    aaa\]\\y wat           hey vanta          | y |\\\│ - ghosty//    UrFU !d               this is dope       | y |///│ - lynn (@chordbug)   │\\     RTF - chempion!       thx 4 sharing      | y |\\\│ - vantablack         │// This is actually amazing.                    | y |///│ //wxyzzyrd   \\Don't forget to grab the source:             | y |\\\│ //international <3   │//                              hi RC!         | y |///│ - maren              │\\ $ git clone \                                | y |\\\│ - rose               │//   https://code.causal.agency/june/torus.git  | y |///│   (@BLASTPROCESSlNG) │/\/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| y |~~~│ - grainloom   2

+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| y |~~~│   @cybre.space~~~~~~~~~hi~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| y |~~~│   @slimelia  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| y |~~~│ - jfo~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~jh~~~~| y |~~~│   @Phairupegiont~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| y |~~~│ - chr@cybre.space <3 │~~~~~~~~~~~~ i want to marry lynn ~~~~~~~~~~~~~~| y |~~~│ - elomatreb :3       │~~~~~~~~~~~~~~~~~~~same~~~~~~~~~~~~~~~~~~~~~~~~~| y |~~~│ - minerobber~~~~~~~~~~~~~~~~~~~same tbh ~~~~~~~~~~~~~~~~   ~| y |~~~│   @tilde.town~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ I iiiiikhjk been ~| y |~~~│ - selfsame(~town)    │~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ moving around ~| y |~~~│ - Zhorken@awoo.space │~~~~~~~~~~ I am bad at VIM ~~~~~~ I didnt know ~| y |~~~│ - noiob@awoo.space   │~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ this is~vim~| y |~~~│ -               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~it's vim~| y |~~~│ - aeon~~~~~~~~~~vim is  bad hello lynn~~~~~~~~~ ~~~~~~| y |~~~│ - unascribed         │~~~~~~~~~~(lovely application, this)~~~~~~~~~~~~| y |~~~│ ~ nee (hidamari.blue)│~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| y |~~~│ ~ curiouser          │~~~~~~~~this is as much nethack               ~~| y |~~~│ - clouded  ~~~~~~~~as it is vim  ~~~~~~~~~ sry i cnt drw ~~| y |~~~│ - flacs (@f1ac5)     │~~~~~~~~~~~~~~~~~~~     ~~~~~~~               ~~| y |~~~│ - xenonnsmb (~town)  │~~~~~~~~~~~~~~~~~~~~emacs keys pls~+2~~~~~~~~~~~| y |~~~│ - Jakob              │~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| y |~~~│ - rose!              │~~~~~~~~oh no its vim~~~~~~~~~~whoops~~~~~~~~~~~| y |~~~│   (@mahoushoujorose) │~~~~~~~~help im trapped in a vim factory~~~~~~~~| y |~~~│ - jess (@dogs ~town) │~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~I messed| yq|~~~│ - rachel (@arjache)  │~~~
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  ^^ ^^^  | y |   │ - Falkreon 3

+            Roses are red.                 ^  | y |   │ - mitosis@manhater.io│

+          Violets are blue.                ^  | y |   │ - yrgfm    

+Some people don't think                    ^  | y |   │ - felix   

+                 ascii.town be like it is.    | y |   │ - ndiesslin     

+              But it do!                   ^  | y |   │ - matth             

+                                           ^  | y |   │ - timi        

+ .-.  .-. .-.  .-. .-..-. .----. .-.  .-.  ^  | y |   │ - @andwhatnot2 

+ }  \/  {  \ \/ /  | ' /  } |__}  \ \  /   ^  | y |   │ - Spocky     

+ | {  } |   `-\ }  | . \  } '__}   `-  }      | y |   │ - Kid Iccurus    

+ `-'  `-'     `-'  `-'`-` `----'      -'      | y |   │ - tom@slime.global :3

+                                              | y |   │ -                    │ 

+                                              | y |   │  lr            

+                                              | y |   │ - fennecs (helo)

+                     this poem sd             | y |   │ - @amsomniac (mastod

+                     is factually             | y |   │    on)               

+                   incorrect, but             | y |   │ - maxj     

+                   at least it rhymes         | y |   │   AdamThePhantump was

+                       i love this place      | y |   │   here. Boo! :)      └┐

+                                              | y |   │   Tsuki "Hello guys !"│

+             now I can't sleep                | y |   │ - @dejawu_           ┌┘

+                                              | y |   │ - @eal  

+                             zzzz             | y |   │   @social.sakamoto.gq

+                                              | y |   │ - frenata            

+we are addicts. we need an intervention       | yq|   │ - banjo            
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
What is this big fucking line of 'y'?        -Ethan    4

+  What is this big fucking line of 'y', you - Profpatsch  └──────┐

+  may ask yourself? It's the torus ouroboros,viv@cybre.space

+  ayying its own lmao.                        | y - dom96 ;)                             | cloin                - dbucklin i went all the way around the       - hiljusti  

+                         ros                   │ - @ercts_mxms  

+             leave some notes on the           │ -f@r4ch0

+           way for your fellow travellersr     │ ffffff               

+                                 ~chr         │ -Harper              
+     this is pretty sweet    │ -@discordalert

+           -meena                             | y |   │ - von   ShadowRZ      world wide web!            (@ShadowRZ@mastodoninternet                    .xyz

+ Your journey begins now.                 |   │ - QBFreak

+                                        y |   │ - @scanlime  +Tuco <3wow this is very exciting       | y |   │   @diode.zone        │-b0rk - zer00              │        O         |   │ - kokakoda│

+  ifdfd\|/(!)  - m3tax         
+o_O|       (!!!!!)    ~~~zgebiit~~~     

+ / \hey b0rk!! - gauntlet

+                           a                            - lastrik
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
                                     - CX    5

+ ######################################     - tomjschwanke 

+#                                    #      - @aliasless     

+ #              Mastodon              #         @awoo.wolfgirl.

+ #  #    engineering

+ # Giving social networks back to you #  - cosine  

+ # Free, decentralized microblogging  #       - stevenleeg

+#            #- steampunc   

+ #      https://joinmastodon.org      #- @blinry

+######################################   - ben@tilde.team          - Alpatron#6158
+   #################################### Calamitous  
+#             #       
+#Witches Town#   │           
+#                                  #                   
+#      Mastodon instance for       #       

+       # queer   feministanarchists #         

+       #         #                     
+#      https://witches.town        #            
+####################################       

+<<<more advertising goes over there<<<          

+   ,-._.~~~^-v^v^v**=>                     

+  /life is better on/ <3                         

+  */a.weirder.earth/*                      

+   \______________/*       love you| yq|   │          │
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    6

+  someone.. feel free to reuse this space.              └

+  it's uh... i wouldn't feel bad about                 

+  erasing whatever this is                            

+                                 

+            Tidied. The ASCII Janitor.           

+                                                         

+                                          

+      WHATS UP MY GLIB GLOBS?                   

+       W U B A L U B A D U B D U B                       *~~~~~~~~~~~~~~~~~~~~~~~*                   
+     < tilde.town <3s u      >                   
+    *~~~~~~~~~~~~~~~~~~~~~~~*         .
+ < shell in today >          
+           *~~~~~~~~~~~~~~~~*        
+                       < :3 >         i want to avoid
+                       *~~~~*       confusion for new
+                 │ people though
+ this thing is cool                  or make the guestbook

+too bad i   ssss u  u k k                  go AROUNDWAYS        │ 

+             s    u  u kk             to be continued...
+           ssss u  u kk │   │ we need to move the
+              s u  u k k     ├───┘ text below elsewhere └─┐           ssss uuuu k  k  at ascii art│ (Snek petting dude moved to│             -xenonnsmb, from ~town000,009.)  │ 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  7

+                                          

+                                           Shrine of the church 
+                                 empty lot             o o o o 
+          COME ON DOWN               o        oTO ASCII TOWN~~~ o     ..   o                             o   |----    o
+                         o   ||       o
+          uiwiidr                 o    ---     o  of                            o   |.       o                    o   ..       o                          o    \---    oI walked the               o     ,/    oourobouros             o   '     oand all I got was this   o o   o o   lousy t-shirt         o         
+_---.___,---_       | y |  ┌┘    GNU   Emacs └─┐/             \        │=========================│     /|  |y|  |\          │ https://gnu.org/s/emacs │|       |           │=========================│|       |     <- amazing shirt|  └┐                      ┌─┘|_______|         would i buy it?|   │ Emacs faithfuls  |   │                            |   │                 \|/  │       maybe           │ signqbelow:v   │
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+      │ - [X] nee     8

+ i may prefer vim, but emacs has tetris   │ - [X] byxor

+which is a stellar feature     │ - [X  @amsomniac (   

+honestly makes me like emacs  │     ]mastodon )      │                           │ - [X] tokenrove 
+l  shame it doesn't have  │ - [X] rkallos Gj  a text editor             │ - [X  nephariuz 

+      fucking heretics   SHAME!!!       │ -      evoxel   

+emacs doesn't have :smile though :)  │ - :wq!<CR>           │M         │                      │       li  │                    │ - [M-x] zge     N            │                                |   │                

+o---------o o----o        y |   │              A

+|| |    |                       │              

+|| |    |                       U|         | |    |        │                │ 

+|         | o----o      stop here  │                      │ 

+|         |         │                      │C

+|         | 000129          go down until you  │                      │ 

+|         |                reach        ok  ││ 

+|         |     100              │                      │ 

+o---------o e|   │     q                │S

+ U:%*-  *Tetris*      Top (1,0)      (Tetris Projqctile[rms])
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+      │               9

+    +/////////////////+                   │            

+    //you@cybre.space//          │                   

+    +/////////////////+                     
+/\/\/\/\/\\\/\/\/\/\/\             │                

+   /                     /    │              

+   \ pleroma.soykaf.com  \   │                

+   /     see   the       /             │              

+   \   -   entire   -    \   I miss Karl...  │                   

+   /     fediverse /   

+   \ \            

+    \/\/\/\/\///\/\/\/\/\/   │               │          .      /---------------------\  

+                    |I'm petting the snek.|   

+           |                     |  

+           |        ^U^          |  

+                    \---------------------/  

+       [X] TODO: move this too            \     

+       [ ] TODO: write todo list            @/   

+       [X] TODO: hey, me too               /|   

+                   / \  

+       ##########################  

+       # TODO: advertise things #     |   │      

+       ##########################             | yq|   │     q                │
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+││10

+                       │└──────┐

+                      ││

+            #          ││
+   ##   #       ##    ││
+    ##   #       ##     ││
+          #             ││
+         ####    ││
+                 ##                     ││
+      #   ###   ##     │     a│
+      ## ## ## ##      │(hells         │
+       ###   ### Capouet│  guettbook     │
+lol this is awesome│    s retch│                       │      goals)│                        ││                       ││                       ││                       ││
+                                    ││
+                             │  │
+todo: write todo list    ││   ▓▓
+                          ▒▒
+                          ░░
+                           
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+   (000,011)   W 

+    E 
+         E 
+ 3333333333333333 E 333333333333   3 E 333 3333   3   3 E 3333333333333333 E              E             H                   E 

+                      E           . H  E 

+   vi for the vin! E   E   E  E  E 
+                      E             E             E                            
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  2  iiit  llgwfth                                                       Acme is best editor :P 
+ is that a plan9 reference    
+ is that a Jojo's referenc  .  is that a let's encryp          
+                    d s f            s            ffd hello there            o f    l   eGenre l? d           j
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  p3  x      m        p  p  
+i want to pet a soft girl      p
+and call her by her first nameu
+god, i'm so lonely. b                         l
+You can do it i                         c
+Go out there and talk to peopleg                      @
+Even if it doesnt go anywhere, don'tc
+see it as a loss, see it as practiceo     n
+I believe in you     f     e
+You will be succesful     r     e
+You just have to try...     n              c
+UwU     e
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    .4y
+o
+u
+r
+d
+a
+                         t
+                              a
+                   .
+f
+             o
+r
+                              wheres area
+                              you   l
+                              going ethere
+                isnothing
+                     downhere
+                       i promise
+   (there is)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+      015 
+ 
+or is there? 
+wow
+  
+you all just 
+bbhello 
+       |   |           
+Rewrite this in Nim for better     
+performance and easier maintenance.
+https://nim-lang.org       
+Sorry, couldn't resist :P         Rewrite it in Idris to prove itscorrectness :P  
+Remove it :P       
+dasdasdasdsa          
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6write.
+
+jjjjjjjjjjj
+     see,What's that creepingi told you  waaaaayyy over there?-------------------->                              
+                                   
+                    
+                                                                       
+            
+q           
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7What is this ?:wqq      there istrust me        | yi wrapped                .          
+   
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+8                 

+Ancient legend tells of the treasure

+of the legendary pirate,                     

+Cap'n Jasmine Sharkbait!

+No one knows where its shiny and well shaded

+glory is hidden, and many have perished

+trying to find it.

+
+   stuff is here                     | 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(000,019)MDG - boi

+
+                                    

+                                             

+                        

+                                            why though

+======================================D

+                  

+
+                (i legitput tape                     | on my       screen
+to do this)you are crazyyou will be seen asa visionaryhello tilde.town hereqweird flex but ok
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+     020         
+please                     stop
+this
+is
+silly

+                                       itllllllllllllll nois                  lovely
+"Root? Where we're going wet
+don't need root!"people havedied tryingdon't try :(
+   don't stop!there's nice artuwujust ahead   
+|||                     vvv 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
                                              | @ | comment space!!! <3(000,021)                                              | @

+                                              | @ |  __________________ 

+                                              > @ > |                  |   

+                                              > @ > | >>> MORE ART >>> | 

+                                              > @ > | (on (001, 021))  |      

+                 #                            > @ > |___________  _____|     

+   ############# #           ##############   | @     

+                                              | @ |       ooh what you drawing

+     /     /     /       /         /          | @ |   just a little landscape

+                                  /           | @ |  nice!! keep it up ^o^

+        /                     /               | @ |   thank you =v=

+                   /              /           | @ 

+           /                /                 | @oh wait hi rose

+                                              | @ |  this is wonderful

+                     /                        | @hi alice!!

+               #### ####                      | @ |                          

+                #######                       | @ |     art restored to its

+         /       ##|##      /                 | @ |     former glory         

+               ####|####                      | @ |  cmon, play nice yall

+                ## |  ##                      | @ |             yeah!

+||||||||||||||||||||\|||||||||||||||||||||||||| @

+#####################\########################| @ |  thanks for watching

+######################\#######################| @ |  i guess it's done now   

+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^| @q|  wonderful :)  might do morer
\ No newline at end of file
diff --git a/www/git.causal.agency/.gitignore b/www/git.causal.agency/.gitignore
new file mode 100644
index 00000000..8d20f25d
--- /dev/null
+++ b/www/git.causal.agency/.gitignore
@@ -0,0 +1,3 @@
+about-filter
+hi
+source-filter
diff --git a/www/git.causal.agency/Makefile b/www/git.causal.agency/Makefile
new file mode 100644
index 00000000..28e08ba5
--- /dev/null
+++ b/www/git.causal.agency/Makefile
@@ -0,0 +1,18 @@
+ETC = /usr/local/etc
+WWW = /usr/local/www/cgit
+LIBEXEC = /usr/local/libexec
+
+BIN = ../../bin
+BINS = about-filter source-filter hi
+
+install: cgitrc custom.css ${BINS}
+	install -m 644 cgitrc ${ETC}
+	install -m 644 custom.css ${WWW}
+	install ${BINS} ${LIBEXEC}
+
+hi: ${BIN}/hi.c
+	${MAKE} -C ${BIN} $@
+	cp ${BIN}/$@ $@
+
+clean:
+	rm -f ${BINS}
diff --git a/www/git.causal.agency/about-filter.sh b/www/git.causal.agency/about-filter.sh
new file mode 100644
index 00000000..d27d2d48
--- /dev/null
+++ b/www/git.causal.agency/about-filter.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+options=fragment,man=%N.%S,includes=../tree/%I
+
+case "$1" in
+	(README.[1-9])
+		exec /usr/bin/mandoc -T html -O $options
+		;;
+	(*.[1-9])
+		exec /usr/bin/mandoc -T html -O $options,toc
+		;;
+	(*)
+		exec /usr/local/libexec/hi -l text -f html
+		;;
+esac
diff --git a/www/git.causal.agency/cgit/.gitignore b/www/git.causal.agency/cgit/.gitignore
new file mode 100644
index 00000000..661df346
--- /dev/null
+++ b/www/git.causal.agency/cgit/.gitignore
@@ -0,0 +1,12 @@
+# Files I don't care to see in git-status/commit
+/cgit
+cgit.conf
+CGIT-CFLAGS
+VERSION
+cgitrc.5
+cgitrc.5.fo
+cgitrc.5.html
+cgitrc.5.pdf
+cgitrc.5.xml
+*.o
+*.d
diff --git a/.gitmodules b/www/git.causal.agency/cgit/.gitmodules
index 5c6ecb4f..5c6ecb4f 100644
--- a/.gitmodules
+++ b/www/git.causal.agency/cgit/.gitmodules
diff --git a/.mailmap b/www/git.causal.agency/cgit/.mailmap
index 03b54796..03b54796 100644
--- a/.mailmap
+++ b/www/git.causal.agency/cgit/.mailmap
diff --git a/AUTHORS b/www/git.causal.agency/cgit/AUTHORS
index 031de338..031de338 100644
--- a/AUTHORS
+++ b/www/git.causal.agency/cgit/AUTHORS
diff --git a/COPYING b/www/git.causal.agency/cgit/COPYING
index d159169d..d159169d 100644
--- a/COPYING
+++ b/www/git.causal.agency/cgit/COPYING
diff --git a/Makefile b/www/git.causal.agency/cgit/Makefile
index 49109adc..49109adc 100644
--- a/Makefile
+++ b/www/git.causal.agency/cgit/Makefile
diff --git a/README b/www/git.causal.agency/cgit/README
index 7a6b4a40..7a6b4a40 100644
--- a/README
+++ b/www/git.causal.agency/cgit/README
diff --git a/cache.c b/www/git.causal.agency/cgit/cache.c
index 2c70be78..2c70be78 100644
--- a/cache.c
+++ b/www/git.causal.agency/cgit/cache.c
diff --git a/cache.h b/www/git.causal.agency/cgit/cache.h
index 470da4fc..470da4fc 100644
--- a/cache.h
+++ b/www/git.causal.agency/cgit/cache.h
diff --git a/cgit.c b/www/git.causal.agency/cgit/cgit.c
index c4320f04..c4320f04 100644
--- a/cgit.c
+++ b/www/git.causal.agency/cgit/cgit.c
diff --git a/cgit.css b/www/git.causal.agency/cgit/cgit.css
index d4aadbfa..d4aadbfa 100644
--- a/cgit.css
+++ b/www/git.causal.agency/cgit/cgit.css
diff --git a/cgit.h b/www/git.causal.agency/cgit/cgit.h
index 7ec46b48..7ec46b48 100644
--- a/cgit.h
+++ b/www/git.causal.agency/cgit/cgit.h
diff --git a/cgit.mk b/www/git.causal.agency/cgit/cgit.mk
index 3fcc1ca3..3fcc1ca3 100644
--- a/cgit.mk
+++ b/www/git.causal.agency/cgit/cgit.mk
diff --git a/cgit.png b/www/git.causal.agency/cgit/cgit.png
index 425528ee..425528ee 100644
--- a/cgit.png
+++ b/www/git.causal.agency/cgit/cgit.png
Binary files differdiff --git a/cgitrc.5.txt b/www/git.causal.agency/cgit/cgitrc.5.txt
index 33a6a8c0..33a6a8c0 100644
--- a/cgitrc.5.txt
+++ b/www/git.causal.agency/cgit/cgitrc.5.txt
diff --git a/cmd.c b/www/git.causal.agency/cgit/cmd.c
index bf6d8f51..bf6d8f51 100644
--- a/cmd.c
+++ b/www/git.causal.agency/cgit/cmd.c
diff --git a/cmd.h b/www/git.causal.agency/cgit/cmd.h
index 6249b1d8..6249b1d8 100644
--- a/cmd.h
+++ b/www/git.causal.agency/cgit/cmd.h
diff --git a/configfile.c b/www/git.causal.agency/cgit/configfile.c
index e0391091..e0391091 100644
--- a/configfile.c
+++ b/www/git.causal.agency/cgit/configfile.c
diff --git a/configfile.h b/www/git.causal.agency/cgit/configfile.h
index af7ca197..af7ca197 100644
--- a/configfile.h
+++ b/www/git.causal.agency/cgit/configfile.h
diff --git a/contrib/hooks/post-receive.agefile b/www/git.causal.agency/cgit/contrib/hooks/post-receive.agefile
index 2f72ae9c..2f72ae9c 100755
--- a/contrib/hooks/post-receive.agefile
+++ b/www/git.causal.agency/cgit/contrib/hooks/post-receive.agefile
diff --git a/favicon.ico b/www/git.causal.agency/cgit/favicon.ico
index 56ff5938..56ff5938 100644
--- a/favicon.ico
+++ b/www/git.causal.agency/cgit/favicon.ico
Binary files differdiff --git a/filter.c b/www/git.causal.agency/cgit/filter.c
index 70f5b749..70f5b749 100644
--- a/filter.c
+++ b/www/git.causal.agency/cgit/filter.c
diff --git a/filters/about-formatting.sh b/www/git.causal.agency/cgit/filters/about-formatting.sh
index 85daf9c2..85daf9c2 100755
--- a/filters/about-formatting.sh
+++ b/www/git.causal.agency/cgit/filters/about-formatting.sh
diff --git a/filters/commit-links.sh b/www/git.causal.agency/cgit/filters/commit-links.sh
index 58819524..58819524 100755
--- a/filters/commit-links.sh
+++ b/www/git.causal.agency/cgit/filters/commit-links.sh
diff --git a/filters/email-gravatar.lua b/www/git.causal.agency/cgit/filters/email-gravatar.lua
index c39b490d..c39b490d 100644
--- a/filters/email-gravatar.lua
+++ b/www/git.causal.agency/cgit/filters/email-gravatar.lua
diff --git a/filters/email-gravatar.py b/www/git.causal.agency/cgit/filters/email-gravatar.py
index d70440ea..d70440ea 100755
--- a/filters/email-gravatar.py
+++ b/www/git.causal.agency/cgit/filters/email-gravatar.py
diff --git a/filters/email-libravatar.lua b/www/git.causal.agency/cgit/filters/email-libravatar.lua
index 7336baf8..7336baf8 100644
--- a/filters/email-libravatar.lua
+++ b/www/git.causal.agency/cgit/filters/email-libravatar.lua
diff --git a/filters/file-authentication.lua b/www/git.causal.agency/cgit/filters/file-authentication.lua
index 02488046..02488046 100644
--- a/filters/file-authentication.lua
+++ b/www/git.causal.agency/cgit/filters/file-authentication.lua
diff --git a/filters/gentoo-ldap-authentication.lua b/www/git.causal.agency/cgit/filters/gentoo-ldap-authentication.lua
index 673c88d1..673c88d1 100644
--- a/filters/gentoo-ldap-authentication.lua
+++ b/www/git.causal.agency/cgit/filters/gentoo-ldap-authentication.lua
diff --git a/filters/html-converters/man2html b/www/git.causal.agency/cgit/filters/html-converters/man2html
index 0ef78841..0ef78841 100755
--- a/filters/html-converters/man2html
+++ b/www/git.causal.agency/cgit/filters/html-converters/man2html
diff --git a/filters/html-converters/md2html b/www/git.causal.agency/cgit/filters/html-converters/md2html
index dc20f42a..dc20f42a 100755
--- a/filters/html-converters/md2html
+++ b/www/git.causal.agency/cgit/filters/html-converters/md2html
diff --git a/filters/html-converters/rst2html b/www/git.causal.agency/cgit/filters/html-converters/rst2html
index 02d90f81..02d90f81 100755
--- a/filters/html-converters/rst2html
+++ b/www/git.causal.agency/cgit/filters/html-converters/rst2html
diff --git a/filters/html-converters/txt2html b/www/git.causal.agency/cgit/filters/html-converters/txt2html
index 495eeceb..495eeceb 100755
--- a/filters/html-converters/txt2html
+++ b/www/git.causal.agency/cgit/filters/html-converters/txt2html
diff --git a/filters/owner-example.lua b/www/git.causal.agency/cgit/filters/owner-example.lua
index 50fc25a8..50fc25a8 100644
--- a/filters/owner-example.lua
+++ b/www/git.causal.agency/cgit/filters/owner-example.lua
diff --git a/filters/simple-authentication.lua b/www/git.causal.agency/cgit/filters/simple-authentication.lua
index 23d34576..23d34576 100644
--- a/filters/simple-authentication.lua
+++ b/www/git.causal.agency/cgit/filters/simple-authentication.lua
diff --git a/filters/syntax-highlighting.py b/www/git.causal.agency/cgit/filters/syntax-highlighting.py
index e912594c..e912594c 100755
--- a/filters/syntax-highlighting.py
+++ b/www/git.causal.agency/cgit/filters/syntax-highlighting.py
diff --git a/filters/syntax-highlighting.sh b/www/git.causal.agency/cgit/filters/syntax-highlighting.sh
index 840bc34f..840bc34f 100755
--- a/filters/syntax-highlighting.sh
+++ b/www/git.causal.agency/cgit/filters/syntax-highlighting.sh
diff --git a/gen-version.sh b/www/git.causal.agency/cgit/gen-version.sh
index 80cf49af..80cf49af 100755
--- a/gen-version.sh
+++ b/www/git.causal.agency/cgit/gen-version.sh
diff --git a/git b/www/git.causal.agency/cgit/git
-Subproject c522f061d551c9bb8684a7c3859b2ece4499b56
+Subproject c522f061d551c9bb8684a7c3859b2ece4499b56
diff --git a/html.c b/www/git.causal.agency/cgit/html.c
index 7f81965f..7f81965f 100644
--- a/html.c
+++ b/www/git.causal.agency/cgit/html.c
diff --git a/html.h b/www/git.causal.agency/cgit/html.h
index fa4de775..fa4de775 100644
--- a/html.h
+++ b/www/git.causal.agency/cgit/html.h
diff --git a/parsing.c b/www/git.causal.agency/cgit/parsing.c
index 93b4767e..93b4767e 100644
--- a/parsing.c
+++ b/www/git.causal.agency/cgit/parsing.c
diff --git a/robots.txt b/www/git.causal.agency/cgit/robots.txt
index 4ce948fe..4ce948fe 100644
--- a/robots.txt
+++ b/www/git.causal.agency/cgit/robots.txt
diff --git a/scan-tree.c b/www/git.causal.agency/cgit/scan-tree.c
index 6a2f65a8..6a2f65a8 100644
--- a/scan-tree.c
+++ b/www/git.causal.agency/cgit/scan-tree.c
diff --git a/scan-tree.h b/www/git.causal.agency/cgit/scan-tree.h
index 1afbd4bb..1afbd4bb 100644
--- a/scan-tree.h
+++ b/www/git.causal.agency/cgit/scan-tree.h
diff --git a/shared.c b/www/git.causal.agency/cgit/shared.c
index 8115469a..8115469a 100644
--- a/shared.c
+++ b/www/git.causal.agency/cgit/shared.c
diff --git a/tests/.gitignore b/www/git.causal.agency/cgit/tests/.gitignore
index 3fd2e965..3fd2e965 100644
--- a/tests/.gitignore
+++ b/www/git.causal.agency/cgit/tests/.gitignore
diff --git a/tests/Makefile b/www/git.causal.agency/cgit/tests/Makefile
index 65e11173..65e11173 100644
--- a/tests/Makefile
+++ b/www/git.causal.agency/cgit/tests/Makefile
diff --git a/tests/filters/dump.lua b/www/git.causal.agency/cgit/tests/filters/dump.lua
index 1f15c931..1f15c931 100644
--- a/tests/filters/dump.lua
+++ b/www/git.causal.agency/cgit/tests/filters/dump.lua
diff --git a/tests/filters/dump.sh b/www/git.causal.agency/cgit/tests/filters/dump.sh
index da6f7a1b..da6f7a1b 100755
--- a/tests/filters/dump.sh
+++ b/www/git.causal.agency/cgit/tests/filters/dump.sh
diff --git a/tests/setup.sh b/www/git.causal.agency/cgit/tests/setup.sh
index 5879348e..5879348e 100755
--- a/tests/setup.sh
+++ b/www/git.causal.agency/cgit/tests/setup.sh
diff --git a/tests/t0001-validate-git-versions.sh b/www/git.causal.agency/cgit/tests/t0001-validate-git-versions.sh
index 73bd32f5..73bd32f5 100755
--- a/tests/t0001-validate-git-versions.sh
+++ b/www/git.causal.agency/cgit/tests/t0001-validate-git-versions.sh
diff --git a/tests/t0010-validate-html.sh b/www/git.causal.agency/cgit/tests/t0010-validate-html.sh
index ca08d69d..ca08d69d 100755
--- a/tests/t0010-validate-html.sh
+++ b/www/git.causal.agency/cgit/tests/t0010-validate-html.sh
diff --git a/tests/t0020-validate-cache.sh b/www/git.causal.agency/cgit/tests/t0020-validate-cache.sh
index 657765d8..657765d8 100755
--- a/tests/t0020-validate-cache.sh
+++ b/www/git.causal.agency/cgit/tests/t0020-validate-cache.sh
diff --git a/tests/t0101-index.sh b/www/git.causal.agency/cgit/tests/t0101-index.sh
index 82ef9b04..82ef9b04 100755
--- a/tests/t0101-index.sh
+++ b/www/git.causal.agency/cgit/tests/t0101-index.sh
diff --git a/tests/t0102-summary.sh b/www/git.causal.agency/cgit/tests/t0102-summary.sh
index b8864cb1..b8864cb1 100755
--- a/tests/t0102-summary.sh
+++ b/www/git.causal.agency/cgit/tests/t0102-summary.sh
diff --git a/tests/t0103-log.sh b/www/git.causal.agency/cgit/tests/t0103-log.sh
index bdf1435a..bdf1435a 100755
--- a/tests/t0103-log.sh
+++ b/www/git.causal.agency/cgit/tests/t0103-log.sh
diff --git a/tests/t0104-tree.sh b/www/git.causal.agency/cgit/tests/t0104-tree.sh
index 2e140f59..2e140f59 100755
--- a/tests/t0104-tree.sh
+++ b/www/git.causal.agency/cgit/tests/t0104-tree.sh
diff --git a/tests/t0105-commit.sh b/www/git.causal.agency/cgit/tests/t0105-commit.sh
index 9cdf55c0..9cdf55c0 100755
--- a/tests/t0105-commit.sh
+++ b/www/git.causal.agency/cgit/tests/t0105-commit.sh
diff --git a/tests/t0106-diff.sh b/www/git.causal.agency/cgit/tests/t0106-diff.sh
index 82b645ec..82b645ec 100755
--- a/tests/t0106-diff.sh
+++ b/www/git.causal.agency/cgit/tests/t0106-diff.sh
diff --git a/tests/t0107-snapshot.sh b/www/git.causal.agency/cgit/tests/t0107-snapshot.sh
index c164d3e2..c164d3e2 100755
--- a/tests/t0107-snapshot.sh
+++ b/www/git.causal.agency/cgit/tests/t0107-snapshot.sh
diff --git a/tests/t0108-patch.sh b/www/git.causal.agency/cgit/tests/t0108-patch.sh
index 013d6802..013d6802 100755
--- a/tests/t0108-patch.sh
+++ b/www/git.causal.agency/cgit/tests/t0108-patch.sh
diff --git a/tests/t0109-gitconfig.sh b/www/git.causal.agency/cgit/tests/t0109-gitconfig.sh
index 8cee75cd..8cee75cd 100755
--- a/tests/t0109-gitconfig.sh
+++ b/www/git.causal.agency/cgit/tests/t0109-gitconfig.sh
diff --git a/tests/t0110-rawdiff.sh b/www/git.causal.agency/cgit/tests/t0110-rawdiff.sh
index 66fa7d5d..66fa7d5d 100755
--- a/tests/t0110-rawdiff.sh
+++ b/www/git.causal.agency/cgit/tests/t0110-rawdiff.sh
diff --git a/tests/t0111-filter.sh b/www/git.causal.agency/cgit/tests/t0111-filter.sh
index 2fdc3669..2fdc3669 100755
--- a/tests/t0111-filter.sh
+++ b/www/git.causal.agency/cgit/tests/t0111-filter.sh
diff --git a/tests/valgrind/bin/cgit b/www/git.causal.agency/cgit/tests/valgrind/bin/cgit
index dcdfbe53..dcdfbe53 100755
--- a/tests/valgrind/bin/cgit
+++ b/www/git.causal.agency/cgit/tests/valgrind/bin/cgit
diff --git a/ui-atom.c b/www/git.causal.agency/cgit/ui-atom.c
index 1056f363..1056f363 100644
--- a/ui-atom.c
+++ b/www/git.causal.agency/cgit/ui-atom.c
diff --git a/ui-atom.h b/www/git.causal.agency/cgit/ui-atom.h
index dda953bb..dda953bb 100644
--- a/ui-atom.h
+++ b/www/git.causal.agency/cgit/ui-atom.h
diff --git a/ui-blame.c b/www/git.causal.agency/cgit/ui-blame.c
index f28eea0c..f28eea0c 100644
--- a/ui-blame.c
+++ b/www/git.causal.agency/cgit/ui-blame.c
diff --git a/ui-blame.h b/www/git.causal.agency/cgit/ui-blame.h
index 5b97e035..5b97e035 100644
--- a/ui-blame.h
+++ b/www/git.causal.agency/cgit/ui-blame.h
diff --git a/ui-blob.c b/www/git.causal.agency/cgit/ui-blob.c
index f76c641e..f76c641e 100644
--- a/ui-blob.c
+++ b/www/git.causal.agency/cgit/ui-blob.c
diff --git a/ui-blob.h b/www/git.causal.agency/cgit/ui-blob.h
index 16847b20..16847b20 100644
--- a/ui-blob.h
+++ b/www/git.causal.agency/cgit/ui-blob.h
diff --git a/ui-clone.c b/www/git.causal.agency/cgit/ui-clone.c
index 5dccb639..5dccb639 100644
--- a/ui-clone.c
+++ b/www/git.causal.agency/cgit/ui-clone.c
diff --git a/ui-clone.h b/www/git.causal.agency/cgit/ui-clone.h
index 3e460a3d..3e460a3d 100644
--- a/ui-clone.h
+++ b/www/git.causal.agency/cgit/ui-clone.h
diff --git a/ui-commit.c b/www/git.causal.agency/cgit/ui-commit.c
index 783211ff..783211ff 100644
--- a/ui-commit.c
+++ b/www/git.causal.agency/cgit/ui-commit.c
diff --git a/ui-commit.h b/www/git.causal.agency/cgit/ui-commit.h
index 8198b4ba..8198b4ba 100644
--- a/ui-commit.h
+++ b/www/git.causal.agency/cgit/ui-commit.h
diff --git a/ui-diff.c b/www/git.causal.agency/cgit/ui-diff.c
index 329c3506..329c3506 100644
--- a/ui-diff.c
+++ b/www/git.causal.agency/cgit/ui-diff.c
diff --git a/ui-diff.h b/www/git.causal.agency/cgit/ui-diff.h
index 39264a16..39264a16 100644
--- a/ui-diff.h
+++ b/www/git.causal.agency/cgit/ui-diff.h
diff --git a/ui-log.c b/www/git.causal.agency/cgit/ui-log.c
index 2939c016..2939c016 100644
--- a/ui-log.c
+++ b/www/git.causal.agency/cgit/ui-log.c
diff --git a/ui-log.h b/www/git.causal.agency/cgit/ui-log.h
index 325607cd..325607cd 100644
--- a/ui-log.h
+++ b/www/git.causal.agency/cgit/ui-log.h
diff --git a/ui-patch.c b/www/git.causal.agency/cgit/ui-patch.c
index 5a964108..5a964108 100644
--- a/ui-patch.c
+++ b/www/git.causal.agency/cgit/ui-patch.c
diff --git a/ui-patch.h b/www/git.causal.agency/cgit/ui-patch.h
index 7a6cacd5..7a6cacd5 100644
--- a/ui-patch.h
+++ b/www/git.causal.agency/cgit/ui-patch.h
diff --git a/ui-plain.c b/www/git.causal.agency/cgit/ui-plain.c
index 2a7b18cc..2a7b18cc 100644
--- a/ui-plain.c
+++ b/www/git.causal.agency/cgit/ui-plain.c
diff --git a/ui-plain.h b/www/git.causal.agency/cgit/ui-plain.h
index 5bff07b8..5bff07b8 100644
--- a/ui-plain.h
+++ b/www/git.causal.agency/cgit/ui-plain.h
diff --git a/ui-refs.c b/www/git.causal.agency/cgit/ui-refs.c
index 456f610d..456f610d 100644
--- a/ui-refs.c
+++ b/www/git.causal.agency/cgit/ui-refs.c
diff --git a/ui-refs.h b/www/git.causal.agency/cgit/ui-refs.h
index 1d4a54a2..1d4a54a2 100644
--- a/ui-refs.h
+++ b/www/git.causal.agency/cgit/ui-refs.h
diff --git a/ui-repolist.c b/www/git.causal.agency/cgit/ui-repolist.c
index 529a2038..529a2038 100644
--- a/ui-repolist.c
+++ b/www/git.causal.agency/cgit/ui-repolist.c
diff --git a/ui-repolist.h b/www/git.causal.agency/cgit/ui-repolist.h
index 1b6b3227..1b6b3227 100644
--- a/ui-repolist.h
+++ b/www/git.causal.agency/cgit/ui-repolist.h
diff --git a/ui-shared.c b/www/git.causal.agency/cgit/ui-shared.c
index d2358f29..d2358f29 100644
--- a/ui-shared.c
+++ b/www/git.causal.agency/cgit/ui-shared.c
diff --git a/ui-shared.h b/www/git.causal.agency/cgit/ui-shared.h
index 6964873a..6964873a 100644
--- a/ui-shared.h
+++ b/www/git.causal.agency/cgit/ui-shared.h
diff --git a/ui-snapshot.c b/www/git.causal.agency/cgit/ui-snapshot.c
index 556d3ed4..556d3ed4 100644
--- a/ui-snapshot.c
+++ b/www/git.causal.agency/cgit/ui-snapshot.c
diff --git a/ui-snapshot.h b/www/git.causal.agency/cgit/ui-snapshot.h
index a8deec36..a8deec36 100644
--- a/ui-snapshot.h
+++ b/www/git.causal.agency/cgit/ui-snapshot.h
diff --git a/ui-ssdiff.c b/www/git.causal.agency/cgit/ui-ssdiff.c
index af8bc9e0..af8bc9e0 100644
--- a/ui-ssdiff.c
+++ b/www/git.causal.agency/cgit/ui-ssdiff.c
diff --git a/ui-ssdiff.h b/www/git.causal.agency/cgit/ui-ssdiff.h
index 11f27144..11f27144 100644
--- a/ui-ssdiff.h
+++ b/www/git.causal.agency/cgit/ui-ssdiff.h
diff --git a/ui-stats.c b/www/git.causal.agency/cgit/ui-stats.c
index 7272a61a..7272a61a 100644
--- a/ui-stats.c
+++ b/www/git.causal.agency/cgit/ui-stats.c
diff --git a/ui-stats.h b/www/git.causal.agency/cgit/ui-stats.h
index 0e61b03d..0e61b03d 100644
--- a/ui-stats.h
+++ b/www/git.causal.agency/cgit/ui-stats.h
diff --git a/ui-summary.c b/www/git.causal.agency/cgit/ui-summary.c
index 947812a8..947812a8 100644
--- a/ui-summary.c
+++ b/www/git.causal.agency/cgit/ui-summary.c
diff --git a/ui-summary.h b/www/git.causal.agency/cgit/ui-summary.h
index cba696af..cba696af 100644
--- a/ui-summary.h
+++ b/www/git.causal.agency/cgit/ui-summary.h
diff --git a/ui-tag.c b/www/git.causal.agency/cgit/ui-tag.c
index 846d5b14..846d5b14 100644
--- a/ui-tag.c
+++ b/www/git.causal.agency/cgit/ui-tag.c
diff --git a/ui-tag.h b/www/git.causal.agency/cgit/ui-tag.h
index d295cdcd..d295cdcd 100644
--- a/ui-tag.h
+++ b/www/git.causal.agency/cgit/ui-tag.h
diff --git a/ui-tree.c b/www/git.causal.agency/cgit/ui-tree.c
index 1e4efb25..1e4efb25 100644
--- a/ui-tree.c
+++ b/www/git.causal.agency/cgit/ui-tree.c
diff --git a/ui-tree.h b/www/git.causal.agency/cgit/ui-tree.h
index bbd34e35..bbd34e35 100644
--- a/ui-tree.h
+++ b/www/git.causal.agency/cgit/ui-tree.h
diff --git a/www/git.causal.agency/cgitrc b/www/git.causal.agency/cgitrc
new file mode 100644
index 00000000..c187e1ee
--- /dev/null
+++ b/www/git.causal.agency/cgitrc
@@ -0,0 +1,27 @@
+root-title=causal agency
+root-desc=“then I'm sorry, no offence, but you write toy programs.”
+
+clone-url=https://$HTTP_HOST/$CGIT_REPO_URL
+snapshots=tar.gz zip
+
+enable-blame=1
+enable-commit-graph=1
+enable-subject-links=1
+enable-follow-links=1
+enable-index-owner=0
+repository-sort=age
+branch-sort=age
+
+css=/custom.css
+email-filter=/usr/local/libexec/cgit-email
+about-filter=/usr/local/libexec/about-filter
+source-filter=/usr/local/libexec/source-filter
+
+readme=:README.7
+readme=:README
+
+remove-suffix=1
+enable-git-config=1
+scan-path=/home/june/pub
+
+cache-size=1024
diff --git a/www/git.causal.agency/custom.css b/www/git.causal.agency/custom.css
new file mode 100644
index 00000000..802882d2
--- /dev/null
+++ b/www/git.causal.agency/custom.css
@@ -0,0 +1,87 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+@import url("cgit.css");
+
+* { line-height: 1.25em; }
+
+div#cgit {
+	max-width: 117ch;
+	margin: auto;
+	font-family: monospace;
+	-moz-tab-size: 4;
+	tab-size: 4;
+}
+
+div#cgit table#header td.logo {
+	display: none;
+}
+div#cgit table#header td.sub {
+	border-top: none;
+}
+div#cgit table.tabs {
+	border-bottom: none;
+}
+div#cgit div.content {
+	border-bottom: none;
+}
+div#cgit table.list th a {
+	color: inherit;
+}
+div#cgit table.list tr:nth-child(even) {
+	background: inherit;
+}
+div#cgit table.list tr:hover {
+	background: inherit;
+}
+div#cgit table.list tr.nohover-highlight:hover:nth-child(even) {
+	background: inherit;
+}
+
+div#cgit table.blob td.linenumbers a:target {
+	color: goldenrod;
+	text-decoration: underline;
+	outline: none;
+}
+
+div#cgit div#summary {
+	max-width: 80ch;
+}
+
+/* from hi(1) */
+div#cgit .hi.Keyword { color: dimgray; }
+div#cgit .hi.Macro { color: green; }
+div#cgit .hi.Tag { color: inherit; text-decoration: underline; }
+div#cgit .hi.String { color: teal; }
+div#cgit .hi.Format { color: teal; font-weight: bold; }
+div#cgit .hi.Interp { color: olive; }
+div#cgit .hi.Comment { color: navy; }
+div#cgit .hi.Todo { color: navy; font-weight: bold; }
+div#cgit .hi.DiffOld { color: red; }
+div#cgit .hi.DiffNew { color: green; }
+div#cgit .hi.Tag:target { color: goldenrod; outline: none; }
+
+/* from mandoc(1) */
+table.head, table.foot { width: 100%; }
+td.head-rtitle, td.foot-os { text-align: right; }
+td.head-vol { text-align: center; }
+div.Pp { margin: 1ex 0ex; }
+div.Nd, div.Bf, div.Op { display: inline; }
+span.Pa, span.Ad { font-style: italic; }
+span.Ms { font-weight: bold; }
+dl.Bl-diag > dt { font-weight: bold; }
+code.Nm, code.Fl, code.Cm, code.Ic, code.In, code.Fd, code.Fn,
+code.Cd { font-weight: bold; font-family: inherit; }
+
+h1.Sh { font-size: 1.5em; }
+table.Nm td:first-child { padding-right: 1ch; }
+code.Fl { white-space: nowrap; }
+span.RsT { font-style: italic; }
+dl.Bl-tag:not(.Bl-compact) dt { margin-top: 1em; }
+ul.Bl-bullet:not(.Bl-compact) li { margin-top: 1em; }
+div.Bd-indent { margin-left: 4ch; }
+table.Bl-column { width: 100%; }
+table.foot { margin-top: 1em; }
+
+div#cgit a.permalink { color: inherit; }
diff --git a/www/git.causal.agency/source-filter.sh b/www/git.causal.agency/source-filter.sh
new file mode 100644
index 00000000..4febc2e0
--- /dev/null
+++ b/www/git.causal.agency/source-filter.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+exec /usr/local/libexec/hi -t -n "$1" -f html -o anchor
diff --git a/www/temp.causal.agency/.gitignore b/www/temp.causal.agency/.gitignore
new file mode 100644
index 00000000..e31ee94e
--- /dev/null
+++ b/www/temp.causal.agency/.gitignore
@@ -0,0 +1 @@
+up
diff --git a/www/temp.causal.agency/Makefile b/www/temp.causal.agency/Makefile
new file mode 100644
index 00000000..3e908305
--- /dev/null
+++ b/www/temp.causal.agency/Makefile
@@ -0,0 +1,16 @@
+WEBROOT = /usr/local/www/temp.causal.agency
+
+CFLAGS += -std=c11 -Wall -Wextra -Wpedantic -I/usr/local/include
+LDFLAGS += -static -L/usr/local/lib
+LDLIBS = -lkcgihtml -lkcgi -lz -lmd
+
+up:
+
+clean:
+	rm -f up
+
+install: up
+	install -m 700 up ${WEBROOT}/up
+
+uninstall:
+	rm -f ${WEBROOT}/up
diff --git a/www/temp.causal.agency/up.c b/www/temp.causal.agency/up.c
new file mode 100644
index 00000000..9e7b4ff7
--- /dev/null
+++ b/www/temp.causal.agency/up.c
@@ -0,0 +1,156 @@
+/* Copyright (C) 2020  June McEnroe <june@causal.agency>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capsicum.h>
+#include <sys/types.h>
+#include <sysexits.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <kcgi.h>
+#include <kcgihtml.h>
+
+static int cwd = -1;
+
+static const struct kvalid Key = { NULL, "file" };
+
+static enum kcgi_err head(struct kreq *req, enum khttp http, enum kmime mime) {
+	return khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[http])
+		|| khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[mime]);
+}
+
+static enum kcgi_err fail(struct kreq *req, enum khttp http) {
+	return head(req, http, KMIME_TEXT_PLAIN)
+		|| khttp_body(req)
+		|| khttp_printf(req, "%s\n", khttps[http]);
+}
+
+static enum kcgi_err handle(struct kreq *req) {
+	if (req->page) return fail(req, KHTTP_404);
+
+	if (req->method == KMETHOD_GET) {
+		struct khtmlreq html;
+		struct khtmlreq *h = &html;
+		return head(req, KHTTP_200, KMIME_TEXT_HTML)
+			|| khttp_body(req)
+			|| khtml_open(h, req, 0)
+			|| khtml_elem(h, KELEM_DOCTYPE)
+			|| khtml_elem(h, KELEM_TITLE)
+			|| khtml_puts(h, "Upload")
+			|| khtml_closeelem(h, 1)
+			|| khtml_attr(
+				h, KELEM_FORM,
+				KATTR_METHOD, "post",
+				KATTR_ACTION, "",
+				KATTR_ENCTYPE, "multipart/form-data",
+				KATTR__MAX
+			)
+			|| khtml_attr(
+				h, KELEM_INPUT,
+				KATTR_TYPE, "file",
+				KATTR_NAME, Key.name,
+				KATTR__MAX
+			)
+			|| khtml_attr(
+				h, KELEM_INPUT,
+				KATTR_TYPE, "submit",
+				KATTR_VALUE, "Upload",
+				KATTR__MAX
+			)
+			|| khtml_close(h);
+
+	} else if (req->method == KMETHOD_POST) {
+		struct kpair *field = req->fieldmap[0];
+		if (!field || !field->valsz) return fail(req, KHTTP_400);
+
+		char name[256];
+		const char *ext = strrchr(field->file, '.');
+		if (!ext) ext = "";
+		snprintf(
+			name, sizeof(name), "%jx%08x%s",
+			(intmax_t)time(NULL), arc4random(), ext
+		);
+
+		int fd = openat(cwd, name, O_CREAT | O_EXCL | O_WRONLY, 0644);
+		if (fd < 0) {
+			warn("openat");
+			return fail(req, KHTTP_507);
+		}
+		ssize_t len = write(fd, field->val, field->valsz);
+		int error = close(fd);
+		if (len < 0 || error) {
+			warn("write");
+			return fail(req, KHTTP_507);
+		}
+
+		return head(req, KHTTP_303, KMIME_TEXT_PLAIN)
+			|| khttp_head(req, kresps[KRESP_LOCATION], "/%s", name)
+			|| khttp_body(req)
+			|| khttp_puts(req, name);
+
+	} else {
+		return fail(req, KHTTP_405);
+	}
+}
+
+static void sandbox(void) {
+	cwd = open(".", O_DIRECTORY);
+	if (cwd < 0) err(EX_CONFIG, ".");
+
+	int error = cap_enter();
+	if (error) err(EX_OSERR, "cap_enter");
+
+	cap_rights_t rights;
+	cap_rights_init(&rights, CAP_LOOKUP, CAP_CREATE, CAP_PWRITE);
+	error = cap_rights_limit(cwd, &rights);
+	if (error) err(EX_OSERR, "cap_rights_limit");
+}
+
+int main(void) {
+	const char *page = "up";
+	if (khttp_fcgi_test()) {
+		struct kfcgi *fcgi;
+		enum kcgi_err error = khttp_fcgi_init(&fcgi, &Key, 1, &page, 1, 0);
+		if (error) errx(EX_CONFIG, "khttp_fcgi_init: %s", kcgi_strerror(error));
+		sandbox();
+		for (
+			struct kreq req;
+			KCGI_OK == (error = khttp_fcgi_parse(fcgi, &req));
+			khttp_free(&req)
+		) {
+			error = handle(&req);
+			if (error && error != KCGI_HUP) break;
+		}
+		if (error != KCGI_EXIT) {
+			errx(EX_PROTOCOL, "khttp_fcgi_parse: %s", kcgi_strerror(error));
+		}
+		khttp_fcgi_free(fcgi);
+	} else {
+		struct kreq req;
+		enum kcgi_err error = khttp_parse(&req, &Key, 1, &page, 1, 0);
+		if (error) errx(EX_PROTOCOL, "khttp_parse: %s", kcgi_strerror(error));
+		error = handle(&req);
+		if (error) errx(EX_PROTOCOL, "%s", kcgi_strerror(error));
+		khttp_free(&req);
+	}
+}
diff --git a/www/text.causal.agency/.gitignore b/www/text.causal.agency/.gitignore
new file mode 100644
index 00000000..37dd51ef
--- /dev/null
+++ b/www/text.causal.agency/.gitignore
@@ -0,0 +1,2 @@
+*.txt
+feed.atom
diff --git a/www/text.causal.agency/001-make.7 b/www/text.causal.agency/001-make.7
new file mode 100644
index 00000000..b4805729
--- /dev/null
+++ b/www/text.causal.agency/001-make.7
@@ -0,0 +1,159 @@
+.Dd September 17, 2018
+.Dt MAKE 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Using Make
+.Nd writing less Makefile
+.
+.Sh DESCRIPTION
+Let's talk about
+.Xr make 1 .
+I think an important thing to know about
+.Xr make 1
+is that you don't need to write a
+.Pa Makefile
+to use it.
+There are default rules
+for C, C++ and probably Fortran.
+To build
+.Pa foo
+from
+.Pa foo.c ,
+just run:
+.
+.Pp
+.Dl make foo
+.
+.Pp
+The default rule for C files uses the
+.Ev CFLAGS
+variable,
+so you can set that in the environment
+to pass flags to the C compiler:
+.
+.Pp
+.Dl CFLAGS=-Wall make foo
+.
+.Pp
+It also uses
+.Ev LDLIBS
+for linking,
+so you can add libraries with:
+.
+.Pp
+.Dl LDLIBS=-lcurses make foo
+.
+.Pp
+Obviously writing this every time
+would become tedious,
+so it might be time to write a
+.Pa Makefile .
+But it really doesn't need much:
+.
+.Bd -literal -offset indent
+CFLAGS += -Wall -Wextra
+LDLIBS = -lcurses
+
+foo:
+.Ed
+.
+.Pp
+Assigning
+.Ev CFLAGS
+with
+.Ql +=
+preserves the system default
+or anything passed in the environment.
+Declaring
+.Pa foo
+as the first rule
+makes it the default when
+.Ql make
+is run without a target.
+Note that the rule doesn't need a definition;
+the default will still be used.
+.
+.Pp
+If
+.Pa foo
+is built from serveral source files,
+unfortunately a rule definition is required:
+.
+.Bd -literal -offset indent
+OBJS = foo.o bar.o baz.o
+
+foo: $(OBJS)
+	$(CC) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@
+.Ed
+.
+.Pp
+This rule uses
+.Ev LDFLAGS
+for passing linker flags,
+which is what the default rule does.
+The
+.Ql $@
+variable here expands to
+.Ql foo ,
+so this rule can be copied easily
+for other binary targets.
+.
+.Pp
+If some sources depend on a header file,
+they can be automatically rebuilt
+when the header changes
+by declaring a dependency rule:
+.
+.Pp
+.Dl foo.o bar.o: foo.h
+.
+.Pp
+Note that several files can appear
+either side of the
+.Ql ":" .
+.
+.Pp
+Lastly,
+it's always nice to add a
+.Cm clean
+target:
+.
+.Bd -literal -offset indent
+clean:
+	rm -f $(OBJS) foo
+.Ed
+.
+.Pp
+I hope this helps getting started with
+.Xr make 1
+without writing too much
+.Pa Makefile !
+.
+.Sh EXAMPLES
+The example
+.Pa Makefile
+in its entirety:
+.
+.Bd -literal -offset indent
+CFLAGS += -Wall -Wextra
+LDLIBS = -lcurses
+OBJS = foo.o bar.o baz.o
+
+foo: $(OBJS)
+	$(CC) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@
+
+foo.o bar.o: foo.h
+
+clean:
+	rm -f $(OBJS) foo
+.Ed
+.
+.Sh AUTHORS
+.An Mt june@causal.agency
+.
+.Pp
+This document is produced from
+.Xr mdoc 7
+source available from
+.Lk https://git.causal.agency/src/tree/www/text.causal.agency
diff --git a/www/text.causal.agency/002-writing-mdoc.7 b/www/text.causal.agency/002-writing-mdoc.7
new file mode 100644
index 00000000..b377d364
--- /dev/null
+++ b/www/text.causal.agency/002-writing-mdoc.7
@@ -0,0 +1,138 @@
+.Dd September 27, 2018
+.Dt WRITING-MDOC 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Writing mdoc
+.Nd semantic markup
+.
+.Sh DESCRIPTION
+I recently learned how to write man pages
+so that I could document
+a bunch of little programs I've written.
+Modern man pages are written in
+.Xr mdoc 7 ,
+whose documentation is also available from
+.Lk http://mandoc.bsd.lv .
+.
+.Pp
+.Xr mdoc 7
+differs from many other markup languages
+by providing
+.Dq semantic markup
+rather than just
+.Dq physical markup.
+What this means is that
+the markup indicates what something is,
+not how to format it.
+For example,
+the
+.Ql \&Ar
+macro is used to indicate
+command-line arguments
+rather than one of the macros
+for bold, italic or underline.
+This frees each author of having to choose
+and enables consistent presentation
+across different man pages.
+.
+.Pp
+Another advantage of semantic markup
+is that information can be extracted from it.
+For example,
+.Xr makewhatis 8
+can easily extract the name and short description
+from each man page
+thanks to the
+.Ql \&Nm
+and
+.Ql \&Nd
+macros.
+I use the same information
+to generate an Atom feed for these documents,
+though in admittedly a much less robust way than
+.Xr mandoc 1 .
+.
+.Pp
+When it comes to actually writing
+.Xr mdoc 7 ,
+it can take some getting used to.
+The language is of
+.Xr roff 7
+lineage
+so its syntax is very particular.
+Macros cannot appear inline,
+but must start on new lines
+beginning with
+.Ql \&. .
+Sentences should likewise
+always start on a new line.
+Since I'm in the habit of writing with
+semantic line breaks,
+I actually find these requirements
+fit in well.
+.
+.Pp
+The more frustrating syntax limitation to me
+is the rule against empty lines.
+Without them,
+it can be quite difficult to edit a lengthy document.
+Thankfully,
+lines with only a
+.Ql \&.
+on them are allowed,
+but this still causes visual noise.
+To alleviate that,
+I have a
+.Xr vim 1
+syntax file for
+.Xr mdoc 7
+which conceals the lone dots:
+.
+.Bd -literal -offset indent
+if exists("b:current_syntax")
+	finish
+endif
+
+runtime! syntax/nroff.vim
+unlet! b:current_syntax
+
+setlocal sections+=ShSs
+syntax match mdocBlank /^\\.$/ conceal
+setlocal conceallevel=2
+
+let b:current_syntax = "mdoc"
+.Ed
+.
+.Pp
+It also adds the
+.Xr mdoc 7
+section header and subsection header macros to the
+.Cm sections
+option to make
+.Xr vim 1 Ap s
+.Ic {
+and
+.Ic }
+motions
+aware of them.
+.
+.Pp
+With that,
+I've found writing man pages pleasant and rewarding.
+I've started writing other documents with
+.Xr mdoc 7
+as well,
+as you can see here.
+.
+.Sh SEE ALSO
+.Lk http://rhodesmill.org/brandon/2012/one-sentence-per-line/ "Semantic Linefeeds"
+.
+.Sh AUTHORS
+.An Mt june@causal.agency
+.
+.Pp
+This document is produced from
+.Xr mdoc 7
+source available from
+.Lk https://git.causal.agency/src/tree/www/text.causal.agency
diff --git a/www/text.causal.agency/003-pleasant-c.7 b/www/text.causal.agency/003-pleasant-c.7
new file mode 100644
index 00000000..16030b7e
--- /dev/null
+++ b/www/text.causal.agency/003-pleasant-c.7
@@ -0,0 +1,120 @@
+.Dd September 30, 2018
+.Dt PLEASANT-C 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Pleasant C
+.Nd it's good, actually
+.
+.Sh DESCRIPTION
+I've been writing a lot of C lately
+and actually find it very pleasant.
+I want to talk about some of its ergonomic features.
+These are C99 features unless otherwise noted.
+.
+.Ss Initializer syntax
+Struct and union initializer syntax
+is well generalized.
+Designators can be chained,
+making initializing nested structs easy,
+and all uninitialized fields are zeroed.
+.
+.Bd -literal -offset indent
+struct {
+	struct pollfd fds[2];
+} loop = {
+	.fds[0].fd = STDIN_FILENO,
+	.fds[1].fd = STDOUT_FILENO,
+	.fds[0].events = POLLIN,
+	.fds[1].events = POLLOUT,
+};
+.Ed
+.
+.Ss Variable-length arrays
+VLAs can be multi-dimensional,
+which can avoid manual stride multiplications
+needed to index a flat
+.Xr malloc 3 Ap d
+array.
+.
+.Bd -literal -offset indent
+uint8_t glyphs[len][height][width];
+fread(glyphs, height * width, len, stdin);
+.Ed
+.
+.Ss Incomplete array types
+The last field of a struct can be an
+.Dq incomplete
+array type,
+which means it doesn't have a length.
+A variable amount of space for the struct can be
+.Xr malloc 3 Ap d ,
+or the struct can be used as
+a sort of pointer with fields.
+.
+.Bd -literal -offset indent
+struct Line {
+	enum Filter type;
+	uint8_t data[];
+} *line = &png.data[1 + lineSize()];
+.Ed
+.
+.Ss Anonymous struct and union fields (C11)
+Members of structs or unions
+which are themselves structs or unions
+can be unnamed.
+In that case,
+each of the inner fields
+is treated as a member of the outer struct or union.
+This makes working with tagged unions nicer.
+.
+.Bd -literal -offset indent
+struct Message {
+	enum { Foo, Bar } type;
+	union {
+		uint8_t foo;
+		uint32_t bar;
+	};
+} msg = { .type = Foo, .foo = 0xFF };
+.Ed
+.
+.Ss Static assert (C11)
+Assertions can be made at compile time.
+Most useful for checking sizes of structs.
+.
+.Bd -literal -offset indent
+static_assert(13 == sizeof(struct PNGHeader), "PNG IHDR size");
+.Ed
+.
+.Ss Leading-break switch
+This one is just an odd style choice
+I came across that C happens to allow.
+To prevent accidental fall-through
+in switch statements,
+you can put breaks before the case labels.
+.
+.Bd -literal -offset indent
+while (0 < (opt = getopt(argc, argv, "h:w:"))) {
+	switch (opt) {
+		break; case 'h': height = optarg;
+		break; case 'w': width = optarg;
+		break; default:  return EX_USAGE;
+	}
+}
+.Ed
+.
+.Sh AUTHORS
+.An Mt june@causal.agency
+.
+.Pp
+This document is produced from
+.Xr mdoc 7
+source available from
+.Lk https://git.causal.agency/src/tree/www/text.causal.agency
+.
+.Sh CAVEATS
+This isn't meant to be advice.
+It's just how I like to write C,
+and I don't
+.Dq ship
+software in C.
diff --git a/www/text.causal.agency/004-uloc.7 b/www/text.causal.agency/004-uloc.7
new file mode 100644
index 00000000..edd78d80
--- /dev/null
+++ b/www/text.causal.agency/004-uloc.7
@@ -0,0 +1,64 @@
+.Dd December 14, 2018
+.Dt ULOC 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm ULOC
+.Nd unique lines of code
+.
+.Sh DESCRIPTION
+There are many tools available
+which measure SLOC: source lines of code.
+These tools are strangely complex
+for what they intend to do,
+which is to estimate the relative sizes of projects.
+They perform some amount of parsing
+in order to discount comments in various languages,
+and for reasons unknown each format their ouput
+in some oddly encumbered way.
+.
+.Pp
+I propose a much simpler method
+of estimating relative sizes of projects:
+unique lines of code.
+ULOC can be calculated with standard tools as follows:
+.
+.Bd -literal -offset indent
+sort -u *.h *.c | wc -l
+.Ed
+.
+.Pp
+In my opinion,
+the number this produces
+should be a better estimate of
+the complexity of a project.
+Compared to SLOC,
+not only are blank lines discounted,
+but so are close-brace lines
+and other repetitive code
+such as common includes.
+On the other hand,
+ULOC counts comments,
+which require just as much maintenance
+as the code around them does,
+while avoiding inflating the result
+with license headers which appear in every file,
+for example.
+.
+.Pp
+It can also be amusing
+to read all of your code sorted alphabetically.
+.
+.Sh AUTHORS
+.An Mt june@causal.agency
+.
+.Pp
+This document is produced from
+.Xr mdoc 7
+source available from
+.Lk https://git.causal.agency/src/tree/www/text.causal.agency
+.
+.Sh CAVEATS
+Estimates such as these
+should not be used for decision making
+as if they were data.
diff --git a/www/text.causal.agency/005-testing-c.7 b/www/text.causal.agency/005-testing-c.7
new file mode 100644
index 00000000..d0c636ff
--- /dev/null
+++ b/www/text.causal.agency/005-testing-c.7
@@ -0,0 +1,73 @@
+.Dd December 21, 2018
+.Dt TESTING-C 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Testing C
+.Nd a simple unit testing setup
+.
+.Sh DESCRIPTION
+This is a simple approach
+to unit testing in C
+that I've used in a couple projects.
+At the bottom of a C file
+with some code I want to test,
+I add:
+.
+.Bd -literal -offset indent
+#ifdef TEST
+#include <assert.h>
+
+int main(void) {
+	assert(...);
+	assert(...);
+}
+
+#endif
+.Ed
+.
+.Pp
+This file normally produces a
+.Pa .o
+to be linked into the main binary.
+For testing,
+I produce separate binaries
+and run them with
+.Xr make 1 :
+.
+.Bd -literal -offset indent
+TESTS = foo.t bar.t
+
+\&.SUFFIXES: .t
+
+\&.c.t:
+	$(CC) $(CFLAGS) -DTEST $(LDFLAGS) $< $(LDLIBS) -o $@
+
+test: $(TESTS)
+	set -e; $(TESTS:%=./%;)
+.Ed
+.
+.Pp
+Note that the test binaries
+aren't linked with the rest of the code,
+so there is potential for simple stubbing or mocking.
+.
+.Pp
+To get the best output
+from C's simple
+.Xr assert 3 ,
+it's best to assert the result
+of a helper function
+which takes the expected output
+and the test input,
+rather than calling
+.Xr assert 3
+inside the helper function.
+This way,
+the message printed by the assert failure
+contains a useful line number
+and the expected output
+rather than just variable names.
+.
+.Sh AUTHORS
+.An Mt june@causal.agency
diff --git a/www/text.causal.agency/006-some-libs.7 b/www/text.causal.agency/006-some-libs.7
new file mode 100644
index 00000000..5af65404
--- /dev/null
+++ b/www/text.causal.agency/006-some-libs.7
@@ -0,0 +1,96 @@
+.Dd December 11, 2019
+.Dt SOME-LIBS 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Some Libraries
+.Nd good ones
+.
+.Sh DESCRIPTION
+This is a little list of C libraries
+I've had good experiences using.
+.
+.Bl -tag -width Ds
+.It Fl lcurl
+The library behind the
+.Xr curl 1
+command.
+It downloads or uploads things on the internet
+through a number of protocols,
+not just HTTP.
+It has an easy-to-use library API,
+appropriately named
+.Xr libcurl-easy 3 .
+I've used it to implement a
+.Lk https://causal.agency/bin/title.html "page title fetcher" .
+.
+.It Fl lcurses
+Okay so this one really isn't great.
+Its interfaces can seem archaic
+and its documentation is often poor.
+However, it gets the job done
+and is commonly available pretty much everywhere.
+Interesting to note that
+.Nx
+uses its own implementation of curses
+that is not GNU ncurses,
+unlike
+.Fx .
+.
+.It Fl ledit
+This is a BSD line editing library,
+similar to GNU readline.
+It supports right-aligned prompts,
+which I prefer for variable-length
+information in shells.
+.
+.It Fl lkcgi
+A CGI and FastCGI library
+for web applications in C.
+Don't worry,
+it isolates HTTP parsing and input validation
+from application logic
+in sandboxed processes.
+I think it's an excellent example
+of how to design an API for C.
+I used it to implement the
+.Lk https://ascii.town/explore.html "torus web viewer" .
+.
+.It Fl lsqlite3
+An embedded relational database engine.
+It's amazing what you can do with this,
+and it's super easy to use!
+My one gripe with it is that the library and SQL documentation
+are not available as
+.Xr man 1
+pages.
+I'm currently working on a project using SQLite,
+but it hasn't gotten very far yet.
+.
+.It Fl ltls
+This is a new library in LibreSSL
+which provides a much simpler interface for TLS sockets
+compared to
+.Fl lssl .
+It's much more like what you'd expect
+from other TLS socket wrappers,
+with calls like
+.Xr tls_connect 3 ,
+.Xr tls_read 3
+and
+.Xr tls_write 3 .
+I've used this for IRC clients, bouncers and bots.
+.
+.It Fl lz
+An implementation of the DEFLATE compression algorithm
+and gzip format.
+It's all documented in comments in
+.In zlib.h ,
+which isn't bad,
+but for my own use I copied the docs into
+.Lk https://code.causal.agency/june/zlib-man-pages "manual pages" .
+I've used this for decoding and encoding PNG images.
+.El
+.
+.Sh AUTHORS
+.An June Bug Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/007-cgit-setup.7 b/www/text.causal.agency/007-cgit-setup.7
new file mode 100644
index 00000000..44fb436a
--- /dev/null
+++ b/www/text.causal.agency/007-cgit-setup.7
@@ -0,0 +1,271 @@
+.Dd December 15, 2019
+.Dt CGIT-SETUP 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm cgit setup
+.Nd configuration notes
+.
+.Sh DESCRIPTION
+I just set up cgit on
+.Lk https://git.causal.agency
+to replace an instance of gitea.
+After 30 days of uptime,
+gitea had accumulated over 11 hours of CPU time
+and was using hundreds of megabytes of memory.
+cgit is much more lightweight
+and much more in line with my aesthetic.
+I'm documenting how I set it up here
+mostly to remind myself in the future.
+.
+.Ss slowcgi
+cgit is CGI software,
+but
+.Xr nginx 8
+only supports FastCGI.
+I used
+.Xr slowcgi 8
+as a compatibility layer
+by adding the following to
+.Pa /etc/rc.conf :
+.Bd -literal -offset indent
+slowcgi_enable="YES"
+slowcgi_flags="-p / -s /var/run/slowcgi.sock"
+.Ed
+.
+.Ss nginx
+I added the following in a new
+.Cm server
+block to
+.Pa /usr/local/etc/nginx/nginx.conf :
+.Bd -literal -offset indent
+root /usr/local/www/cgit;
+location / {
+	try_files $uri @cgit;
+}
+location @cgit {
+	fastcgi_pass unix:/var/run/slowcgi.sock;
+	fastcgi_param SCRIPT_FILENAME $document_root/cgit.cgi;
+	fastcgi_param SCRIPT_NAME /;
+	fastcgi_param PATH_INFO $uri;
+	fastcgi_param QUERY_STRING $query_string;
+	fastcgi_param REQUEST_METHOD $request_method;
+	fastcgi_param CONTENT_TYPE $content_type;
+	fastcgi_param CONTENT_LENGTH $content_length;
+	fastcgi_param HTTPS $https if_not_empty;
+	fastcgi_param SERVER_PORT $server_port;
+	fastcgi_param SERVER_NAME $server_name;
+}
+.Ed
+.
+.Pp
+The
+.Cm try_files
+directive causes
+.Xr nginx 8
+to first try to serve static files from
+.Pa /usr/local/www/cgit
+before passing anything else on to FastCGI.
+.
+.Pp
+The
+.Va SCRIPT_FILENAME
+parameter tells
+.Xr slowcgi 8
+the path of the CGI binary to run.
+Setting
+.Va SCRIPT_NAME
+to
+.Pa /
+tells cgit its root URL
+and avoids it using query strings for everything.
+.
+.Ss cgit
+cgit doesn't provide any configuration to start from,
+so you have to just read
+.Xr cgitrc 5 .
+I added the following to
+.Pa /usr/local/etc/cgitrc :
+.Bd -literal -offset indent
+cache-size=1024
+clone-url=https://$HTTP_HOST/$CGIT_REPO_URL
+snapshots=tar.gz zip
+remove-suffix=1
+enable-git-config=1
+scan-path=/home/june/pub
+.Ed
+.
+.Pp
+The
+.Cm cache-size
+option enables caching,
+which by default is stored in
+.Pa /var/cache/cgit ,
+so I made sure that directory exists
+and is writable by the
+.Sy www
+user.
+The
+.Cm clone-url
+option sets the clone URL to advertise.
+cgit will automatically serve git over HTTP.
+The
+.Cm snapshots
+option makes tarballs available for tags and commits.
+.
+.Pp
+The
+.Cm scan-path
+option causes cgit to scan the given path
+for git repositories.
+I'm putting mine in
+.Pa ~/pub .
+The
+.Cm remove-suffix
+option causes cgit to remove the
+.Pa .git
+suffix from the URLs it uses
+for the repositories it finds,
+so that
+.Pa ~/pub/pounce.git
+is served at
+.Pa /pounce .
+The
+.Cm enable-git-config
+option allows controlling some cgit options
+from the
+.Xr git-config 1
+of each repository.
+See
+.Sx git
+below.
+.
+.Pp
+I also set up a filter to render
+.Xr mdoc 7
+files
+and do syntax highlighting
+by adding the following to
+.Pa cgitrc :
+.Bd -literal -offset indent
+readme=:README.7
+readme=:README
+about-filter=/usr/local/libexec/cgit-filter
+source-filter=/usr/local/libexec/cgit-filter
+.Ed
+.
+.Pp
+The
+.Cm readme
+options tell cgit which files to look for
+to render the
+.Dq about
+page.
+The colon prefix causes it to look for them
+in the git tree.
+The
+.Pa /usr/local/libexec/cgit-filter
+script contains the following:
+.Bd -literal -offset indent
+#!/bin/sh
+case "$1" in
+	(*.[1-9])
+		/usr/bin/mandoc -T utf8 | /usr/local/libexec/ttpre
+		;;
+	(*)
+		exec /usr/local/libexec/hi -t -n "$1" -f html -o anchor
+		;;
+esac
+.Ed
+.
+.Pp
+Filter scripts are run with the filename as their first argument
+and the contents of the file on standard input.
+The
+.Xr ttpre 1
+command is my own utility to convert
+.Xr man 1
+output to HTML.
+The
+.Xr hi 1
+command is my own
+.Lk https://causal.agency/bin/hi.html "syntax highlighter" .
+.
+.Ss git
+I create my repositories in
+.Pa ~/pub
+with
+.Ql git init --bare
+and use
+.Pa git.causal.agency:pub/example.git
+locally as the remote.
+Descriptions are set by editing the
+.Pa description
+file in each repository.
+The section and homepage can be set with
+.Xr git-config 1
+through the keys
+.Cm cgit.section
+and
+.Cm cgit.homepage ,
+respectively,
+thanks to the
+.Cm enable-git-config
+option above.
+.
+.Ss Redirects
+I added the following to the
+.Cm server
+block that used to serve gitea in
+.Pa nginx.conf :
+.Bd -literal -offset indent
+location ~* /june/([^.]+)[.]git(.*) {
+	return 301 https://git.causal.agency/$1$2?$query_string;
+}
+location ~* /june/([^/]+) {
+	return 301 https://git.causal.agency/$1;
+}
+location / {
+	return 301 https://git.causal.agency;
+}
+.Ed
+.
+.Pp
+This redirects any links to my gitea repos
+to the corresponding repo in cgit.
+The first
+.Sy location
+block also redirects gitea HTTP clone URLs to cgit
+so that
+.Xr git-pull 1
+continues to work on existing clones.
+.
+.Ss Update: fast HTTPS clones
+Someone pointed out that cloning my repos
+over HTTPS was incredibly slow,
+and this is because cgit only implements the
+.Dq dumb
+HTTP git transport.
+To speed up cloning,
+I send the URLs used by the
+.Dq smart
+HTTP transport to
+.Xr git-http-backend 1
+instead:
+.Bd -literal -offset indent
+location ~ /.+/(info/refs|git-upload-pack) {
+	fastcgi_pass unix:/var/run/slowcgi.sock;
+	fastcgi_param SCRIPT_NAME /usr/local/libexec/git-core/git-http-backend;
+	fastcgi_param GIT_HTTP_EXPORT_ALL 1;
+	fastcgi_param GIT_PROJECT_ROOT /home/june/pub;
+	include fastcgi_params;
+}
+.Ed
+.
+.Pp
+I factored out the FastCGI parameters
+I'm using with cgit
+to be included here as well.
+.
+.Sh AUTHORS
+.An June Bug Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/008-how-irc.7 b/www/text.causal.agency/008-how-irc.7
new file mode 100644
index 00000000..aba1bbf9
--- /dev/null
+++ b/www/text.causal.agency/008-how-irc.7
@@ -0,0 +1,193 @@
+.Dd March  8, 2020
+.Dt HOW-IRC 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm How I Relay Chat
+.Nd in code
+.
+.Sh DESCRIPTION
+I've been writing a lot of IRC software lately
+.Pq Sx SEE ALSO ,
+and developed some nice code patterns
+that I've been reusing.
+Here they are.
+.
+.Ss Parsing
+I use fixed size buffers almost everywhere,
+so it's necessary to know IRC's size limits.
+A traditional IRC message is a maximum of 512 bytes,
+but the IRCv3 message-tags spec adds
+(unreasonably, in my opinion)
+8191 bytes for tags.
+IRC messages also have a maximum of 15 command parameters.
+.Bd -literal -offset indent
+enum { MessageCap = 8191 + 512 };
+enum { ParamCap = 15 };
+.Ed
+.
+.Pp
+If I'm using tags,
+I'll use X macros
+to declare the set I care about.
+X macros are a way of maintaining parallel arrays,
+or in this case an enum and an array.
+.Bd -literal -offset indent
+#define ENUM_TAG \e
+	X("msgid", TagMsgid) \e
+	X("time", TagTime)
+
+enum Tag {
+#define X(name, id) id,
+	ENUM_TAG
+#undef X
+	TagCap,
+};
+
+static const char *TagNames[TagCap] = {
+#define X(name, id) [id] = name,
+	ENUM_TAG
+#undef X
+};
+.Ed
+.
+.Pp
+The TagNames array is used by the parsing function
+to assign tag values into the message structure,
+which looks like this:
+.Bd -literal -offset indent
+struct Message {
+	char *tags[TagCap];
+	char *nick;
+	char *user;
+	char *host;
+	char *cmd;
+	char *params[ParamCap];
+};
+.Ed
+.
+.Pp
+I'm a fan of using
+.Xr strsep 3
+for simple parsing.
+Although it modifies its input
+(replacing delimiters with NUL terminators),
+since the raw message is in a static buffer,
+it is ideal for so-called zero-copy parsing.
+I'm not going to include the whole parsing function here,
+but I will at least include the part that many get wrong,
+which is dealing with the colon-prefixed trailing parameter:
+.Bd -literal -offset indent
+msg.cmd = strsep(&line, " ");
+for (int i = 0; line && i < ParamCap; ++i) {
+	if (line[0] == ':') {
+		msg.params[i] = &line[1];
+		break;
+	}
+	msg.params[i] = strsep(&line, " ");
+}
+.Ed
+.
+.Ss Handling
+To handle IRC commands and replies
+I add handler functions to a big array.
+I usually have some form of helper as well
+to check the number of expected parameters.
+.Bd -literal -offset indent
+typedef void HandlerFn(struct Message *msg);
+
+static const struct Handler {
+	const char *cmd;
+	HandlerFn *fn;
+} Handlers[] = {
+	{ "001", handleReplyWelcome },
+	{ "PING", handlePing },
+	{ "PRIVMSG", handlePrivmsg },
+};
+.Ed
+.
+.Pp
+Since I keep these arrays sorted anyway,
+I started using the standard
+.Xr bsearch 3
+function,
+but a basic for loop probably works just as well.
+I do wish I could compile-time assert
+that the array really is sorted, though.
+.Bd -literal -offset indent
+static int compar(const void *cmd, const void *_handler) {
+	const struct Handler *handler = _handler;
+	return strcmp(cmd, handler->cmd);
+}
+
+void handle(struct Message msg) {
+	if (!msg.cmd) return;
+	const struct Handler *handler = bsearch(
+		msg.cmd,
+		Handlers, ARRAY_LEN(Handlers),
+		sizeof(*handler), compar
+	);
+	if (handler) handler->fn(&msg);
+}
+.Ed
+.
+.Ss Capabilities
+For IRCv3 capabilties
+I use X macros again,
+this time with another handy macro
+for declaring bit flag enums.
+.Bd -literal -offset indent
+#define BIT(x) x##Bit, x = 1 << x##Bit, x##Bit_ = x##Bit
+
+#define ENUM_CAP \e
+	X("message-tags", CapMessageTags) \e
+	X("sasl", CapSASL) \e
+	X("server-time", CapServerTime)
+
+enum Cap {
+#define X(name, id) BIT(id),
+	ENUM_CAP
+#undef X
+};
+
+static const char *CapNames[] = {
+#define X(name, id) [id##Bit] = name,
+	ENUM_CAP
+#undef X
+};
+.Ed
+.
+.Pp
+The
+.Fn BIT
+macro declares, for example,
+.Dv CapSASL
+as the bit flag and
+.Dv CapSASLBit
+as the corresponding index.
+The
+.Vt "enum Cap"
+is used as a set,
+for example checking if SASL is enabled with
+.Ql caps & CapSASL .
+.
+.Pp
+These patterns are serving my IRC software well,
+and my IRC projects are serving me well.
+It is immensely satisfying
+to be (near) constantly using software
+that I wrote myself and am happy with,
+regardless of how niche it may be.
+.
+.Sh SEE ALSO
+.Bl -item -compact
+.It
+.Lk https://git.causal.agency/pounce/about "IRC bouncer"
+.It
+.Lk https://git.causal.agency/litterbox/about "IRC logger"
+.It
+.Lk https://git.causal.agency/catgirl/about "IRC client"
+.El
+.
+.Sh AUTHORS
+.An June Bug Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/009-casual-update.7 b/www/text.causal.agency/009-casual-update.7
new file mode 100644
index 00000000..0548436a
--- /dev/null
+++ b/www/text.causal.agency/009-casual-update.7
@@ -0,0 +1,127 @@
+.Dd May  6, 2020
+.Dt CASUAL-UPDATE 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm casual update
+.Nd software developments
+.
+.Sh DESCRIPTION
+I've been figuring out more of IMAP
+and Internet Messages in general
+while working on a new project
+so I've revisited some older ones.
+I've copied my somewhat more proper
+IMAP parsing code into them,
+so they should be more robust.
+.
+.Pp
+.Xr imbox 1
+is my tool to export messages
+in mboxrd format directly from IMAP.
+It's mostly for applying patches sent by email
+without having any kind of local mail setup.
+For that,
+it includes the
+.Xr git-fetch-email 1
+wrapper which works very similarly to
+.Xr git-send-email 1 .
+I learned by reading the source of
+.Xr git-subtree 1
+that
+.Xr git-rev-parse 1
+can be used by shell scripts
+to parse long options,
+so I added those.
+I also added the
+.Fl Fl apply
+flag to automatically pipe to
+.Xr git-am 1
+with the right flags for mboxrd.
+.
+.Pp
+.Xr notemap 1
+is a tool for mirroring text files
+to an IMAP Notes mailbox,
+which is used by FastMail's web UI
+and the macOS/iOS Notes app.
+Its original parsing code
+was particularly ad-hoc.
+Since I've now learned
+how UTF-8 headers are encoded,
+I updated it to properly encode
+the file name in the Subject line.
+.
+.Pp
+I also got distracted by
+a conversation about UNIX-domain sockets
+where I was comparing the macOS and FreeBSD
+.Xr unix 4
+pages and the Linux
+.Xr unix 7
+page.
+This lead me to make
+.Xr exman 1 ,
+a tool to locally install and read
+manual pages for Linux, POSIX,
+.Fx ,
+.Nx
+and
+.Ox .
+I've already gotten quite a bit of use out of it.
+.
+.Pp
+In yet another IRC distraction,
+I was talking about some further plans for my IRC software,
+and realized it might be time to write
+my future projects list down.
+I opened a
+.Pa .plan
+file,
+immediately wondered how anyone can write plain text,
+then switched to a
+.Pa plan.7
+file.
+There's nothing I won't use
+.Xr mdoc 7
+for.
+After a little setup,
+I can now be fingered,
+and make jokes about this silly little protocol
+from the days of old.
+.Xr finger 1 Ap s
+default output fills me with joy:
+.Bd -unfilled -offset indent
+No Mail.
+No Plan.
+.Ed
+.
+.Pp
+And speaking of IRC and plans,
+I've been meaning to tag
+.Xr catgirl 1
+version 1.0 for a while now.
+I've been using it as my main client
+and my commits to it have really slowed down.
+When I do tag it,
+I'm planning on writing another post
+about my whole
+.Dq suite
+of IRC software
+and how the parts work together.
+Watch this space.
+.
+.Sh SEE ALSO
+.Bl -item -compact
+.It
+.Lk https://git.causal.agency/imbox "imbox"
+.It
+.Lk https://git.causal.agency/notemap "notemap"
+.It
+.Lk https://git.causal.agency/exman "exman"
+.It
+.Lk https://git.causal.agency/catgirl "catgirl"
+.El
+.
+.Sh AUTHORS
+.An June Bug Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/010-irc-suite.7 b/www/text.causal.agency/010-irc-suite.7
new file mode 100644
index 00000000..b54adf3d
--- /dev/null
+++ b/www/text.causal.agency/010-irc-suite.7
@@ -0,0 +1,409 @@
+.Dd June 19, 2020
+.Dt IRC-SUITE 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm IRC suite
+.Nd my own IRC software
+.
+.Sh DESCRIPTION
+Over the past months
+.Po
+eight of them, according to
+.Xr git-log 1
+.Pc
+I developed a new
+.Dq suite
+of IRC software
+that I now use full-time,
+consisting of a bouncer,
+a new logging and search solution,
+and a terminal client.
+These new programs share some characteristics:
+they are all TLS-only
+and use the libtls API from LibreSSL,
+they can all be entirely configured from the command line
+or with equivalent configuration files,
+they are all designed as
+a one process to one IRC connection mapping,
+and they all take advantage of IRCv3 features.
+.
+.Pp
+For context,
+I was previously running
+the znc IRC bouncer
+and using the Textual IRC client
+with its plain text logs.
+I also continue to use
+the Palaver IRC client for iOS.
+.
+.Ss Background
+A bouncer is a piece of server software
+that stays connected to IRC at all times
+and acts as a relay
+between your client and the IRC server.
+When the client is disconnected,
+the bouncer buffers incoming messages
+to send to the client when it reconnects.
+.
+.Pp
+Aside from this,
+bouncers have another advantage:
+client multiplexing.
+Several clients,
+for instance on different computers
+or a phone,
+should be able to connect to the same bouncer,
+and send and receive messages under the same nick.
+Unfortunately,
+znc does not handle this use-case well at all.
+Out of the box it offers two options:
+either any client connection totally clears the buffer,
+causing other clients to miss chat history;
+or the buffer is never cleared,
+causing every client connection
+to be repeatedly spammed with redundant history.
+There is also a znc wiki page
+that suggests one way to solve this issue
+is to connect znc to itself multiple times over.
+Yikes.
+.
+.Ss pounce
+My dissatisfaction with
+connecting multiple clients to znc
+directly motivated me to start working
+on a new multi-client-focused IRC bouncer.
+The result is
+.Xr pounce 1 ,
+based on a rather straightforward
+single-producer (the IRC server)
+multiple-consumer (each IRC client)
+ring buffer.
+Each client has its own
+independent position in the buffer
+so can properly be brought up to date
+whenever it connects.
+.
+.Pp
+Additionally,
+by assuming support for the IRCv3
+.Sy server-time
+extension,
+all IRC events can be accurately
+relayed to clients at any time,
+and the internals of
+.Xr pounce 1
+can be kept very simple.
+In fact,
+it completely avoids parsing most IRC messages,
+simply pushing them into the buffer
+with an associated timestamp.
+.
+.Pp
+The usernames sent by clients during registration
+are used as opaque identifiers for buffer consumers.
+This was chosen since most clients
+can be configured to send an arbitrary username,
+and those that don't often default
+to the name of the client itself,
+making it an appropriate identifier.
+.
+.Pp
+Later,
+I added a way for clients
+to be informed of their own buffer positions
+using a vendor-specific IRCv3 capability.
+This means a client
+can save the position
+of the last message it actually received,
+and request to set its position
+when it reconnects,
+ensuring no messages are lost
+to network issues
+or software crashes.
+.
+.Ss calico
+Due to the simple design of mapping
+one process to one IRC (server) connection,
+it is necessary to run several instances of
+.Xr pounce 1 .
+Initially I simply used different ports for each,
+but as I connected to more networks
+and even ran some instances for friends,
+it became less feasible.
+.
+.Pp
+The solution I came up with
+was to dispatch incoming connections
+using Server Name Indication, or SNI.
+This way,
+multiple domains pointing to the same host
+could be used with only one port
+to connect to different instances of
+.Xr pounce 1 .
+For example,
+I use a
+.Li *.irc.causal.agency
+wildcard DNS entry
+and a subdomain for each IRC network,
+all on port 6697.
+.
+.Pp
+The
+.Xr calico 1
+daemon included with pounce
+accomplishes this dispatch
+using the
+.Dv MSG_PEEK
+flag of
+.Xr recvmsg(2)
+on incoming connections.
+Since SNI is immediately sent by TLS clients
+as part of the ClientHello message in clear-text,
+it can be processed
+without doing any actual TLS.
+The connection itself is then
+sent to the corresponding
+.Xr pounce 1
+instance
+over UNIX-domain socket,
+which handles TLS as normal.
+This means that
+.Xr calico 1
+and
+.Xr pounce 1
+operate entirely independently of each other.
+.
+.Ss litterbox
+Based on the multiple-consumer ring buffer design,
+I realized it would be easy
+to implement additional functionality
+as independent purpose-built clients
+which connect to
+.Xr pounce 1
+alongside regular clients.
+This could allow dedicated OTR or DCC software
+to operate in parallel with a basic client,
+or for more passive software
+to provide notifications
+or dedicated logging.
+.
+.Pp
+For the latter,
+I wanted to do better than
+plain text log files.
+.Xr grep 1
+over files works fine,
+but search could be faster and smarter,
+and the text format is
+more lossy and less structured
+than I'd like it to be.
+Conveniently,
+SQLite provides an extension
+(actually two)
+for full-text search.
+.
+.Pp
+The litterbox project
+is my dedicated logging solution
+using SQLite FTS5.
+It consists of three tools:
+the
+.Xr litterbox 1
+daemon itself which connects to pounce
+and logs messages to SQLite,
+the
+.Xr scoop 1
+command line query tool,
+and the
+.Xr unscoop 1
+plain text import tool.
+The
+.Xr scoop 1
+tool constructs SQL queries
+and formats the results for viewing,
+with coloured nicks
+and piped to a pager
+by default.
+.
+.Pp
+The
+.Xr litterbox 1
+daemon
+can also provide a simple
+.Dq online
+.Pq over IRC
+search query interface
+to other connected clients.
+The simplest way to allow different
+.Xr pounce 1
+clients to talk to each other
+was to route private messages to self
+internally without sending them to the IRC server.
+So from any client
+I can simply message myself
+a full-text search query
+and
+.Xr litterbox 1
+responds with the results.
+.
+.Pp
+Along with routing self-messages,
+.Xr pounce 1
+also provides a vendor-specific IRCv3 capability
+for passive clients such as
+.Xr litterbox 1
+to indicate that they should not influence
+the automatic away status,
+which is normally only set
+when no clients are connected.
+.
+.Pp
+An advantage of this architecture
+of dedicated clients
+rather than bouncer modules
+is that they need not run
+on the same host.
+I run my bouncers on a VPS,
+but I'd rather not store my private logs there,
+so
+.Xr litterbox 1
+runs instead on a Raspberry Pi
+in my apartment.
+Also,
+since it is essentially
+just a regular IRC bot,
+it could be used independently
+for keeping public logs for a channel.
+.
+.Ss catgirl
+There's not really that much to say
+about the client,
+.Xr catgirl 1 .
+Of the three projects
+it contains the most code
+but is also the least interesting,
+in my opinion.
+It just does what I want a client to do,
+and gets the details right.
+.
+.Pp
+Tab complete is ordered by most recently seen or used,
+and completing several nicks
+inserts commas between them
+as well as the colon following the final nick.
+In the input line,
+the prompt is updated
+to reflect whether the input
+will be interpreted as a command or as a message.
+Messages are automatically scanned for URLs,
+which can be opened or copied with commands
+specifying the nick or a substring of the URL.
+.
+.Pp
+Scrolling in a window creates a split view,
+keeping the latest messages visible.
+Nick colours are based instead on usernames,
+keeping them more stable across renames,
+and mentions in messages are coloured
+to make the conversation easier to follow.
+The visibility of ignored messages
+can be toggled at any time.
+Channels can be muted
+so their activity is hidden
+from the status line
+unless you are pinged.
+.
+.Pp
+.Xr catgirl 1
+is configured entirely on the command line
+or in equivalent simple configuration files.
+There's no dynamic manipulation of state
+using complex
+.Ql /
+commands like in some other clients.
+.
+.Pp
+The major caveat is that
+.Xr catgirl 1
+connects to only one network at a time.
+This keeps the configuration, the interface
+and the code much simpler.
+.Xr tmux 1 ,
+.Xr screen 1
+or a tabbed terminal emulator
+are good options to run several instances.
+.
+.Pp
+If you're interested in giving
+.Xr catgirl 1
+a quick (and necessarily limited) try,
+you can
+.Li ssh chat@ascii.town .
+.
+.Ss Future
+I think I'm done with IRC software for now.
+As mentioned above,
+there are a few more pieces
+that could fit in to this setup,
+but I don't really want or need them right now.
+One thing I definitely want to try
+at some point
+is adding a litterbox component
+to index the contents of URLs
+to make finding previously shared links easier.
+.
+.Pp
+If you try any of this software
+and have feedback,
+let me know in
+.Li #ascii.town
+on freenode
+or by email.
+And of course,
+patches are always welcome.
+.
+.Ss Update: scooper
+Somehow I had the motivation
+to create a web interface for litterbox:
+.Xr scooper 1 .
+It can be used either as CGI
+or as a FastCGI worker,
+and I used the excellent
+.Xr kcgi 3
+library for it.
+.
+.Pp
+The main advantage of this interface
+is that you can click on a search result
+to be brought to its context in the log viewer.
+I also added an option to
+.Xr litterbox 1
+to provide a corresponding scooper link
+in response to its query interface.
+.
+.Pp
+A small demo of scooper is hosted at
+.Aq Lk "https://causal.agency/scooper/" .
+It publicly logs the
+.Li #litterbox
+channel on freenode.
+.
+.Sh SEE ALSO
+.Bl -item -compact
+.It
+.Lk "https://git.causal.agency/pounce" pounce
+.It
+.Lk "https://git.causal.agency/litterbox" litterbox
+.It
+.Lk "https://git.causal.agency/catgirl" catgirl
+.It
+.Lk "https://www.sqlite.org/fts5.html" "SQLite FTS5 Extension"
+.It
+.Lk "https://git.causal.agency/scooper" scooper
+.It
+.Lk "https://kristaps.bsd.lv/kcgi/" kcgi
+.El
+.
+.Sh AUTHORS
+.An June Bug Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/011-libretls.7 b/www/text.causal.agency/011-libretls.7
new file mode 100644
index 00000000..c29c325e
--- /dev/null
+++ b/www/text.causal.agency/011-libretls.7
@@ -0,0 +1,220 @@
+.Dd August  9, 2020
+.Dt LIBRETLS 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm LibreTLS
+.Nd libtls for OpenSSL
+.
+.Sh DESCRIPTION
+This is a sort of announcement post about LibreTLS,
+my port of libtls from LibreSSL to OpenSSL.
+If you've wanted to try any of my software
+but have been unable to because of LibreSSL,
+LibreTLS is an option that will likely work for you.
+I'm including instructions
+for building it and my IRC software
+on Debian as an example,
+since manually installing libraries
+is less straightforward than it could be.
+.
+.Pp
+libtls is
+.Do
+a new TLS library,
+designed to make it easier to write foolproof applications
+.Dc .
+It was developed as part of LibreSSL,
+.Ox Ap s
+fork of OpenSSL,
+and is implemented against their version of libssl.
+It provides a nice high-level API
+for TLS sockets,
+with functions like
+.Xr tls_connect 3 ,
+.Xr tls_read 3
+and
+.Xr tls_write 3 .
+This is a vast improvement over libssl's
+confusing mess of an API!
+Its relative obscurity is a real shame
+for C programmers.
+.
+.Pp
+An obvious cause of its obscurity
+is that it is tied to LibreSSL.
+Although LibreSSL is available
+for platforms other than
+.Ox ,
+it conflicts with OpenSSL
+so is difficult to install alongside it
+and is often not packaged at all.
+Additionally,
+even if a user manually installs LibreSSL,
+libtls is likely not to work on some distros
+due to its hardcoded CA bundle file path.
+.
+.Pp
+Since libtls is implemented against libssl,
+which originates in OpenSSL,
+it should be possible to use libtls with it.
+This is what I set out to do in LibreTLS.
+I started by importing the sources
+from a LibreSSL-portable release,
+then worked on porting the portions
+that were incompatible with OpenSSL.
+.
+.Pp
+The simpler changes just involved
+replacing internal struct field accesses
+with public APIs.
+libtls accesses libssl internals
+using a hack to get the header files
+to declare private struct fields,
+and for basically no reason.
+The bigger changes involved
+reimplementing some functions
+which only exist in LibreSSL,
+but these were still quite small.
+I also imported the necessary compatibility functions
+from LibreSSL's libcrypto
+and adapated the autotools build files
+to produce only a libtls
+which depends on OpenSSL.
+.
+.Pp
+Along the way
+I decided to make one small behavioural change
+in order for LibreTLS to be more likely
+to work for everyone.
+I removed the hardcoded CA file path
+and changed the default configuration
+to use OpenSSL's default CA paths,
+which include a CA directory.
+This seems to be the preferred CA source
+on systems such as Debian,
+where the default CA file path doesn't exist.
+.
+.Pp
+I think the reason LibreSSL
+wants to avoid using a CA directory
+is so that it can fully load the CA file
+once before being sandboxed.
+However,
+using OpenSSL's default configuration,
+the CA file will still be loaded immediately
+if it exists.
+If it doesn't exist,
+sandboxed applications
+will fail when trying to
+load certificates from the directory,
+but unsandboxed applications
+will work just fine.
+Since LibreSSL's libtls
+would fail either way,
+I think the new behaviour
+is an improvement.
+.
+.Pp
+Another advantage of separating libtls from LibreSSL
+is that it is unencumbered by OpenSSL's
+awkward double-license,
+both of which are incompatible with the GPL.
+libtls is all new ISC-licensed code,
+and future versions of OpenSSL (3.0)
+will be released under the Apache 2.0 license,
+which is compatible with GPLv3.
+In the future,
+GPL software will be able to link with
+libtls and OpenSSL without additional permissions.
+.
+.Pp
+It's also worth noting that LibreSSL
+likely will not be able to import any code
+from future versions of OpenSSL,
+since Apache 2.0 is on
+.Ox Ap s
+license shitlist.
+LLVM is also slowly changing their license
+to Apache 2.0,
+so it'll be interesting to see what
+.Ox
+does.
+.
+.Ss Installing Manually
+To install LibreTLS on Debian,
+for example,
+fetch a release tarball from
+.Lk https://causal.agency/libretls/
+and install the build dependencies:
+.Bd -literal -offset indent
+sudo apt-get install build-essential libssl-dev pkgconf
+.Ed
+.
+.Pp
+.Xr pkgconf 1
+isn't a dependency of LibreTLS itself,
+but it's how my software
+configures its build
+for a dependency on libtls.
+The usual build steps
+will install the library:
+.Bd -literal -offset indent
+\&./configure
+make all
+sudo make install
+.Ed
+.
+.Pp
+The library will be installed in
+.Pa /usr/local/lib
+by default,
+and you need to make sure
+the dynamic linker
+will be able to find it there.
+On Debian,
+.Pa /usr/local/lib
+already appears in
+.Pa /etc/ld.so.conf.d/libc.conf ,
+but on other systems
+you'll probably need to add it to either
+.Pa /etc/ld.so.conf
+or a new file such as
+.Pa /etc/ld.so.conf.d/local.conf .
+Once the library is installed
+and the path is configured,
+the linker cache needs to be refreshed:
+.Bd -literal -offset indent
+sudo ldconfig
+.Ed
+.
+.Pp
+You'll probably also need to set
+.Ev PKG_CONFIG_PATH
+for the configure scripts
+of my software:
+.Bd -literal -offset indent
+PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./configure
+.Ed
+.
+.Pp
+On
+.Fx ,
+LibreTLS and some of my IRC software
+can be installed from my own
+.Lk https://git.causal.agency/ports/ "ports tree"
+.
+.Sh SEE ALSO
+.Bl -item -compact
+.It
+.Lk https://git.causal.agency/libretls/about LibreTLS
+.It
+.Lk https://man.openbsd.org/tls_init.3 "libtls API documentation"
+.El
+.
+.Pp
+Another alternative libtls implementation,
+.Lk https://sr.ht/~mcf/libtls-bearssl/ "libtls-bearssl"
+.
+.Sh AUTHORS
+.An June Bug Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/012-inability.7 b/www/text.causal.agency/012-inability.7
new file mode 100644
index 00000000..d352143b
--- /dev/null
+++ b/www/text.causal.agency/012-inability.7
@@ -0,0 +1,39 @@
+.Dd November 26, 2020
+.Dt INABILITY 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Inability
+.Nd losing the ability to create
+.
+.Sh DESCRIPTION
+For often weeks, sometimes months at a time,
+I lose the ability to write new code.
+I can still make fixes
+and little cleanups
+in my existing projects,
+but if I try to work on something new,
+nothing happens.
+I can't get anything done.
+.
+.Pp
+I think it's now been
+over 3 months
+since I've created anything.
+I don't know what to do about it.
+In the past I've eventually
+regained the ability to code,
+but it's unclear to me how or why.
+I also don't know what
+I should be doing instead.
+Writing code is the only hobby
+I've ever really developed,
+so without it I basically
+don't do anything.
+.
+.Pp
+Does this happen to anyone else?
+How do you cope?
+.
+.Sh AUTHORS
+.Mt june@causal.agency
diff --git a/www/text.causal.agency/013-hot-tips.7 b/www/text.causal.agency/013-hot-tips.7
new file mode 100644
index 00000000..63b6e353
--- /dev/null
+++ b/www/text.causal.agency/013-hot-tips.7
@@ -0,0 +1,156 @@
+.Dd December  2, 2020
+.Dt HOT-TIPS 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm hot tips
+.Nd from my files
+.
+.Sh DESCRIPTION
+This is a short list of tips
+from my configuration files and code
+that might be useful.
+.
+.Ss Shell
+.Bl -tag -width Ds
+.It CDPATH=:~
+This is useful if you sometimes type,
+for example,
+.Ql cd src/bin
+wanting to go to
+.Pa ~/src/bin
+but you aren't in
+.Pa ~ .
+If the path doesn't exist
+in the current directory,
+.Ic cd
+will try it in
+.Pa ~
+as well.
+.
+.It alias ls='LC_COLLATE=C ls'
+This makes it so that
+.Xr ls 1
+lists files in ASCIIbetical order,
+which puts capitalized names like
+.Pa README
+and
+.Pa Makefile
+first.
+.
+.It git config --global commit.verbose true
+Not shell but close enough.
+This makes it so the entire diff is shown
+below the usual summary
+in the editor for a
+.Xr git-commit(1)
+message.
+Useful for doing a quick review
+of what you're committing.
+.El
+.
+.Ss (neo)vim
+.Bl -tag -width Ds
+.It set inccommand=nosplit
+This is the only
+.Xr nvim 1
+feature I really care about
+aside from the improved defaults.
+This provides a live preview of what a
+.Ic :s
+substitution command will do.
+It makes it much easier to
+write complex substitutions.
+.
+.It nmap <leader>s vip:sort<CR>
+This mapping sorts the lines of a paragraph,
+or block of text separated by blank lines.
+I use this a lot to sort
+#include directives.
+.
+.It nmap <leader>S $vi{:sort<CR>
+Similar to the last mapping,
+this one sorts lines inside braces.
+I use this to sort
+switch statement cases
+or array initializers.
+.
+.It nmap <leader>a m':0/^#include <<CR>:nohlsearch<CR>O#include <
+I use this mapping to add new
+#include directives,
+usually followed by
+.Ic <leader>s
+and
+.Ic ''
+to sort them
+and return to where I was.
+.
+.It nmap <leader>d :0delete<CR>:0read !date +'.Dd \e%B \e%e, \e%Y'<CR>
+I use this to replace the first line of
+.Xr mdoc 7
+files with the current date.
+.El
+.
+.Ss C
+.Bl -tag -width Ds
+.It #define Q(...) #__VA_ARGS__
+This is what I've started using
+to quote things like SQL statements
+or HTML fragments in C.
+Anything that happens to be valid C tokens,
+which is most code,
+can be quoted this way.
+Macros are not expanded
+inside the quoted part.
+You can embed (matched) quotes
+without having to escape them.
+Whitespace gets collapsed,
+so you can write nicely formatted multi-line SQL
+that doesn't mess up your debug logging,
+for example.
+.Bd -literal -offset indent
+const char *sql = Q(
+	INSERT OR IGNORE INTO names (nick, user, host)
+	VALUES (:nick, :user, :host);
+);
+.Ed
+.
+.It #define BIT(x) x##Bit, x = 1 << x##Bit, x##Bit_ = x##Bit
+I use this macro to declare bitflag enums.
+It takes advantage of
+auto-incrementing enum items
+so you don't need to set the values manually.
+You also get constants
+for both the bit index
+and the flag value
+for each item.
+.Bd -literal -offset indent
+enum Attr {
+	BIT(Bold),
+	BIT(Reverse),
+	BIT(Italic),
+	BIT(Underline),
+};
+.Ed
+.Pp
+For example,
+defines
+.Sy ItalicBit = 2
+and
+.Sy Italic = 1 << 2 .
+Ignore the extraneous constants.
+.
+.It typedef int FnType(const char *str, size_t len);
+You can just typedef function types!
+It annoys me more than it probably should
+that everyone writes ugly
+function pointer typedefs.
+Just stick
+.Sy typedef
+on the front of a function declaration
+and use
+.Vt FnType * .
+.El
+.
+.Sh AUTHORS
+.Mt june@causal.agency
diff --git a/www/text.causal.agency/Makefile b/www/text.causal.agency/Makefile
new file mode 100644
index 00000000..6a8fcc87
--- /dev/null
+++ b/www/text.causal.agency/Makefile
@@ -0,0 +1,31 @@
+WEBROOT = /usr/local/www/text.causal.agency
+
+TXTS += 001-make.txt
+TXTS += 002-writing-mdoc.txt
+TXTS += 003-pleasant-c.txt
+TXTS += 004-uloc.txt
+TXTS += 005-testing-c.txt
+TXTS += 006-some-libs.txt
+TXTS += 007-cgit-setup.txt
+TXTS += 008-how-irc.txt
+TXTS += 009-casual-update.txt
+TXTS += 010-irc-suite.txt
+TXTS += 011-libretls.txt
+TXTS += 012-inability.txt
+TXTS += 013-hot-tips.txt
+
+all: ${TXTS}
+
+.SUFFIXES: .7 .txt
+
+.7.txt:
+	mandoc -T utf8 $< | col -bx > $@
+
+feed.atom: feed.sh ${TXTS}
+	sh feed.sh > feed.atom
+
+clean:
+	rm -f ${TXTS} feed.atom
+
+install: ${TXTS} feed.atom
+	install -p -m 644 ${TXTS} feed.atom ${WEBROOT}
diff --git a/www/text.causal.agency/feed.sh b/www/text.causal.agency/feed.sh
new file mode 100644
index 00000000..fe028621
--- /dev/null
+++ b/www/text.causal.agency/feed.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+set -eu
+
+readonly Root='https://text.causal.agency'
+
+updated=$(date -u '+%FT%TZ')
+cat <<-EOF
+	<?xml version="1.0" encoding="utf-8"?>
+	<feed xmlns="http://www.w3.org/2005/Atom">
+	<title>Causal Agency</title>
+	<author><name>June</name><email>june@causal.agency</email></author>
+	<link href="${Root}"/>
+	<link rel="self" href="${Root}/feed.atom"/>
+	<id>${Root}/</id>
+	<updated>${updated}</updated>
+EOF
+
+encode() {
+	sed '
+		s/&/\&amp;/g
+		s/</\&lt;/g
+		s/"/\&quot;/g
+	' "$@"
+}
+
+for entry in *.7; do
+	txt="${entry%.7}.txt"
+	date=$(grep '^[.]Dd' "$entry" | cut -c 5-)
+	title=$(grep '^[.]Nm' "$entry" | cut -c 5- | encode)
+	summary=$(grep '^[.]Nd' "$entry" | cut -c 5- | encode)
+	published=$(date -ju -f '%B %d, %Y %T' "${date} 00:00:00" '+%FT%TZ')
+	mtime=$(stat -f '%m' "$entry")
+	updated=$(date -ju -f '%s' "$mtime" '+%FT%TZ')
+	cat <<-EOF
+		<entry>
+		<title>${title}</title>
+		<summary>${summary}</summary>
+		<link href="${Root}/${txt}"/>
+		<id>${Root}/${txt}</id>
+		<published>${published}</published>
+		<updated>${updated}</updated>
+		<content type="xhtml">
+		<div xmlns="http://www.w3.org/1999/xhtml">
+	EOF
+	printf '<pre>'
+	encode "$txt"
+	cat <<-EOF
+		</pre>
+		</div>
+		</content>
+		</entry>
+	EOF
+done
+
+echo '</feed>'