summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--bunblog.cabal11
-rw-r--r--fonts/Redaction-Bold.woff2bin0 -> 29684 bytes
-rw-r--r--fonts/Redaction-Italic.woff2bin0 -> 29212 bytes
-rw-r--r--fonts/Redaction-Regular.woff2bin0 -> 28392 bytes
-rw-r--r--fonts/Redaction_10-Bold.woff2bin0 -> 127192 bytes
-rw-r--r--fonts/Redaction_10-Italic.woff2bin0 -> 152500 bytes
-rw-r--r--fonts/Redaction_10-Regular.woff2bin0 -> 126552 bytes
-rw-r--r--fonts/Redaction_100-Bold.woff2bin0 -> 20052 bytes
-rw-r--r--fonts/Redaction_100-Italic.woff2bin0 -> 20488 bytes
-rw-r--r--fonts/Redaction_100-Regular.woff2bin0 -> 19504 bytes
-rw-r--r--fonts/Redaction_20-Bold.woff2bin0 -> 68320 bytes
-rw-r--r--fonts/Redaction_20-Italic.woff2bin0 -> 78536 bytes
-rw-r--r--fonts/Redaction_20-Regular.woff2bin0 -> 68996 bytes
-rw-r--r--fonts/Redaction_35-Bold.woff2bin0 -> 42464 bytes
-rw-r--r--fonts/Redaction_35-Italic.woff2bin0 -> 50260 bytes
-rw-r--r--fonts/Redaction_35-Regular.woff2bin0 -> 38876 bytes
-rw-r--r--fonts/Redaction_50-Bold.woff2bin0 -> 33336 bytes
-rw-r--r--fonts/Redaction_50-Italic.woff2bin0 -> 35340 bytes
-rw-r--r--fonts/Redaction_50-Regular.woff2bin0 -> 32972 bytes
-rw-r--r--fonts/Redaction_70-Bold.woff2bin0 -> 26424 bytes
-rw-r--r--fonts/Redaction_70-Italic.woff2bin0 -> 30760 bytes
-rw-r--r--fonts/Redaction_70-Regular.woff2bin0 -> 25064 bytes
-rw-r--r--fonts/VictorMono-Bold.woff2bin0 -> 71928 bytes
-rw-r--r--fonts/VictorMono-BoldItalic.woff2bin0 -> 89444 bytes
-rw-r--r--fonts/VictorMono-BoldOblique.woff2bin0 -> 78688 bytes
-rw-r--r--fonts/VictorMono-ExtraLight.woff2bin0 -> 69540 bytes
-rw-r--r--fonts/VictorMono-ExtraLightItalic.woff2bin0 -> 85776 bytes
-rw-r--r--fonts/VictorMono-ExtraLightOblique.woff2bin0 -> 75976 bytes
-rw-r--r--fonts/VictorMono-Italic.woff2bin0 -> 86276 bytes
-rw-r--r--fonts/VictorMono-Light.woff2bin0 -> 70164 bytes
-rw-r--r--fonts/VictorMono-LightItalic.woff2bin0 -> 86268 bytes
-rw-r--r--fonts/VictorMono-LightOblique.woff2bin0 -> 75656 bytes
-rw-r--r--fonts/VictorMono-Medium.woff2bin0 -> 69908 bytes
-rw-r--r--fonts/VictorMono-MediumItalic.woff2bin0 -> 87432 bytes
-rw-r--r--fonts/VictorMono-MediumOblique.woff2bin0 -> 76148 bytes
-rw-r--r--fonts/VictorMono-Oblique.woff2bin0 -> 75792 bytes
-rw-r--r--fonts/VictorMono-Regular.woff2bin0 -> 70852 bytes
-rw-r--r--fonts/VictorMono-SemiBold.woff2bin0 -> 68872 bytes
-rw-r--r--fonts/VictorMono-SemiBoldItalic.woff2bin0 -> 85268 bytes
-rw-r--r--fonts/VictorMono-SemiBoldOblique.woff2bin0 -> 75200 bytes
-rw-r--r--fonts/VictorMono-Thin.woff2bin0 -> 69816 bytes
-rw-r--r--fonts/VictorMono-ThinItalic.woff2bin0 -> 85996 bytes
-rw-r--r--fonts/VictorMono-ThinOblique.woff2bin0 -> 75896 bytes
-rw-r--r--images/atombadge.pngbin0 -> 4767 bytes
-rw-r--r--images/background.jpgbin0 -> 284594 bytes
-rw-r--r--images/cel.jpgbin0 -> 707625 bytes
-rw-r--r--index.html16
-rw-r--r--posts/2023-09-16-ejabberd.md504
-rw-r--r--site.hs67
-rw-r--r--style.css458
-rw-r--r--templates/default.html30
-rw-r--r--templates/post-list.html7
-rw-r--r--templates/post.html13
54 files changed, 1109 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..633c358
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+_*
+dist-newstyle
+
diff --git a/bunblog.cabal b/bunblog.cabal
new file mode 100644
index 0000000..be2a04c
--- /dev/null
+++ b/bunblog.cabal
@@ -0,0 +1,11 @@
+name: bunblog
+version: 0.1.0.0
+build-type: Simple
+cabal-version: >= 1.10
+
+executable site
+ main-is: site.hs
+ build-depends: base == 4.*
+ , hakyll == 4.16.*
+ ghc-options: -threaded -rtsopts -with-rtsopts=-N
+ default-language: Haskell2010
diff --git a/fonts/Redaction-Bold.woff2 b/fonts/Redaction-Bold.woff2
new file mode 100644
index 0000000..392d60d
--- /dev/null
+++ b/fonts/Redaction-Bold.woff2
Binary files differ
diff --git a/fonts/Redaction-Italic.woff2 b/fonts/Redaction-Italic.woff2
new file mode 100644
index 0000000..1bd3c5c
--- /dev/null
+++ b/fonts/Redaction-Italic.woff2
Binary files differ
diff --git a/fonts/Redaction-Regular.woff2 b/fonts/Redaction-Regular.woff2
new file mode 100644
index 0000000..d187654
--- /dev/null
+++ b/fonts/Redaction-Regular.woff2
Binary files differ
diff --git a/fonts/Redaction_10-Bold.woff2 b/fonts/Redaction_10-Bold.woff2
new file mode 100644
index 0000000..c6bf1b1
--- /dev/null
+++ b/fonts/Redaction_10-Bold.woff2
Binary files differ
diff --git a/fonts/Redaction_10-Italic.woff2 b/fonts/Redaction_10-Italic.woff2
new file mode 100644
index 0000000..1746de1
--- /dev/null
+++ b/fonts/Redaction_10-Italic.woff2
Binary files differ
diff --git a/fonts/Redaction_10-Regular.woff2 b/fonts/Redaction_10-Regular.woff2
new file mode 100644
index 0000000..0095a6a
--- /dev/null
+++ b/fonts/Redaction_10-Regular.woff2
Binary files differ
diff --git a/fonts/Redaction_100-Bold.woff2 b/fonts/Redaction_100-Bold.woff2
new file mode 100644
index 0000000..71e0a10
--- /dev/null
+++ b/fonts/Redaction_100-Bold.woff2
Binary files differ
diff --git a/fonts/Redaction_100-Italic.woff2 b/fonts/Redaction_100-Italic.woff2
new file mode 100644
index 0000000..706d8ad
--- /dev/null
+++ b/fonts/Redaction_100-Italic.woff2
Binary files differ
diff --git a/fonts/Redaction_100-Regular.woff2 b/fonts/Redaction_100-Regular.woff2
new file mode 100644
index 0000000..cdd97e8
--- /dev/null
+++ b/fonts/Redaction_100-Regular.woff2
Binary files differ
diff --git a/fonts/Redaction_20-Bold.woff2 b/fonts/Redaction_20-Bold.woff2
new file mode 100644
index 0000000..c4e29e5
--- /dev/null
+++ b/fonts/Redaction_20-Bold.woff2
Binary files differ
diff --git a/fonts/Redaction_20-Italic.woff2 b/fonts/Redaction_20-Italic.woff2
new file mode 100644
index 0000000..72fc206
--- /dev/null
+++ b/fonts/Redaction_20-Italic.woff2
Binary files differ
diff --git a/fonts/Redaction_20-Regular.woff2 b/fonts/Redaction_20-Regular.woff2
new file mode 100644
index 0000000..adbe270
--- /dev/null
+++ b/fonts/Redaction_20-Regular.woff2
Binary files differ
diff --git a/fonts/Redaction_35-Bold.woff2 b/fonts/Redaction_35-Bold.woff2
new file mode 100644
index 0000000..737e8be
--- /dev/null
+++ b/fonts/Redaction_35-Bold.woff2
Binary files differ
diff --git a/fonts/Redaction_35-Italic.woff2 b/fonts/Redaction_35-Italic.woff2
new file mode 100644
index 0000000..18e54ff
--- /dev/null
+++ b/fonts/Redaction_35-Italic.woff2
Binary files differ
diff --git a/fonts/Redaction_35-Regular.woff2 b/fonts/Redaction_35-Regular.woff2
new file mode 100644
index 0000000..52a9ff6
--- /dev/null
+++ b/fonts/Redaction_35-Regular.woff2
Binary files differ
diff --git a/fonts/Redaction_50-Bold.woff2 b/fonts/Redaction_50-Bold.woff2
new file mode 100644
index 0000000..af22835
--- /dev/null
+++ b/fonts/Redaction_50-Bold.woff2
Binary files differ
diff --git a/fonts/Redaction_50-Italic.woff2 b/fonts/Redaction_50-Italic.woff2
new file mode 100644
index 0000000..13e52f0
--- /dev/null
+++ b/fonts/Redaction_50-Italic.woff2
Binary files differ
diff --git a/fonts/Redaction_50-Regular.woff2 b/fonts/Redaction_50-Regular.woff2
new file mode 100644
index 0000000..26ea348
--- /dev/null
+++ b/fonts/Redaction_50-Regular.woff2
Binary files differ
diff --git a/fonts/Redaction_70-Bold.woff2 b/fonts/Redaction_70-Bold.woff2
new file mode 100644
index 0000000..929a599
--- /dev/null
+++ b/fonts/Redaction_70-Bold.woff2
Binary files differ
diff --git a/fonts/Redaction_70-Italic.woff2 b/fonts/Redaction_70-Italic.woff2
new file mode 100644
index 0000000..e97449d
--- /dev/null
+++ b/fonts/Redaction_70-Italic.woff2
Binary files differ
diff --git a/fonts/Redaction_70-Regular.woff2 b/fonts/Redaction_70-Regular.woff2
new file mode 100644
index 0000000..4ccafe4
--- /dev/null
+++ b/fonts/Redaction_70-Regular.woff2
Binary files differ
diff --git a/fonts/VictorMono-Bold.woff2 b/fonts/VictorMono-Bold.woff2
new file mode 100644
index 0000000..7249c2c
--- /dev/null
+++ b/fonts/VictorMono-Bold.woff2
Binary files differ
diff --git a/fonts/VictorMono-BoldItalic.woff2 b/fonts/VictorMono-BoldItalic.woff2
new file mode 100644
index 0000000..0fa7a11
--- /dev/null
+++ b/fonts/VictorMono-BoldItalic.woff2
Binary files differ
diff --git a/fonts/VictorMono-BoldOblique.woff2 b/fonts/VictorMono-BoldOblique.woff2
new file mode 100644
index 0000000..827c36c
--- /dev/null
+++ b/fonts/VictorMono-BoldOblique.woff2
Binary files differ
diff --git a/fonts/VictorMono-ExtraLight.woff2 b/fonts/VictorMono-ExtraLight.woff2
new file mode 100644
index 0000000..a2ac7ea
--- /dev/null
+++ b/fonts/VictorMono-ExtraLight.woff2
Binary files differ
diff --git a/fonts/VictorMono-ExtraLightItalic.woff2 b/fonts/VictorMono-ExtraLightItalic.woff2
new file mode 100644
index 0000000..4791649
--- /dev/null
+++ b/fonts/VictorMono-ExtraLightItalic.woff2
Binary files differ
diff --git a/fonts/VictorMono-ExtraLightOblique.woff2 b/fonts/VictorMono-ExtraLightOblique.woff2
new file mode 100644
index 0000000..814cb1d
--- /dev/null
+++ b/fonts/VictorMono-ExtraLightOblique.woff2
Binary files differ
diff --git a/fonts/VictorMono-Italic.woff2 b/fonts/VictorMono-Italic.woff2
new file mode 100644
index 0000000..b4aa39a
--- /dev/null
+++ b/fonts/VictorMono-Italic.woff2
Binary files differ
diff --git a/fonts/VictorMono-Light.woff2 b/fonts/VictorMono-Light.woff2
new file mode 100644
index 0000000..b059857
--- /dev/null
+++ b/fonts/VictorMono-Light.woff2
Binary files differ
diff --git a/fonts/VictorMono-LightItalic.woff2 b/fonts/VictorMono-LightItalic.woff2
new file mode 100644
index 0000000..5101bd2
--- /dev/null
+++ b/fonts/VictorMono-LightItalic.woff2
Binary files differ
diff --git a/fonts/VictorMono-LightOblique.woff2 b/fonts/VictorMono-LightOblique.woff2
new file mode 100644
index 0000000..98eb08c
--- /dev/null
+++ b/fonts/VictorMono-LightOblique.woff2
Binary files differ
diff --git a/fonts/VictorMono-Medium.woff2 b/fonts/VictorMono-Medium.woff2
new file mode 100644
index 0000000..e712bc0
--- /dev/null
+++ b/fonts/VictorMono-Medium.woff2
Binary files differ
diff --git a/fonts/VictorMono-MediumItalic.woff2 b/fonts/VictorMono-MediumItalic.woff2
new file mode 100644
index 0000000..7a489d7
--- /dev/null
+++ b/fonts/VictorMono-MediumItalic.woff2
Binary files differ
diff --git a/fonts/VictorMono-MediumOblique.woff2 b/fonts/VictorMono-MediumOblique.woff2
new file mode 100644
index 0000000..98797cb
--- /dev/null
+++ b/fonts/VictorMono-MediumOblique.woff2
Binary files differ
diff --git a/fonts/VictorMono-Oblique.woff2 b/fonts/VictorMono-Oblique.woff2
new file mode 100644
index 0000000..caef3a8
--- /dev/null
+++ b/fonts/VictorMono-Oblique.woff2
Binary files differ
diff --git a/fonts/VictorMono-Regular.woff2 b/fonts/VictorMono-Regular.woff2
new file mode 100644
index 0000000..63af0d4
--- /dev/null
+++ b/fonts/VictorMono-Regular.woff2
Binary files differ
diff --git a/fonts/VictorMono-SemiBold.woff2 b/fonts/VictorMono-SemiBold.woff2
new file mode 100644
index 0000000..d520338
--- /dev/null
+++ b/fonts/VictorMono-SemiBold.woff2
Binary files differ
diff --git a/fonts/VictorMono-SemiBoldItalic.woff2 b/fonts/VictorMono-SemiBoldItalic.woff2
new file mode 100644
index 0000000..3a8970e
--- /dev/null
+++ b/fonts/VictorMono-SemiBoldItalic.woff2
Binary files differ
diff --git a/fonts/VictorMono-SemiBoldOblique.woff2 b/fonts/VictorMono-SemiBoldOblique.woff2
new file mode 100644
index 0000000..8409aa1
--- /dev/null
+++ b/fonts/VictorMono-SemiBoldOblique.woff2
Binary files differ
diff --git a/fonts/VictorMono-Thin.woff2 b/fonts/VictorMono-Thin.woff2
new file mode 100644
index 0000000..58c3309
--- /dev/null
+++ b/fonts/VictorMono-Thin.woff2
Binary files differ
diff --git a/fonts/VictorMono-ThinItalic.woff2 b/fonts/VictorMono-ThinItalic.woff2
new file mode 100644
index 0000000..c1ab8fd
--- /dev/null
+++ b/fonts/VictorMono-ThinItalic.woff2
Binary files differ
diff --git a/fonts/VictorMono-ThinOblique.woff2 b/fonts/VictorMono-ThinOblique.woff2
new file mode 100644
index 0000000..39fb9d3
--- /dev/null
+++ b/fonts/VictorMono-ThinOblique.woff2
Binary files differ
diff --git a/images/atombadge.png b/images/atombadge.png
new file mode 100644
index 0000000..db1cabf
--- /dev/null
+++ b/images/atombadge.png
Binary files differ
diff --git a/images/background.jpg b/images/background.jpg
new file mode 100644
index 0000000..bd9aedf
--- /dev/null
+++ b/images/background.jpg
Binary files differ
diff --git a/images/cel.jpg b/images/cel.jpg
new file mode 100644
index 0000000..910bf6d
--- /dev/null
+++ b/images/cel.jpg
Binary files differ
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..a03ba6a
--- /dev/null
+++ b/index.html
@@ -0,0 +1,16 @@
+---
+title: home
+---
+
+<div style="float: right; margin: 10px; width: 40%;">
+ <img src="/images/cel.jpg" style="width: 100%;" />
+ <p>when i computer</p>
+</div>
+<h2>
+ this is the dev?blog of celeste <span style="font-style: normal;">❀ (花春雷)</span></h2>
+<p>welcome! everything to do with technology is banished to here.</p>
+<p>email: username@domain | git: <a href="https://bunny.garden">bunny.garden</a></p>
+
+<h2>posts: <a class="small-badge" href="/feed.xml"><img class="small-badge" src="/images/atombadge.png"
+ alt="atom newsfeed"></a></h2>
+$partial("templates/post-list.html")$ \ No newline at end of file
diff --git a/posts/2023-09-16-ejabberd.md b/posts/2023-09-16-ejabberd.md
new file mode 100644
index 0000000..73ee4e5
--- /dev/null
+++ b/posts/2023-09-16-ejabberd.md
@@ -0,0 +1,504 @@
+---
+title: ejabberd server guide
+---
+
+Prerequisites:
+
+- A domain name
+- A server
+
+This tutorial begins with a single virtualhost[^1]. For example purposes, `example.net` is the domain. Replace all instances of `example.net` with your own domain.
+
+[^1]: Virtualhosts allow more than one XMPP service to be run on one server. For example, one XMPP service with the domain `example.net` and another XMPP service with the domain `example.org`, both somewhat separated from each other as if they were run on different servers.
+
+<div><div class="panel checklist">checklist!: <input type="checkbox" id="hide-checklist"><label for="hide-checklist">hide</label><div>
+
+- [ ] add dns records
+- [ ] open firewall ports
+- [ ] set up web server
+- [ ] get ssl certificates
+- [ ] install ejabberd
+- [ ] set up postgres database
+- [ ] write/edit configuration
+- [ ] start service
+- [ ] create admin user
+- [ ] change loglevel
+
+</div></div>
+
+## Step 1: Set up the DNS records
+
+DNS A/AAAA records and SRV records are required for each service on the XMPP server.
+
+A records:
+
+- `example.net`
+- `muc.example.net` (for group chats)
+- `upload.example.net` (for HTTP file upload)
+- `pubsub.example.net` (for the pubsub node)
+- `proxy.example.net` (for file transfer proxy)
+- `turn.example.net` (for STUN/TURN)
+
+Each pointing to the IP address of the server that is going to run Ejabberd.
+
+Create SRV records for each, pointing to a domain that resolves to the server, as so:
+
+(All records are in the form
+`_service._proto.name IN SRV priority weight port target`)
+
+```
+_xmpp-client._tcp IN SRV 5 0 5222 example.net.
+_xmpps-client._tcp IN SRV 5 0 5223 example.net.
+_xmpp-server._tcp IN SRV 5 0 5269 example.net.
+_xmpps-server._tcp IN SRV 5 0 5270 example.net.
+```
+
+and
+
+```
+_xmpp-client._tcp.muc IN SRV 5 0 5222 example.net.
+_xmpps-client._tcp.muc IN SRV 5 0 5223 example.net.
+_xmpp-server._tcp.muc IN SRV 5 0 5269 example.net.
+_xmpps-server._tcp.muc IN SRV 5 0 5270 example.net.
+```
+
+for each of the subdomains (starting with `muc`). Exclude `turn.example.net`.
+
+Lastly, add one set of SRV records, for STUN/TURN.
+
+```
+_stun._udp IN SRV 5 0 3478 turn.example.net.
+_stun._tcp IN SRV 5 0 3478 turn.example.net.
+_stuns._tcp IN SRV 5 0 5349 turn.example.net.
+
+_turn._udp IN SRV 5 0 3478 turn.example.net.
+_turn._tcp IN SRV 5 0 3478 turn.example.net.
+_turns._tcp IN SRV 5 0 5349 turn.example.net.
+```
+
+Extra info: as a result of these SRV delegation records, hosting XMPP on a server other than the one at `example.net` is an option (i.e. if splitting services on one domain across servers). Further info can be found at [XEP-0368](https://xmpp.org/extensions/xep-0368.html).
+
+## Step 2: Open the firewall ports
+
+Open ports:
+
+TCP: `80` `443` `5222` `5223` `5269` `5270` `3478` `5349` `49152-65535`
+
+UDP: `3478` `49152-65535`
+
+`80` & `443` are for the web server, `5222`, `5223`, `5269` and `5270` are for XMPP, and the rest are for STUN/TURN.
+
+## Step 3: Set up the web server and get your SSL certificates
+
+- get (an) SSL certificate(s) for your domain, as well as several subdomains, all in all:
+ - `example.net`
+ - `muc.example.net`
+ - `upload.example.net`
+ - `pubsub.example.net`
+ - `proxy.example.net`
+ - `turn.example.net`
+- proxypass http://127.0.0.1:5443 through to:
+ - https://example.net/xmpp
+ - https://example.net/.well-known/host-meta
+ - https://example.net/.well-known/host-meta.json
+
+ Make sure that for /xmpp you have what is necessary to proxy websockets too. If you're using Nginx, increase `client_max_body_size` for HTTP uploads.
+- make sure the certificate files are readable and/or in a place that is readable by the `ejabberd` user.
+
+To avoid using something like Nginx + Certbot, use the built-in [Ejabberd acme module](https://docs.ejabberd.im/admin/configuration/basic/#acme), but this article assumes it is desired to host other web services on the same system, in which case each HTTP service would be reverse proxied to a single HTTPS web service.
+
+## Step 4: Install Ejabberd
+
+Finally install the [system package](https://docs.ejabberd.im/admin/installation/#operating-system-packages). Ensure the build has PostgreSQL support.
+
+Confirm the file `/etc/ejabberd/ejabberd.yml` exists, and is readable by the user that runs Ejabberd (almost definitely `ejabberd`), by, if necessary, copying over the example `ejabberd.yml` or `wget`/`curl`ing it from the [github repo](https://github.com/processone/ejabberd/blob/master/ejabberd.yml.example). If it is obtained from the repo, make sure the version corresponds to the version of Ejabberd packaged by your OS.
+
+## Step 5: Set up PostgreSQL database
+
+A separate database should be created for each virtualhost, as this makes things clearer, and in addition, easier to migrate individual virtualhosts in the future. However, the ability to use only one, as described [here](https://docs.ejabberd.im/admin/configuration/database/#default-and-new-schemas), now exists as well.
+
+Follow standard PostgreSQL installation instructions for your OS. Once this has been done, connect to the database as an admin and:
+
+1. create an Ejabberd database user with `CREATE USER ejabberd WITH PASSWORD 'your_password';`. Don't forget to change the password, and note it down.
+2. create a database for the virtualhost with `CREATE DATABASE ejabberd_example OWNER ejabberd;`. Replace `example` with something corresponding to the virtualhost.
+3. quit the `psql` shell, and import the database schema from GitHub with the command `curl -s https://raw.githubusercontent.com/processone/ejabberd/master/sql/pg.sql | sudo -u ejabberd psql ejabberd_example` (once again replace `example`).
+
+## Step 6: Ejabberd configuration
+
+Begin by replacing `localhost` under `hosts` with the virtualhost (e.g. `example.net`), then list the certfiles previously obtained under `certfiles`.
+
+```
+hosts:
+ - example.net
+
+certfiles:
+ - "/etc/ejabberd/certs/*/*"
+```
+
+Now set `default_db: sql` at the root level of the YAML file. This should be followed by `host_config` and the database configuration for your virtualhost, as shown below. Customise each value to according to the setup.
+
+```
+host_config:
+ example.net:
+ sql_type: pgsql
+ sql_server: "localhost"
+ sql_port: 5432
+ sql_username: "ejabberd"
+ sql_password: "postgres_password"
+ sql_database: "ejabberd_example"
+ auth_method: sql
+ auth_password_format: scram
+```
+
+Under `listen`, ensure all the correct services are enabled on each port, including s2s TLS on port `5270` (not default):
+
+```
+listen:
+ -
+ port: 5222
+ module: ejabberd_c2s
+ max_stanza_size: 262144
+ shaper: c2s_shaper
+ access: c2s
+ starttls_required: true
+ -
+ port: 5223
+ tls: true
+ module: ejabberd_c2s
+ max_stanza_size: 262144
+ shaper: c2s_shaper
+ access: c2s
+ starttls_required: true
+ -
+ port: 5269
+ module: ejabberd_s2s_in
+ max_stanza_size: 524288
+ -
+ port: 5270
+ tls: true
+ module: ejabberd_s2s_in
+ max_stanza_size: 524288
+```
+
+Next, enable the HTTP server and the STUN/TURN server modules. Set `turn_ipv4_address` and `ip` to the server's IPv4 address. TLS will be off for the HTTP server as it is reverse proxied through the previously set-up web server.
+
+```
+ -
+ port: 5443
+ module: ejabberd_http
+ request_handlers:
+ /xmpp/admin: ejabberd_web_admin
+ /xmpp/bosh: mod_bosh
+ /xmpp/upload: mod_http_upload
+ /xmpp/ws: ejabberd_http_ws
+ /.well-known/host-meta: mod_host_meta
+ /.well-known/host-meta.json: mod_host_meta
+ -
+ port: 3478
+ transport: udp
+ module: ejabberd_stun
+ use_turn: true
+ turn_min_port: 49152
+ turn_max_port: 65535
+ # The server's public IPv4 address:
+ turn_ipv4_address: 0.0.0.0
+ -
+ port: 5349
+ transport: tcp
+ module: ejabberd_stun
+ use_turn: true
+ tls: true
+ turn_min_port: 49152
+ turn_max_port: 65535
+ ip: 0.0.0.0
+ turn_ipv4_address: 0.0.0.0
+```
+
+Set `s2s_use_starttls: required` at the root.
+
+At this point it is possible to set up some ACLs. `acls` are just the access control lists, set up `access_rules` corresponding to your needs, which will be what are passed to module settings. At the minimum an admin user is recommended. Example:
+
+```
+acl:
+ admin:
+ user: juliet@example.net
+ capulet:
+ - user: juliet@example.net
+ - user: tybalt@example.net
+ nurse:
+ - user: angelica@example.net
+
+access_rules:
+ household:
+ allow: capulet
+ allow: nurse
+```
+
+### Modules
+
+Add abuse addresses under `mod_disco`. It is also possible to add other contact addresses according to [XEP-0157](https://xmpp.org/extensions/xep-0157.html):
+
+```
+modules:
+# ...
+ mod_disco:
+ server_info:
+ -
+ modules: all
+ name: "abuse-addresses"
+ urls: ["mailto:abuse@example.net"]
+```
+
+Add `mod_host_meta`:
+
+```
+ mod_host_meta:
+ bosh_service_url: "https://@HOST@/xmpp/bosh"
+ websocket_url: "wss://@HOST@/xmpp/ws"
+```
+
+Edit `mod_mam`, and change `assume_mam_usage` to `false` and `default` to `never` if it is not desireable to default to archiving messages on the server:
+
+```
+ mod_mam:
+ db_type: sql
+ assume_mam_usage: never
+ default: never
+```
+
+Add `mod_stun_disco` to advertise the STUN service to clients, changing `0.0.0.0` and `example.net` to the server's IP and hostname respectively:
+
+```
+ mod_stun_disco:
+ credentials_lifetime: 12h
+ services:
+ -
+ host: 0.0.0.0
+ port: 3478
+ type: stun
+ transport: udp
+ restricted: false
+ -
+ host: 0.0.0.0
+ port: 3478
+ type: turn
+ transport: udp
+ restricted: true
+ -
+ host: turn.example.net
+ port: 5349
+ type: stuns
+ transport: tcp
+ restricted: false
+ -
+ host: turn.example.net
+ port: 5349
+ type: turns
+ transport: tcp
+ restricted: true
+```
+
+#### MUCs:
+
+Set the host to the `muc` subdomain, otherwise it will attempt to use `conference.example.net`. Setting `mam: false` in `default_room_options` will disable server-side message archiving by default.
+
+```
+ mod_muc:
+ host: muc.example.net
+ access:
+ - allow
+ access_admin:
+ - allow: admin
+ access_create: muc_create
+ access_persistent: muc_create
+ access_mam:
+ - allow
+ default_room_options:
+ mam: false
+```
+
+#### File proxy:
+
+```
+ mod_proxy65:
+ access: local
+ max_connections: 5
+```
+
+#### HTTP file upload:
+
+```
+ mod_http_upload:
+ put_url: https://@HOST@/xmpp/upload
+ docroot: /var/www/ejabberdupload
+ max_size: 1073741824
+ custom_headers:
+ "Access-Control-Allow-Origin": "https://@HOST@"
+ "Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
+ "Access-Control-Allow-Headers": "Content-Type"
+```
+
+Create the folder for the `docroot`, and ensure it is owned by the `ejabberd` user. Change `max_size` (the max upload size) to whatever is preferred.
+
+#### PubSub:
+
+```
+ mod_pubsub:
+ access_createnode: pubsub_createnode
+ plugins:
+ - flat
+ - pep
+ force_node_config:
+ ## Avoid buggy clients to make their bookmarks public
+ storage:bookmarks:
+ access_model: whitelist
+```
+
+## Step 7: Start server and create admin user
+
+Start the Ejabberd server!
+
+Use `sudo -u ejabberd ejabberdctl register admin example.net password` to register `admin@example.net` with the password `password`.
+
+There is a compliance tester at [compliance.conversations.im](https://compliance.conversations.im) to test the server. After everything has been set up correctly, optionally change the [`loglevel`](https://docs.ejabberd.im/admin/configuration/toplevel/#loglevel) at the root of the config.
+
+There will be an admin page accessible at [https://example.net/xmpp/admin](https://example.net/xmpp/admin).
+
+# Extra goodies!
+
+## Web client
+
+It is possible to set up conversejs using [`mod_conversejs`](https://docs.ejabberd.im/admin/configuration/modules/#mod-conversejs). It may be required to update the web server config to proxy the new endpoint (`/chat` below).
+
+```
+listen:
+ -
+ port: 5443
+ module: ejabberd_http
+ request_handlers:
+ /xmpp/bosh: mod_bosh
+ /xmpp/ws: ejabberd_http_ws
+ /chat: mod_conversejs
+
+modules:
+ mod_conversejs:
+ websocket_url: "ws://@HOST@/xmpp/ws"
+ bosh_service_url: "https://@HOST@/xmpp/bosh"
+```
+
+## Further virtualhosts
+
+For each further virtualhost a new database should be created, and added to the database part of the config. e.g.:
+
+```
+host_config:
+ example.net:
+ sql_type: pgsql
+ sql_server: "localhost"
+ sql_port: 5432
+ sql_username: "ejabberd"
+ sql_password: "postgres_password"
+ sql_database: "ejabberd_net"
+ auth_method: sql
+ auth_password_format: scram
+ example.org:
+ sql_type: pgsql
+ sql_server: "localhost"
+ sql_port: 5432
+ sql_username: "ejabberd"
+ sql_password: "postgres_password"
+ sql_database: "ejabberd_org"
+ auth_method: sql
+ auth_password_format: scram
+```
+
+There cannot be conflicts between declarations in the config file, so if `mod_muc`, `mod_proxy65`, `mod_http_upload` and `mod_pubsub` are declared under `modules` at the root, they (as well as other configuration differences between virtualhosts) must be deleted and replicated for each virtualhost under `append_host_config`, at the root. Example as so:
+
+```
+append_host_config:
+ example.org:
+ modules:
+ mod_muc:
+ host: muc.example.org
+ access_create: org_users
+ access_persistent: org_users
+ access:
+ - allow
+ access_admin:
+ - allow: admin
+ default_room_options:
+ mam: false
+ mod_proxy65:
+ access: org_users
+ max_connections: 5
+ mod_http_upload:
+ access: org_users
+ put_url: https://@HOST@/xmpp/upload
+ docroot: /var/www/ejabberdupload
+ max_size: 1073741824
+ custom_headers:
+ "Access-Control-Allow-Origin": "https://@HOST@"
+ "Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
+ "Access-Control-Allow-Headers": "Content-Type"
+ mod_pubsub:
+ access_createnode: org_users
+ plugins:
+ - flat
+ - pep
+ force_node_config:
+ ## Avoid buggy clients to make their bookmarks public
+ storage:bookmarks:
+ access_model: whitelist
+ example.net:
+ modules:
+ mod_muc:
+ hosts:
+ - muc.example.net
+ access_create: net_users
+ access_persistent: net_users
+ access:
+ - allow
+ access_admin:
+ - allow: admin
+ default_room_options:
+ mam: false
+ mod_proxy65:
+ access: net_users
+ max_connections: 5
+ mod_http_upload:
+ access: net_users
+ put_url: https://@HOST@/xmpp/upload
+ docroot: /var/www/ejabberdupload
+ max_size: 1073741824
+ custom_headers:
+ "Access-Control-Allow-Origin": "https://@HOST@"
+ "Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
+ "Access-Control-Allow-Headers": "Content-Type"
+ mod_pubsub:
+ access_createnode: net_users
+ plugins:
+ - flat
+ - pep
+ force_node_config:
+ ## Avoid buggy clients to make their bookmarks public
+ storage:bookmarks:
+ access_model: whitelist
+```
+
+As above, it is possible to disable access to certain services per virtualhost using ACLs, in order to e.g. prevent users on `example.net` from creating MUCs on `muc.example.org`.
+
+## Separate TURN server (Coturn)
+
+In this case, change `mod_stun_disco` to this, and don't enable the `listen` opts for STUN/TURN. Generate an auth secret and share it with the TURN server instance.
+
+```
+ mod_stun_disco:
+ secret: "auth_secret"
+ services:
+ -
+ host: turn.example.net
+ type: stun
+ -
+ host: turn.example.net
+ type: turn
+```
diff --git a/site.hs b/site.hs
new file mode 100644
index 0000000..0313066
--- /dev/null
+++ b/site.hs
@@ -0,0 +1,67 @@
+--------------------------------------------------------------------------------
+{-# LANGUAGE OverloadedStrings #-}
+import Data.Monoid (mappend)
+import Hakyll
+
+
+--------------------------------------------------------------------------------
+main :: IO ()
+main = hakyll $ do
+ create ["feed.xml"] $ do
+ route idRoute
+ compile $ do
+ let feedCtx = postCtx `mappend` bodyField "description"
+ posts <- fmap (take 10) . recentFirst =<< loadAllSnapshots "posts/*" "content"
+ renderAtom feedConfig feedCtx posts
+
+ match "images/*" $ do
+ route idRoute
+ compile copyFileCompiler
+
+ match "fonts/*" $ do
+ route idRoute
+ compile copyFileCompiler
+
+ match "style.css" $ do
+ route idRoute
+ compile compressCssCompiler
+
+ match "posts/*" $ do
+ route $ setExtension "html"
+ compile $ pandocCompiler
+ >>= loadAndApplyTemplate "templates/post.html" postCtx
+ >>= saveSnapshot "content"
+ >>= loadAndApplyTemplate "templates/default.html" postCtx
+ >>= relativizeUrls
+
+
+ match "index.html" $ do
+ route idRoute
+ compile $ do
+ posts <- recentFirst =<< loadAll "posts/*"
+ let indexCtx =
+ listField "posts" postCtx (return posts) `mappend`
+ defaultContext
+
+ getResourceBody
+ >>= applyAsTemplate indexCtx
+ >>= loadAndApplyTemplate "templates/default.html" indexCtx
+ >>= relativizeUrls
+
+ match "templates/*" $ compile templateBodyCompiler
+
+
+--------------------------------------------------------------------------------
+postCtx :: Context String
+postCtx =
+ dateField "date" "%Y-%m-%d" `mappend`
+ defaultContext
+
+feedConfig :: FeedConfiguration
+feedConfig = FeedConfiguration
+ { feedTitle = "cel's dev?blog"
+ , feedDescription = "banished technology"
+ , feedAuthorName = "cel ❀"
+ , feedAuthorEmail = "cel@bunny.garden"
+ , feedRoot = "https://bunny.garden/~cel"
+ }
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..ab9f128
--- /dev/null
+++ b/style.css
@@ -0,0 +1,458 @@
+html {
+ background-image: url('/images/background.jpg');
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 100vh;
+}
+
+* {
+ word-break: break-word !important;
+ overflow-wrap: break-word !important;
+}
+
+body {
+ border: 4px solid #e8cdcf;
+ padding: 10vw 5vw;
+ max-width: 1000px;
+ margin: auto;
+ color: #e8cdcf;
+ background-color: #311f20;
+ font-family: 'Redaction 50';
+}
+
+h1 {
+ font-size: 2rem;
+}
+
+img {
+ border: 4px solid #e8cdcf;
+}
+
+hr {
+ background-color: #e8cdcf;
+ border: none;
+ height: 4px;
+}
+
+a {
+ color: #7f99e6;
+}
+
+a:visited {
+ color: #d1a2e7;
+}
+
+ul {
+ list-style: none;
+}
+
+ul li::before {
+ content: '❀';
+ font-style: normal;
+}
+
+#posts {
+ font-style: normal;
+}
+
+code {
+ font-family: 'Victor Mono';
+ border: 1px solid #e8cdcf;
+}
+
+pre code {
+ box-sizing: border-box;
+ border: none;
+}
+
+pre {
+ width: 100%;
+ white-space: pre-wrap;
+ overflow: auto;
+ border: 1px solid #e8cdcf;
+}
+
+.checklist {
+ border: 4px solid #e8cdcf;
+ padding: 2vw;
+ background-color: #311f20;
+ position: sticky !important;
+ display: inline-block;
+ float: right;
+ width: auto;
+ top: 2vw;
+ right: -2vw;
+ margin: 2vw 0;
+}
+
+input[type=checkbox]:checked~div {
+ display: none;
+}
+
+.checklist p {
+ display: inline;
+}
+
+.checklist ul {
+ list-style-type: none !important;
+ padding: 0;
+ margin-bottom: 0;
+}
+
+.badge,
+.badge * {
+ height: 31px;
+ width: 88px;
+ padding: 0;
+ border-radius: 0;
+ border: 0;
+ margin: 0;
+ text-decoration: none;
+ font-size: 0;
+ background-color: #0000;
+}
+
+.small-badge,
+.small-badge * {
+ padding: 0;
+ border: 0;
+ margin: 0;
+ font-size: 0;
+}
+
+@font-face {
+ font-family: 'Redaction';
+ src: url('/fonts/Redaction-Bold.woff2') format('woff2');
+ font-weight: bold;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction 10';
+ src: url('/fonts/Redaction_10-Regular.woff2') format('woff2');
+ font-weight: normal;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction 50';
+ src: url('/fonts/Redaction_50-Regular.woff2') format('woff2');
+ font-weight: normal;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction 70';
+ src: url('/fonts/Redaction_70-Italic.woff2') format('woff2');
+ font-weight: normal;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction';
+ src: url('/fonts/Redaction-Italic.woff2') format('woff2');
+ font-weight: normal;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction';
+ src: url('/fonts/Redaction-Regular.woff2') format('woff2');
+ font-weight: normal;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction 10';
+ src: url('/fonts/Redaction_10-Italic.woff2') format('woff2');
+ font-weight: normal;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction 35';
+ src: url('/fonts/Redaction_35-Regular.woff2') format('woff2');
+ font-weight: normal;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction 50';
+ src: url('/fonts/Redaction_50-Italic.woff2') format('woff2');
+ font-weight: normal;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction 70';
+ src: url('/fonts/Redaction_70-Bold.woff2') format('woff2');
+ font-weight: bold;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction 20';
+ src: url('/fonts/Redaction_20-Regular.woff2') format('woff2');
+ font-weight: normal;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction 100';
+ src: url('/fonts/Redaction_100-Regular.woff2') format('woff2');
+ font-weight: normal;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction 50';
+ src: url('/fonts/Redaction_50-Bold.woff2') format('woff2');
+ font-weight: bold;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction 100';
+ src: url('/fonts/Redaction_100-Italic.woff2') format('woff2');
+ font-weight: normal;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction 35';
+ src: url('/fonts/Redaction_35-Bold.woff2') format('woff2');
+ font-weight: bold;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction 20';
+ src: url('/fonts/Redaction_20-Italic.woff2') format('woff2');
+ font-weight: normal;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction 10';
+ src: url('/fonts/Redaction_10-Bold.woff2') format('woff2');
+ font-weight: bold;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction 35';
+ src: url('/fonts/Redaction_35-Italic.woff2') format('woff2');
+ font-weight: normal;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction 100';
+ src: url('/fonts/Redaction_100-Bold.woff2') format('woff2');
+ font-weight: bold;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction 70';
+ src: url('/fonts/Redaction_70-Regular.woff2') format('woff2');
+ font-weight: normal;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Redaction 20';
+ src: url('/fonts/Redaction_20-Bold.woff2') format('woff2');
+ font-weight: bold;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-Bold.woff2') format('woff2');
+ font-weight: bold;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono Oblique';
+ src: url('/fonts/VictorMono-Oblique.woff2') format('woff2');
+ font-weight: normal;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono Oblique';
+ src: url('/fonts/VictorMono-BoldOblique.woff2') format('woff2');
+ font-weight: bold;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-ExtraLight.woff2') format('woff2');
+ font-weight: 200;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-Light.woff2') format('woff2');
+ font-weight: 300;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-SemiBoldOblique.woff2') format('woff2');
+ font-weight: 600;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-Italic.woff2') format('woff2');
+ font-weight: normal;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-ExtraLightOblique.woff2') format('woff2');
+ font-weight: 200;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-SemiBold.woff2') format('woff2');
+ font-weight: 600;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-LightOblique.woff2') format('woff2');
+ font-weight: 300;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-MediumOblique.woff2') format('woff2');
+ font-weight: 500;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-MediumItalic.woff2') format('woff2');
+ font-weight: 500;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-Regular.woff2') format('woff2');
+ font-weight: normal;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-BoldItalic.woff2') format('woff2');
+ font-weight: bold;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-SemiBoldItalic.woff2') format('woff2');
+ font-weight: 600;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-LightItalic.woff2') format('woff2');
+ font-weight: 300;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-ExtraLightItalic.woff2') format('woff2');
+ font-weight: 200;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-Medium.woff2') format('woff2');
+ font-weight: 500;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-ThinItalic.woff2') format('woff2');
+ font-weight: 100;
+ font-style: italic;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-Thin.woff2') format('woff2');
+ font-weight: 100;
+ font-style: normal;
+ font-display: block;
+}
+
+@font-face {
+ font-family: 'Victor Mono';
+ src: url('/fonts/VictorMono-ThinOblique.woff2') format('woff2');
+ font-weight: 100;
+ font-style: italic;
+ font-display: block;
+} \ No newline at end of file
diff --git a/templates/default.html b/templates/default.html
new file mode 100644
index 0000000..887060c
--- /dev/null
+++ b/templates/default.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<html lang="en">
+
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="x-ua-compatible" content="ie=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <title>cel - $title$</title>
+ <link rel="stylesheet" href="/style.css" />
+</head>
+
+<body>
+ <header>
+ <h1><a href="/">~cel</a> <span style="font-style: normal">❀</span> <a href="https://bunny.garden">bunny.garden</a>
+ </h1>
+ </header>
+
+ <main role="main">
+ $body$
+ </main>
+
+ <br>
+ <hr>
+ <footer>
+ <a class="badge" href="https://blos.sm"><img src="https://blos.sm/static/badges/cel.png"></a>
+ <img class="badge" src="https://cel.blos.sm/static/badges/servinghttps_7.png">
+ </footer>
+</body>
+
+</html> \ No newline at end of file
diff --git a/templates/post-list.html b/templates/post-list.html
new file mode 100644
index 0000000..f447bab
--- /dev/null
+++ b/templates/post-list.html
@@ -0,0 +1,7 @@
+<ul id="posts">
+ $for(posts)$
+ <li>
+ <a href="$url$">$date$ - $title$</a>
+ </li>
+ $endfor$
+</ul> \ No newline at end of file
diff --git a/templates/post.html b/templates/post.html
new file mode 100644
index 0000000..80d0291
--- /dev/null
+++ b/templates/post.html
@@ -0,0 +1,13 @@
+<article>
+ <hr>
+ <br>
+ <section class="header">
+ <h1>$title$</h1>
+ <span style="float: right;">$date$</span>
+ </section>
+ <br>
+ <br>
+ <section>
+ $body$
+ </section>
+</article> \ No newline at end of file