telodendria/site/index.html

1153 lines
44 KiB
HTML
Raw Normal View History

2022-07-23 00:19:12 +00:00
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="Jordan Bancino">
<meta name="description"
content="Telodendria, a Matrix homeserver written in ANSI C.">
<meta property="og:title"
content="Telodendria | A Matrix Homeserver written in ANSI C.">
<meta property="og:type" content="website">
<meta property="og:url"
2022-07-23 04:34:11 +00:00
content="https://telodendria.io">
2022-07-23 00:19:12 +00:00
<meta property="og:description"
content="Telodendria, a Matrix homeserver written in ANSI C.">
2022-07-23 21:35:22 +00:00
<link rel="stylesheet" href="style.css">
2022-08-08 14:57:42 +00:00
<link rel="icon" href="assets/Telodendria-196x196.png">
2022-07-23 00:19:12 +00:00
<title>Telodendria | A Matrix Homeserver written in ANSI C.</title>
</head>
<body>
2022-08-08 14:57:42 +00:00
<img id="logo" src="assets/Telodendria-500x500.png" alt="Telodendria Logo" />
2022-07-23 00:19:12 +00:00
<h1 id="telodendria">Telodendria</h1>
<p>
<b>Telodendria:</b> The terminal branches of an axon.
</p>
<div class="msg-error">
2022-07-23 00:19:12 +00:00
<b><i>Note:</i></b> <b>Telodendria</b> is under <i>heavy</i> development.
Please see the <a href="#project-status">Project Status</a>.
</div>
2022-07-23 00:19:12 +00:00
<p>
<b>Telodendria</b> is a Matrix homeserver implementation written from
scratch in ANSI C. It is designed to be lightweight and simple, yet
functional. <b>Telodendria</b> differentiates itself from other Matrix
homeserver implementations because it:
<ul>
<li>
Is written in C, a stable, low-level programming language with a long
history, low build and runtime overhead, and wide compatibility.
</li>
<li>
Is written with minimalism as a primary design goal. Whenever possible
and practical, no third-party libraries are pulled in to the source
code. Everything <b>Telodendria</b> needs is custom written. As a
result, <b>Telodendria</b> depends only on a standard C compiler and
2022-07-23 20:05:50 +00:00
POSIX C library to be built, both of which are built in to a good
Unix-style operating system already, which means you shouldn't have
to install anything extra.
2022-07-23 00:19:12 +00:00
</li>
<li>
Uses a flat-file directory structure to store data instead of a
database. This has a number of advantages:
<ul>
<li>It makes setup and maintenance much easier.</li>
<li>
It allows <b>Telodendria</b> to run on systems with fewer resources.
</li>
<li>
It provides both runtime and data safety and stability. Since no
database is running, there's fewer things that could go wrong because
there's a lot less code running on the system.
</li>
</ul>
</li>
<li>
2022-07-23 20:05:50 +00:00
Is packaged as a single small, statically-linked and highly-optimized
binary that can be run just about anywhere. It is designed to be
extremely easy to set up and consume as few resources as possible.
2022-07-23 00:19:12 +00:00
</li>
<li>
2022-07-28 16:00:43 +00:00
Is permissively licensed. <b>Telodendria</b> is licensed under a
modified MIT license, which imposes few restrictions on what you can do with
it.
</li>
2022-07-23 00:19:12 +00:00
</ul>
<p>
<b>Telodendria</b> is on Matrix! Check out the official Matrix rooms:
</p>
<table>
<tr>
<th>Room</th>
<th>Description</th>
</tr>
<tr>
<td>
<code>#telodendria-releases:bancino.net</code>
</td>
<td>
Get notified of new releases.
</td>
</tr>
<tr>
<td>
<code>#telodendria-general:bancino.net</code>
</td>
<td>
General discussion and support for <b>Telodendria</b>.
</td>
</tr>
<tr>
<td>
<code>#telodendria-issues:bancino.net</code>
</td>
<td>
Report issues with <b>Telodendria</b>.
</td>
</tr>
<tr>
<td>
<code>#telodendria-patches:bancino.net</code>
</td>
<td>
Submit code patches to the <b>Telodendria</b> project.
</td>
</tr>
</table>
<h2 id="table-of-contents">Table of Contents</h2>
<ul>
<li>
<a href="#telodendria">Telodendria</a>
<ul>
<li><a href="#table-of-contents">Table of Contents</a></li>
<li><a href="#download">Download</a></li>
<li><a href="#building-the-source">Building The Source</a></li>
2022-07-23 00:19:12 +00:00
</li>
<li><a href="#configure">Configure Telodendria</a></li>
</li>
<li>
<a href="#project-status">Project Status</a>
<ul>
<li><a href="#phase-1">Phase 1: Getting Off The Ground</a></li>
<li><a href="#phase-2">Phase 2: Building A Foundation</a></li>
<li><a href="#phase-3">Phase 3: Welcome To Matrix</a></li>
<li><a href="#phase-4">Phase 4: A Real Homeserver</a></li>
</ul>
</li>
2022-07-23 01:44:47 +00:00
<li><a href="#documentation-status">Documentation Status</a></li>
2022-07-23 00:19:12 +00:00
<li><a href="#rationale">Rationale</a></li>
<li><a href="#project-goals">Project Goals</a></li>
<li><a href="#getting-support">Getting Support</a></li>
2022-07-23 20:05:50 +00:00
<li>
2022-07-23 00:19:12 +00:00
<a href="#contributing">Contributing</a>
<ul>
<li><a href="#reporting-issues">Reporting Issues</a></li>
<li>
<a href="#Developing">Developing</a>
<ul>
<li><a href="#getting-the-code">Getting The Code</a></li>
<li><a href="#code-style">Code Style</a></li>
2022-07-29 19:29:39 +00:00
<li><a href="#the-build-script">The Build Script</a></li>
<li><a href="#submitting-patches">Submitting Patches</a></li>
2022-07-23 00:19:12 +00:00
</ul>
</li>
</ul>
</li>
<li><a href="#license">License</a></li>
2022-07-25 20:00:52 +00:00
<li><a href="#contributors">Contributors</a></li>
2022-07-23 00:19:12 +00:00
<li><a href="#change-log">Change Log</a></li>
2022-07-23 21:29:54 +00:00
<li><a href="#resources">Resources</a></li>
2022-07-23 00:19:12 +00:00
</ul>
</li>
</ul>
<h2 id="download">Download</h2>
2022-07-23 00:19:12 +00:00
<p>
<b>Telodendria</b> is distributed as source tarballs, in true Unix
fashion. If you want, you can verify the checksum of your download,
and check the signature. To check the signature, you'll need
<code>signify</code>, and the signify public key:
2022-07-23 04:34:11 +00:00
<a href="/telodendria-signify.pub">
telodendria-signify.pub</a>.
2022-07-23 00:19:12 +00:00
</p>
<p>
If your operating system has an official package or port of
<b>Telodendria</b>, you should prefer to use that instead of manually
downloading the source and building it. If your operating system's
package or port is too out of date for your tastes, please contact
the package's maintainers to notify them, or offer to update the
package yourself.
2022-07-23 00:19:12 +00:00
</p>
<table>
<tr>
<th>Version</th>
<th>Download</th>
<th>Checksum</th>
<th>Signature</th>
</tr>
<!--
<tr>
<td>v0.0.0</td>
<td>
2022-07-23 04:34:11 +00:00
<a href="/pub/v0.0.0/Telodendria-v0.0.0.tar.gz">
Telodendria-v0.0.0.tar.gz
</a>
</td>
<td>
2022-07-23 04:34:11 +00:00
<a href="/pub/v0.0.0/Telodendria-v0.0.0.tar.gz.sha256">
SHA256
</a>
</td>
<td>
2022-07-23 04:34:11 +00:00
<a href="/pub/v0.0.0/Telodendria-v0.0.0.tar.gz.sig">
Signify
</a>
</td>
</tr>
-->
<tr>
<td colspan="4" style="text-align: center;">
No downloads here yet. See the
<a href="#project-status">Project Status</a> for more
information.
</td>
</tr>
</table>
2022-07-23 01:38:24 +00:00
<p>
You can check out the change log <a href="#change-log">here</a>.
</p>
<h2 id="building-the-source">Building The Source</h4>
2022-07-23 00:19:12 +00:00
<p>
<b>Telodendria</b> is designed to be light enough that it can be built
from source on just about any operating system. It only has the
following requirements, all of which should be already available to
you on a sufficiently complete operating system:
</p>
<ul>
<li>
2022-08-10 15:58:39 +00:00
A standards-compliant C compiler with a POSIX.1c standard library and
access to a <code>chroot()</code> system call (which is available in all
UNIX-like operating systems, but is not POSIX.) Because <b>Telodendria</b>
is written in ANSI C and sticks almost entirely to POSIX.1c features, it
2022-07-23 20:05:50 +00:00
should compile on almost any compiler and operating system with minimal
effort, but the following compilers and operating systems are known to work:
2022-07-23 00:19:12 +00:00
<ul>
<li>GCC on Linux</li>
2022-08-01 20:09:18 +00:00
<li>Clang on OpenBSD, Linux, and WSL</li>
2022-07-23 00:19:12 +00:00
<li>
2022-08-01 20:09:18 +00:00
Tiny C Compiler (<b>Note:</b> must remove
2022-07-23 00:19:12 +00:00
<code>-Wl,-static -Wl,-gc-sections</code> from <code>LDFLAGS</code>)
on OpenBSD.
2022-07-23 00:19:12 +00:00
</li>
</ul>
Other compilers should work as well, but you may have to play with the
2022-08-01 20:09:18 +00:00
flags.
2022-07-23 00:19:12 +00:00
</li>
<li>
POSIX base utilities, including <code>find</code>, <code>stat</code>,
<code>env</code>, and compliant <code>sh</code>-like shell.
</li>
</ul>
<div class="code">
2022-07-30 13:34:27 +00:00
$ . tools/env.sh
$ td build
2022-07-23 00:19:12 +00:00
</div>
<p>
2022-07-30 13:34:27 +00:00
If everything went well, that will produce <code>build/telodendria</code>,
which you can then place wherever you want, and run as a system daemon.
See the <code>contrib</code> folder for configuration examples.
</p>
<p>
If you're going to be doing more than just building the code, see
<a href="#the-build-script">The Build Script</a> for full documentation on
what the <code>td</code> script can do.
2022-07-23 00:19:12 +00:00
</p>
<h2 id="configure">Configure Telodendria</h3>
2022-07-23 00:19:12 +00:00
<p>
2022-08-10 14:40:41 +00:00
<b>Telodendria</b> is designed to be extremely configurable. As such, it has
a fairly extensive configuration file. The configuration file is passed to
the <b>Telodendria</b> binary with the <code>-c</code> option, and is
typically called <code>/etc/telodendria.conf</code>. It uses OpenBSD-style
syntax, and consists of the following options.
2022-07-23 00:19:12 +00:00
</p>
2022-08-10 14:40:41 +00:00
<p>
There are example configuration files in the <code>contrib</code> folder of
every source tarball, and in the CVS repository.
</p>
<ul>
<li>
2022-08-21 16:37:26 +00:00
<code><b>listen</b> [port]</code>
2022-08-10 14:40:41 +00:00
<p>
2022-08-21 16:37:26 +00:00
The port to listen on. <b>Telodendria</b> will bind to all interfaces, but
It is recommended to configure your firewall so that it only listens on
2022-08-10 14:40:41 +00:00
<code>localhost</code>, and then configure a reverse proxy such as
<code>relayd(8)</code> in front of it, because <b>Telodendria</b> does
not implement TLS. Note that <b>Telodendria</b> doesn't provide multiple
ports for the various services it offers. All APIs are made available over
the same port.
</p>
<p>
2022-08-21 16:37:26 +00:00
<code>[port]</code> should be a decimal port number or a service name.
2022-08-10 14:40:41 +00:00
</p>
<p>
The <code>listen</code> directive is entirely optional; if it is omitted,
then <b>Telodendria</b> will listen on <code>localhost</code>, port
<code>8008</code> by default.
</p>
</li>
<li>
<code><b>server-name</b> [name]</code>
<p>
Configure the domain name of your homeserver. Note that Matrix servers
cannot be migrated to other domains, so once this is set, it should never
change, unless you want unexpected things to happen, or you want to start
over.
</p>
<p>
<code>[name]</code> should be a DNS name that everyone on the network
can resolve. This directive is required.
</p>
</li>
<li>
<code><b>chroot</b> [directory]</code>
<p>
Change the root to the specified directory as soon as possible. Note that
all other paths and files specified in the configuration file must be
accessible from this directory. This only works if <b>Telodendria</b> is
running as <code>root</code>. If it isn't, then a warning is printed to the
log, and no <code>chroot</code> call is made. In that case, <b>Telodendria</b>
will still change into the specified directory, so that other paths can be
made relative to this one.
</p>
<p>
This directive is required. It is expected that the server data and logs
will live here.
</p>
</li>
<li>
<code><b>id</b> [uid] [gid]</code>
<p>
The effective UNIX user and group to drop to after binding to the socket and
changing the filesystem root. This only works if <b>Telodendria</b> is running as
<code>root</code>, and is used as a security mechanism. If <b>Telodendria</b> is
started as a non-privileged user, then a warning is printed to the log if that
user does not match what's specified here.
</p>
<p>
This directive is required, even if <b>Telodendria</b> is unable to switch to
this user. It can be used as a sanity check to make sure the permissions are
working properly.
</p>
</li>
<li>
<code><b>data-dir</b> [directory]</code>
<p>
The data directory in which <b>Telodendria</b> will write all user and event
information. <b>Telodendria</b> doesn't use a database like other Matrix
homeserver implementations; it uses a flat-file directory structure, similar to
how an SMTP server uses Maildirs to deliver email.
</p>
<p>
This directive is required. <code>[directory]</code> should be a path relative
to the <code>chroot</code> directory. Don't depend on the <code>chroot</code>
option working, because there are many legitimate cases when <b>Telodendria</b>
will not be started as <code>root</code>, thus causing the chroot to fail.
</p>
</li>
<li>
<code><b>federation</b> [true|false]</code>
<p>
Whether to enable federation with other Matrix homeservers or not. Matrix by
its very nature is a federated protocol, but if you just want your own internal
chat server with no contact with the outside, then you can use this option to
disable federation. It is highly recommended to set this to <code>true</code>,
however, if you wish to be able to communicate with users on other Matrix
servers.
</p>
<p>
This directive is required, though it may be made optional at some point in
the future.
</p>
</li>
<li>
<code><b>registration</b> [true|false]</code>
<p>
Whether or not to enable new user registration or not. For security and anti-spam
reasons, you can set this to <code>false</code>. If you do, you can still add users
via the administrator API.
</p>
<p>
In an ideal world, everyone would run their own homeserver, so no public
registration would ever be required. But unfortunately, not everyone has the means
to run their own homeserver, especially because of the fact that public IPv4 addresses
are becoming increasingly harder to come by. If you would like to provide a service
for those that are unable to run their own homeserver, you can set this to <code>true</code>,
which will allow anyone to create an account.
</p>
<p>
<b>Telodendria</b> should be capable of handling a large amount of users without
difficulty or security issues. This directive is required.
</p>
</li>
<li>
<code><b>log</b> [file|stdout]</code>
<p>
The log configuration. <b>Telodendria</b> uses its own logging facility, which can
output to either standard output or a file. A number of child directives can be
added to this directive to customize the log output:
</p>
<ul>
<li>
<code><b>level</b> [error|warning|task|message|debug]</code>
<p>
The level of messages to log at. Each level shows all the levels above it. For
example, setting the level to <code>error</code> will show only errors, while
setting the level to <code>warning</code> will show warnings <i>and</i> errors.
<code>task</code> shows tasks, warnings, and errors, and so on.
</p>
</li>
<li>
<code><b>timestampFormat</b> [format|none|default]</code>
<p>
If you want to custom ize the timestamp format shown in the log, or disable
the timestamp functionality altogether, you do so via this option. Acceptable
values are <code>none</code>, <code>default</code>, or a formatter string as
described by your system's <code>strftime()</code> manual.
</li>
<li>
<code><b>color</b> [true|false]</code>
<p>
Whether or not to enable colored output on TTYs. Note that ANSI color sequences
will <i>not</i> be written to a log file, only a real terminal, so this option
only applies if the log is being written to a standard output which is connected
to a terminal.
</p>
</li>
</ul>
</li>
<li>
<code><b>threads</b> [count]</code>
<p>
How many worker threads to sping up to handle requests. This should generally be
less than the total CPU core count, to prevent overloading the system. The most
efficient number of threads ultimately depends on the configuration of the machine
running <b>Telodendria</b>, so you may just have to play around with different
values here to see which gives the best performance.
</p>
</li>
</ul>
2022-07-23 00:19:12 +00:00
<h2 id="project-status">Project Status</h2>
<p>
<b>Telodendria</b> is a very ambitious project. There's a lot that needs
2022-07-23 17:44:55 +00:00
to happen yet before it is usable. At the moment, there's nothing that
even remotely ressembles a Matrix homeserver here; I'm still
2022-07-23 00:19:12 +00:00
getting off the ground and building a foundation.
</p>
<p>
2022-07-23 17:44:55 +00:00
But just because there's nothing here yet doesn't mean you should go away!
I could always use help, so you are more than welcome to help
2022-07-23 00:19:12 +00:00
out if you want things to go quicker. Please see the
<a href="#contributing">Contributing</a> section for details on how you
can get involved.
</p>
<h3 id="phase-1">Phase 1: Getting Off The Ground</h3>
<ul>
<li><s>Name this project</s></li>
<li><s>Set up a CVS repository</s></li>
<li><s>Make CVS repository public</s></li>
<li><s>Write a coding style guide</s></li>
<li><s>Write a build script</s></li>
<li><s>Add a license</s></li>
<li><s>Add support and issue reporting guide</s></li>
<li><s>Add table of contents to this document</s></li>
</ul>
<h3 id="phase-2">Phase 2: Building A Foundation</h3>
<ul>
<li><s>Implement an array</s></li>
<li><s>Implement a logging facility</s></li>
<li><s>Implement argument parsing (<code>-c file -Vh</code>)</s></li>
<li><s>Implement a hash map</s></li>
<li><s>Combine library code files</s></li>
<li><s>Implement configuration file parsing using the hash map</s></li>
<li><s>Base64 encoding/decoding with padded/unpadded support</s></li>
2022-07-23 17:44:55 +00:00
<li><s>Write a release script</s></li>
2022-07-27 17:45:35 +00:00
<li><s>UTF-8 encoder</s></li>
2022-07-26 03:02:50 +00:00
<li>
2022-08-09 17:22:33 +00:00
<s>Implement a JSON library using the hash map and array</s>
2022-07-26 03:02:50 +00:00
<ul>
<li><s>Basic encoding from HashMap/Array/strings, etc.</s></li>
2022-08-09 17:22:33 +00:00
<li><s>Basic decoding to HashMap/Array/strings, etc.</s></li>
2022-07-26 17:08:53 +00:00
<li><s>Proper string encoding</s></li>
<li><s>Proper string decoding</s></li>
2022-07-27 17:38:35 +00:00
<li>
<s><q>Canonical JSON</q></s>
2022-07-27 17:38:35 +00:00
<ul>
2022-07-27 21:30:36 +00:00
<li><s>Keys are sorted lexicographically</s></li>
<li><s>Floats are not allowed (ignore any float values)</s></li>
2022-07-27 17:45:35 +00:00
<li><s>Encode as UTF-8 instead of using <code>\u</code> escapes</s></li>
<li><s>Decode encoded strings to UTF-8</s></li>
2022-07-27 17:38:35 +00:00
</ul>
</li>
2022-07-26 03:02:50 +00:00
</ul>
</li>
2022-08-09 17:22:33 +00:00
<li><s>Write a function that gets the current Unix timestamp in milliseconds.</s></code>
2022-07-23 17:44:55 +00:00
<li><s>Figure out how to w</s>Write unit tests for array/hashmap/etc</li>
2022-08-10 14:40:41 +00:00
<li><s>Parse the <b>Telodendria</b> config file</s></li>
2022-07-28 16:00:43 +00:00
<li>Add <s>license/</s>documentation comments to all source files</li>
2022-07-23 00:19:12 +00:00
<li>Implement a simple HTTP server</li>
<li>
Design the server architecture
<ul>
<li>Route requests</li>
<li>Handle requests</li>
<li>Data abstraction layer</li>
<li>Error generation</li>
</ul>
</li>
</ul>
<h3 id="phase-3">Phase 3: Welcome To Matrix</h3>
<ul>
<li>
2022-08-10 14:40:41 +00:00
Client-Server API
</li>
<li>
Server-Server API
</li>
<li>
Application Service API
</li>
<li>
Identity Service API
2022-07-23 00:19:12 +00:00
</li>
<li>
2022-08-10 14:40:41 +00:00
Push Gateway API
2022-07-23 00:19:12 +00:00
</li>
<li>
2022-08-10 14:40:41 +00:00
Room Versions
2022-07-23 00:19:12 +00:00
</li>
</ul>
<h3 id="phase-4">Phase 4: A Real Homeserver</h3>
<ul>
<li>
Create an OpenBSD package and get it submitted to ports
</li>
<li>
Create a command line tool to manage Telodendria
<ul>
<li>Configuration file generation</li>
<li>User management</li>
<li>Room management</li>
</ul>
</li>
<li>
Migrate from Synapse. I run a Synapse homeserver right now, so somehow
I have to get all my data into the Telodendria format.
</li>
</ul>
<h2 id="documentation-status">Documentation Status</h2>
<p>
This documentation needs just a little work. Here's the things
on my list for that:
</p>
<ul>
2022-07-23 20:05:50 +00:00
<li><s>Update Rationale section</s></li>
<li><s>Update Project description (no longer a CGI binary)</s></li>
<li><s>Update project code requirements (ANSI C, POSIX.1c)</s></li>
<li><s>Clean up dark mode colors (tables, background, code snippets)</s></li>
2022-08-08 15:34:38 +00:00
<li><s>Add logo (possibly center title?)</s></li>
2022-07-25 19:53:49 +00:00
<li><s>Update Code Style to not include indent or line rules, but to use
<code>indent(1)</code> instead.</s></li>
2022-07-25 14:53:20 +00:00
<li><s>Fix typo "Subitting Patches" in Table of Contents</s></li>
<li><s>Make a note in Getting The Code that the password for the
<code>anoncvs</code> account is just <code>anoncvs</code>.</s></li>
2022-07-25 20:00:52 +00:00
<li><s>Add Contributors list</s></li>
2022-08-01 20:09:18 +00:00
<li><s>Add list of <code>make.sh</code> recipes, and what they do</s></li>
2022-08-09 17:22:33 +00:00
<li>Improve Google Lighthouse score
2022-08-08 15:34:38 +00:00
<ul>
<li>Image elements do not have explicit width and height</li>
<li>Background and foreground colors do not have a sufficient contrast ratio. (<code>msg-error</code> div)</li>
2022-08-09 17:22:33 +00:00
<li><s>Lists do not contain only <code>li</code> elements.</s></li>
2022-08-08 15:34:38 +00:00
</ul>
</li>
<li>Add other message <code>div</code>s for notes and warnings.</li>
2022-07-23 00:19:12 +00:00
</ul>
<h2 id="rationale">Rationale</h2>
<p>
2022-07-23 20:05:50 +00:00
I want a lightweight Matrix homeserver designed specifically for
OpenBSD and other Unix-like operating systems. I want a homeserver
that can be developed in <code>vi(1)</code> and compiled with the
built-in C compiler. I want it to function entirely on a base OS
install without having to install any extra packages whatsoever.
I've found that as far as these priorities are concerned, the
existing homeserver implementations fall tremendously short. This
project aims to point out that existing homeserver implementations
are way over-engineered and written in such a way that many programs
and libraries have to be pulled in to use them.
2022-07-23 00:19:12 +00:00
</p>
<p>
2022-07-23 20:05:50 +00:00
I also want to learn how Matrix works, and I want to understand the
code I'm running on my server, which is why I'm writing every
component from scratch, even the HTTP server.
2022-07-23 00:19:12 +00:00
</p>
2022-07-23 20:05:50 +00:00
Telodendria is written entirely in portable ANSI C. It depends on
no third-party C libraries other than the standard POSIX C library.
The only thing you need to run it is a reverse proxy with HTTPS support,
such as <code>relayd(8)</code>, and a directory that data can be
written to. Everything Telodendria needs to run itself is compiled
into a single static binary, and the source code can be built
anywhere, right out of the box. This makes it suitable for running
in a <code>chroot(8)</code> environment.
2022-07-23 00:19:12 +00:00
</p>
<p>
Telodendria doesn't use a database like all the other homeservers.
Instead, it operates more like email: it uses a flat-file data
2022-07-23 20:05:50 +00:00
structure similar to Maildir to store data. The advantage of this is
2022-07-23 00:19:12 +00:00
that it saves server maintainers from also having to maintain a
database. It greatly simplifies the process of getting a Matrix
homeserver up and running, and it makes it highly portable. It also is
extremely easy to back up and restore with base tools; just
<code>tar(1)</code> up the directory, and you're good to go.
</p>
<p>
Telodendria is developed and tested on OpenBSD, but you'll find that it
2022-07-23 20:05:50 +00:00
should just run on any POSIX operating system without modification.
2022-07-23 00:19:12 +00:00
</p>
<h2 id="project-goals">Project Goals</h2>
<p>
The goals of this project are generally divided into <i>user goals</i>,
and <i>developer goals</i>, depending on who they impact the most. This
isn't an exhaustive list, but it is a list of things that I want to
prioritize, <i>because other server implementations lack them</i>.
</p>
<p>
The user goals are as follows:
2022-07-23 00:19:12 +00:00
</p>
<ul>
<li>
To be a production-ready Matrix server capable of handling a lot of
users. Telodendria should have good performance in many diverse
environments. It should scale up well for large instances, and yet also
be able to scale down to a peer-to-peer device.
</li>
<li>
To be easier to configure than any of the other Matrix homeserver
implementations. The configuration file should be flexible,
well-documented, and easy to understand and modify. An intuitive
command-line tool for administrative tasks should also be available.
2022-07-23 00:19:12 +00:00
</li>
</ul>
<p>
The developer goals are as follows:
</p>
<ul>
2022-07-23 00:19:12 +00:00
<li>
To have as few external build and run dependencies as possible. It
should be possible to compile Telodendria on any POSIX operating
system right out of the box, and have it be totally statically linked,
ready to run under a <code>chroot(8)</code>. You'll even notice that
2022-07-23 00:19:12 +00:00
the documentation is written in HTML directly, not Markdown, to remove
the dependency on a Markdown parser and renderer.
</li>
<li>
To have a simple yet elegant workflow, and not depend on any large
or complex development tools, such as code forges. The entire
development workflow should be able to be successfully and efficiently
completed on a base OpenBSD install. Of course you don't have to use
OpenBSD for development by any means, but the point is, the workflow
should not require a lot of tools.
<li>
2022-08-12 23:08:41 +00:00
To be written in clean, elegant, well-tested, and well-documented
code. The goal is to build a Matrix homeserver from the ground up, not
just because I don't like the way existing homeservers are implemented,
but also so I can learn how Matrix really works, and maybe even teach
others along the way.
</li>
<li>
To foster a welcoming community environment. Many of the big communities
such as Linux and OpenBSD have fairly hostile leaders. <b>Telodendria</b>
shouldn't have a hostile leader. I want to be as understanding as I can,
and talk through issues politely and in a civil manner. If I'm failing
in this way, don't be afraid to call me out!
2022-07-23 00:19:12 +00:00
</li>
</ul>
<h2 id="getting-support">Getting Support</h2>
<p>
<b>Telodendria</b> is designed to be fairly straightforward, but that
doesn't mean there won't be hiccups along the way. If you are struggling
to get <b>Telodendria</b> up and running, you're more than welcome to
reach out for support. Just join the
<code>#telodendria-general:bancino.net</code> Matrix channel. Before
you do though, make sure you're running the latest version of
<b>Telodendria</b> and you've thoroughly read through all the
relevant documentation.
</p>
<h2 id="contributing">Contributing</h2>
<p>
<b>Telodendria</b> is an open source project. As such, it welcomes
contributions. There are many ways you can contribute, and any way you
can is greatly appreciated.
</p>
<h3 id="reporting-issues">Reporting Issues</h3>
<p>
If&mdash;after you've reached out to
<code>#telodendria-general:bancino.net</code>&mdash;it has been
determined that there is a problem with <b>Telodendria</b>, it should
be reported to <code>#telodendria-issues:bancino.net</code>. There it
can be discussed further. The issues room serves as the official
issue tracker of <b>Telodendria</b>; although severe issues may be copied
2022-07-23 00:19:12 +00:00
into a <code>TODO</code> file in the CVS repository just so they
don't get lost.
</p>
<h3 id="developing">Developing</h3>
<p>
The primary language used to write <b>Telodendria</b> code is ANSI C.
Yes, that's the original C standard from 1989. The reason this standard
is chosen, and the reason that it will not be changed, is because the
original C is the most portable. Other languages you'll find in the
<b>Telodendria</b> repository are shell scripts and HTML. If you have
any experience at all with any of these languages, your contributions
are valuable. Please follow the guidelines in this section to ensure
the contribution workflow goes as smoothly as possible.
</p>
<h4 id="getting-the-code">Getting The Code</h4>
<p>
If you'd like to hack on <b>Telodendria</b>, you'll need the following
tools in addition to the tools required to
<a href="#building-the-source">build the source</a>:
</p>
<ul>
<li><code>cvs</code> for checking out and updating a local copy
of the source code.</li>
<li><code>indent</code> for formatting your code before generating
patches</li>
<li><code>patch</code> for applying patches to your local copy of the
source code.</li>
</ul>
<p>
Note that all of these tools are built into OpenBSD. While you of course
don't have to use OpenBSD to develop <b>Telodendria</b>, it may make
the process a little easier. In fact, these tools were chosen
precisely <i>because</i> they were built into OpenBSD, the operating
system I use.
</p>
<p>
You can download an official release tarball if you would really like,
2022-07-23 00:19:12 +00:00
but the preferred way is to check out the source code from CVS. This
makes generating patches a lot easier.
2022-07-23 00:19:12 +00:00
</p>
<div class="code">
2022-07-30 13:34:27 +00:00
$ cvs -d anoncvs@bancino.net:/cvs checkout -P Telodendria
2022-07-23 00:19:12 +00:00
$ cd Telodendria
</div>
<p>
2022-07-30 13:34:27 +00:00
If you already checkout out the code previously, make sure you update your
local copy before you start developing:
2022-07-25 15:05:59 +00:00
</p>
2022-07-30 13:34:27 +00:00
<div class="code">
$ cvs -q update -dP
2022-07-30 13:34:27 +00:00
</div>
<p>
2022-07-23 00:19:12 +00:00
You should now have the latest <b>Telodendria</b> source code. Follow
the <a href="#code-style">Code Style</a> as you make your changes.
</p>
<h4 id="code-style">Code Style</h4>
<p>
2022-07-25 19:53:49 +00:00
In general, these are the conventions used by the code base. This
guide may be slightly outdated or subject to change, however. The
source code itself is the absolute source of truth, so as long as
you make your code look like the code surrounding it, you should
be fine.
2022-07-23 00:19:12 +00:00
</p>
<ul>
<li>
All function, enumeration, structure, and header names are
<code>CamelCase</code>. This is preferred to <code>snake_case</code>
because it is more compact.
</li>
<li>
All variable names are <code>lowerCamelCase</code>. This is preferred
to <code>snake_case</code> because it is more compact.
</li>
<li>
2022-07-23 00:19:12 +00:00
<code>enum</code>s and <code>struct</code>s are always
<code>typedef</code>-ed to their same name. The <code>typedef</code>
occurs in the public API header, and the actual declaration occurs in
2022-07-25 19:53:49 +00:00
the implementation file.
2022-07-23 00:19:12 +00:00
</li>
<li>
2022-07-25 19:53:49 +00:00
A feature of the code base lives in a single <code>.c</code> file that
has a matching header file. The header file should only export public
symbols, everything else in the <code>.c</code> file should be
<code>static</code>.
2022-07-23 00:19:12 +00:00
</li>
<li>
2022-07-25 19:53:49 +00:00
Anywhere curly braces are optional, there must still be curly braces.
This makes it easy to add on to the code later, and just makes things
less ambiguous.
2022-07-23 00:19:12 +00:00
</li>
</ul>
<p>
2022-07-25 19:53:49 +00:00
As far as actually formatting the code goes, such as where to put
brackets and whether you use tabs or spaces, use <code>indent(1)</code>
to take care of all of that. The root of the repository has a
<code>.indent.pro</code> file that should automatically be loaded by
<code>indent(1)</code> to set the correct rules. If you don't have
access to a working <code>indent(1)</code>, just indicate in your patch
that I should run <code>indent(1)</code> on the code after applying it.
I'll likely run my <code>indent(1)</code> on the code anyway though,
just to make sure the spacing is consistent, if nothing else.
2022-07-23 00:19:12 +00:00
</p>
2022-07-29 19:29:39 +00:00
<h4 id="the-build-script">The Build Script</h4>
2022-07-29 21:09:34 +00:00
<p>
<b>Telodendria</b> uses a custom build script called <code>td</code>,
2022-07-30 13:34:27 +00:00
for <i>Telodendria developer</i>. The <code>td</code> script is not only
2022-07-29 21:09:34 +00:00
a build script, however. It does all kinds of cool things like
format the source code, and generate patch files. <code>td</code> is
2022-07-30 13:34:27 +00:00
the only supported way to develop <b>Telodendria</b>.
2022-07-29 21:09:34 +00:00
</p>
<p>
I opted to write a custom build script instead of just using
<code>make</code>, because I felt that there is really no way to make
a truly portable <code>Makefile</code> that could do everything I
need. I was doing a lot of research on the differences between the
GNU and BSD versions of <code>make</code>, and I felt it just wasn't
worth it when I could write a small POSIX script that would run on
both GNU systems and BSD systems without a fuss. I also wanted a way
to automate more than just building code. I wanted a system
versatile enough that I could define arbitrary recipes to do whatever
I want. I didn't want to be limited by the <code>Makefile</code>
syntax.
</p>
<p>
The <code>td</code> script is fairly intuitive. It operates somewhat
like <code>make</code>, in that it has recipes that you specify
on the command line. To start using it, just run the following
command in your <b>Telodendria</b> directory:
</p>
<div class="code">
2022-07-30 13:34:27 +00:00
$ . tools/env.sh
2022-07-29 21:09:34 +00:00
</div>
<p>
<b>Note:</b> You will have to run the above command every time you
2022-07-30 13:34:27 +00:00
start a new terminal session, as nothing is persisted to your system.
I believe in non-invasive, fully self-contained tooling, so it is up to
you to hook the Telodendria tools into your environment if you want them
to persist.
2022-07-29 21:09:34 +00:00
</p>
<p>
If you're going to be submitting patches, you should also configure
2022-07-30 13:34:27 +00:00
a <code>.env</code> file in the project directory root, which
<code>td</code> will include automatically for you. For the best
experience, you'll want at least these values:
2022-07-29 21:09:34 +00:00
</p>
<div class="code">
MXID=@your:matrix-id.net
DISPLAY_NAME="Your Display Name"
EDITOR=gedit # Or whatever your preferred editor is.
</div>
<p>
If you don't provide these values, <code>td</code> will deduce them
from your environment. It uses your system username and hostname to
create the <code>MXID</code>, and reads the password database to
get your configured display name. It also uses <code>vi</code> as
the default editor, which may not be suitable for all developers.
</p>
2022-08-01 20:09:18 +00:00
<p>
You can invoke <code>td</code> recipes with the following syntax:
</p>
<div class="code">
$ td &lt;recipe&gt; ...
</div>
<p>
Multiple recipes can be invoked in a single run of <code>td</code>.
At the moment, recipes are run unconditionally; that is, even if parts
of a recipe fail, all following recipes still run.
</p>
<p>
Here is a complete list of recipes currently supported by
<code>td</code>, and a description of what they do. Some recipes can
alter their behavior based on certain environment variables. Those
variables are also documented here.
</p>
<ul>
<li>
<code>build</code>: Build the source code, and generate the
<code>build/telodendria</code> binary. This is the <i>default</i>
recipe; that is, it runs if no recipes are given as arguments to
<code>td</code>. The build recipe is incremental; it only rebuilds
sources that have been modified since the last build, which makes
builds faster. This recipe makes use of many environment variables:
<ul>
<li><code>CFLAGS</code>: Compiler flags to generate object files.</li>
<li><code>LDFLAGS</code>: Compiler flags to link the output binary</li>
<li><code>CC</code>: The compiler to use.</li>
<li><code>PROG</code>: The name of the output binary</li>
<li><code>DEFINES</code>: Global preprocessor definitions</li>
<li><code>INCLUDES</code>: Header directories to make available</li>
2022-08-10 14:49:17 +00:00
<li><code>DEBUG</code>: If set to "1", append some debug flags to
<code>CFLAGS</code>, and wipe out any <code>LDFLAGS</code> that
would cause the output binary to be optimized. Also appends "-debug"
to <code>PROG</code>.</li>
2022-08-01 20:09:18 +00:00
</ul>
<code>TELODENDRIA_VERSION</code> also makes its way into the output
binary, but it is primarily used for generating releases.
</li>
<li>
2022-08-13 02:00:36 +00:00
<code>run</code>: Run a built <b>Telodendria</b> binary with the
development configuration in <code>contrib/</code>. This recipe can
be used for quick testing during development. It is <i>not</i> the
recommended way to run <b>Telodendria</b> in a production environment;
it should be used only for development.
</li>
<li>
2022-08-01 20:09:18 +00:00
<code>clean</code>: Remove the <code>build/</code> directory, which
effectively cleans the source tree. Note that <code>build</code> does
not place anything outside of <code>build/</code>, so this is
functionally equivalent to running <code>rm -r build</code>.
</li>
<li>
<code>format</code>: Make sure the source code copyright headers are
up to date, and format the source code using the system's
<code>indent(1)</code> command. This should be run before generating
patch files, to ensure that the code follows the project conventions.
Note that the provided <code>.indent.pro</code> assumes an OpenBSD
<code>indent</code>, which may cause the GNU implementation to choke.
</li>
<li>
<code>test</code>: Run all of the unit tests, and report the results.
It is highly recommended to ensure that the tests all pass before
submitting a patch, because patches that break the tests are likely
to be rejected.
</li>
<li>
<code>site</code>: Deploy the <b>Telodendria</b> website by copying the
required files to a web root. The web root is defined by the
<code>TELODENDRIA_PUB</code> environment variable. This is mainly
used to deploy the official website, but it can be used to set up a
local development site if absolutely neccessary.
</li>
<li>
<code>release</code>: Generate a release tarball, checksum and sign it,
and push it into the web root. The web root is defined by the
<code>TELODENDRIA_PUB</code> environment variable. You may also have
to set <code>CVSROOT</code> to the official CVS repository of
<b>Telodendria</b>. If you want to create a release tarball for an
older version, set <code>TELODENDRIA_VERSION</code>. If you want to
generate a release tarball for the latest code, set
<code>CVS_TAG</code> to <code>HEAD</code>. If you want to sign the
generated tarball, set <code>TELODENDRIA_SIGNIFY_SECRET</code> to a
<code>signify</code> private key file. This is mainly used to deploy
releases to the official website, but it can be used to independently
mirror <b>Telodendria</b> releases from the official CVS repository.
</li>
<li>
<code>patch</code>: Generate a formatted patch file. The
<b>Telodendria</b> project isn't super picky about how patches look
as long as they apply cleanly, but this recipe generates patches in
the format we like them. It makes them easy to read. This recipe will
use the <code>EDITOR</code> variable to open your formatted patch in
your preferred editor. If no <code>EDITOR</code> is specified, then
<code>vi</code> is used. For more details on how this recipe works,
read <a href="#submitting-patches">Submitting Patches</a>. This
recipe also makes use of <code>PATCHSET</code>, <code>MXID</code>,
and <code>DISPLAY_NAME</code>.
</li>
<li>
<code>diff</code>: Generate a temporary preview patch that is
opened in the default pager. This recipe uses the <code>PAGER</code>
variable to preview patches. If no <code>PAGER</code> is set, then
<code>less -F</code> is used as the default. This can be used for
quickly previewing patches. Setting <code>PATCHSET</code> allows you
to only preview certain changes; see
<a href="#submitting-patches">Submitting Patches</a> for more
information on how <code>PATCHSET</code> works.
</li>
</ul>
<p>
Any environment variables noted above that <code>td</code> recipes
use can be specified in a <code>.env</code> file in the root of the
<code>Telodendria</code> directory. This saves you from constantly
having to set the environment variables in your shell, as well as
from having to pollute your user environment with <b>Telodendria</b>
variables. <code>td</code> will automatically include the
<code>.env</code> file on every run.
</p>
<div class="msg-err">
<code>td</code> will source the <code>.env</code> file, which means
it executes it like a shell script. Any shell code inside of
<code>.env</code> will be executed every time <code>td</code> is
run.
</div>
2022-07-23 00:19:12 +00:00
<h4 id="submitting-patches">Submitting Patches</h4>
<p>
2022-07-28 01:24:10 +00:00
<b>Telodendria</b> aims at remaining as minimal as possible. This doesn't
just mean minimal code, it also means a minimal development process, which
is why <b>Telodendria</b> doesn't use GitHub, GitLab, or even SourceHut.
Instead, the contribution workflow operates on submitting patch files to
a public Matrix room, sort of like the OpenBSD project operates on patch
files sent to a public mailing list.
</p>
<p>
If you're not used to manually creating and submitting patches instead of
opening a "pull request," you should be pleased to hear that submitting
patches is fairly easy to do if you've got the CVS sources checked out.
In fact, I find it easier than having to make a GitHub account, forking
a project repository, and then making a pull request for it. Once you
2022-07-29 21:09:34 +00:00
have made your changes in your local copy of the code, and you've
configured your environment using the instructions in
<a href="#the-build-script">The Build Script</a>, just run the
2022-07-29 19:29:39 +00:00
<code>patch</code> recipe:
2022-07-23 00:19:12 +00:00
</p>
<div class="code">
2022-07-29 21:09:34 +00:00
$ td patch
2022-07-23 00:19:12 +00:00
</div>
<p>
2022-07-29 21:09:34 +00:00
This will automatically generate a patch file for all your changes,
2022-07-30 13:34:27 +00:00
and then open it in your preferred editor.
2022-07-29 19:29:39 +00:00
You can also generate a patch for only certain files and directories.
2022-07-29 21:09:34 +00:00
To do that, set <code>PATCHSET</code>, like this:
2022-07-29 19:29:39 +00:00
</p>
<div class="code">
# Only write a patch for README.txt and the files in site/
2022-07-29 21:09:34 +00:00
$ PATCHSET="README.txt site/" td patch
2022-07-29 19:29:39 +00:00
</div>
<p>
As you'll notice, the top of the patch file should have some email-style
headers that look like this:
2022-07-28 01:24:10 +00:00
</p>
<div class="code">
From: Jordan Bancino &lt;@jordan:bancino.net&gt;
2022-07-29 19:29:39 +00:00
Date: Fri Jul 29 03:21:21 PM EDT 2022
2022-07-28 01:24:10 +00:00
Subject: Document Patch Procedure
</div>
<p>
2022-08-01 20:09:18 +00:00
As much information as possible should be filled out for you, such as
the date. An attempt to fill out the <code>From</code> header was also
made, but the information there can be made more accurate by setting
<code>MXID</code> to your Matrix ID, and <code>DISPLAY_NAME</code> to
your real name in your environment, or the <code>.env</code> file.
The <code>Subject</code> should very briefly describe what the patch
is about. Below these headers, write a more in-depth description of
the patch.
2022-07-28 01:24:10 +00:00
</p>
<p>
Then, send the resulting patch file to
<code>#telodendria-patches:bancino.net</code>, so it can be
discussed and reviewed by the community. If you don't have a Matrix
account, and you <i>really</i> don't want to create one&mdash;ignoring
how odd it is that you are trying to contribute to a <i>Matrix</i>
homeserver project&mdash;you can email your patches to
<a href="mailto:jordan@bancino.net">jordan@bancino.net</a>. However,
the preferred way of submitting patches is to the official Matrix room,
so I will upload your patch there along with your email address. If you
are going to send patches via email, <b>they must be plain text</b> emails,
and the patch must be in the main body of the email. No MIME, base64, or
printed-quotable garbage. I will silently reject emails that are not
purely plain text. I should be able to write a raw copy of your message to
an <code>mbox</code> file, and then apply it onto my code right from
there, with no further processing. If you're going to be a regular contributor,
it would just be easier to create a Matrix account. It doesn't have to be
on my public homeserver, but it certainly can be. Note that the discussion and
ultimately the decision on what to do with your patch will all happen in
the Matrix room, so if you submit patches using email, you'll miss out on
any feedback.
</p>
<p>
Try to keep your patches on topic&mdash;make one patch file per feature
or bug fix being implemented. It is okay if your patches depend on previous
patches, just indicate that in the patch. Note that it may take a while
for patches to be committed, and some patches may not be committed at
all. In either case, all sent patches are queued from the Matrix room into the
<a href="/patches">public patch directory</a>, so they can be referenced easier
in the future. If you want to reference a submitted patch in a Matrix message
or email, it might be a good idea to link to it in the public patch directory.
</p>
<p>
2022-08-02 02:22:43 +00:00
The public patch directory works as follows: when you send your patch to the
Matrix room, it is downloaded by the <b>Telodendria</b> Bot and placed in the
<code>ingress/</code> directory, named as the message ID. Then, it is
assigned a <b>Telodendria</b> patch ID and copied to the <code>p/</code>
directory as just <code>${id}.patch</code>. This is a permanent link that will
2022-07-29 19:29:39 +00:00
always reference your patch. Then, your patch is symlinked to the
<code>queue/</code> directory. I have a script that automatically ingresses
patches and queues them for me, and I'll use this to review patches.
If your patch is accepted, the queue symlink will be moved to the
<code>accepted/</code> directory and the patch is committed to the official
CVS repository. If you patch is rejected for some reason, its symlink will
be moved to the <code>rejected/</code> directory. Regardless of the state
of your patch, it will always remain permalinked in the <code>p/</code>
2022-08-02 02:22:43 +00:00
directory, and when it is accepted or rejected, <b>Telodendria</b> Bot will
send a message.
2022-07-28 01:24:10 +00:00
</p>
<p>
You're always welcome to inquire about rejected patches, and request they be
reviewed again, or you can use them as a starting point for future patches.
2022-07-23 00:19:12 +00:00
</p>
<h2 id="license">License</h2>
<p>
2022-08-08 14:57:42 +00:00
All of the code for <b>Telodendria</b> is licensed under a modified
MIT license. Please consult the <code>src/header.txt</code> file for
the actual license text. The <b>Telodendria</b> license text differs
from the MIT license in the following ways:
2022-07-23 00:19:12 +00:00
</p>
2022-07-28 16:00:43 +00:00
<ul>
<li>
Where the MIT license states that the copyright notice and permission
notice shall be included in all copies or <i>substantial</i> portions
of the software, the <b>Telodendria</b> license requires the copyright
notice and permission notice be included in <i>all</i>
portions&mdash;regardless of size&mdash;of the software by omitting
the word <i>substantial</i>.
</li>
2022-08-09 17:22:33 +00:00
</ul>
2022-08-08 14:57:42 +00:00
<p>
The documentation for <b>Telodendria</b> is also licensed under the
modified MIT license that lives in <code>src/header.txt</code>. Whether
you obtain the documentation by printing, or otherwise downloading
this page, or by checking it out from the CVS source, it is licensed
the same as the code.
<p>
The <b>Telodendria</b> logo, however, belongs solely to the
<b>Telodendria</b> project. It must only be used to represent the
official <b>Telodendria</b> project, and may only appear in official
<b>Telodendria</b> media. If <b>Telodendria</b> is forked, the logo
must be removed from the project and optionally replaced by a different
one. The logo may not be modified in any way or for any purpose.
</p>
2022-07-25 20:00:52 +00:00
<h2 id="contributors">Contributors</h2>
<p>
<b>Telodendria</b> would not be possible without the support of the
2022-07-25 20:00:52 +00:00
following people:
</p>
<ul>
<li>
<b>Project Manager:</b>
Jordan Bancino &lt;@jordan:bancino.net&gt;
</li>
<li>
<b>Logo/site design:</b>
Jonah
</li>
2022-07-25 20:00:52 +00:00
</ul>
2022-07-23 00:19:12 +00:00
<h2 id="change-log">Change Log</h2>
<p>
At this time, Telodendria does not have any tagged releases because it
is not yet functional as a Matrix homeserver. Please check out the <a
href="#project-status">Project Status</a> to see where things are
currently at.
</p>
2022-07-23 21:29:54 +00:00
<h2 id="resources">Resources</h2>
<ul>
2022-08-02 19:15:19 +00:00
<li><a target="_blank" href="/pub">Old <b>Telodendria</b> Versions</a></li>
2022-08-03 02:21:00 +00:00
<li><a target="_blank" href="/spec.matrix.org">Matrix Spec Mirror</a>
(<a href="/matrix-spec-v1.3.tar.gz">Download matrix-spec-v1.3.tar.gz</a>)
</li>
2022-08-02 19:15:19 +00:00
<li><a target="_blank" href="/patches">Public Patch Directory</a></li>
2022-07-23 21:29:54 +00:00
</ul>
2022-07-23 23:18:17 +00:00
<hr>
&copy; 2022 Jordan Bancino &lt;@jordan:bancino.net&gt;
2022-07-23 00:19:12 +00:00
</body>
</html>