summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.gitignore14
-rw-r--r--Makefile461
-rw-r--r--README.adoc33
-rw-r--r--deps.mk3420
-rwxr-xr-xmkdeps.sh125
-rw-r--r--po/de.po222
-rw-r--r--po/en.po180
-rw-r--r--po/eo.po221
-rw-r--r--po/es.po221
-rw-r--r--po/euandre.org.pot175
-rw-r--r--po/fr.po221
-rw-r--r--po/po4a.cfg131
-rw-r--r--po/pt.po211
l---------src/collections/de/blog1
l---------src/collections/de/folien1
l---------src/collections/de/hhg1
l---------src/collections/de/pastebin1
l---------src/collections/de/podcast1
l---------src/collections/de/screencast1
l---------src/collections/en/slide1
l---------src/collections/eo/blog1
l---------src/collections/eo/hml1
l---------src/collections/eo/lumbildoj1
l---------src/collections/eo/pastebin1
l---------src/collections/eo/podkasto1
l---------src/collections/eo/screencast1
l---------src/collections/es/blog1
l---------src/collections/es/diapositivas1
l---------src/collections/es/haa1
l---------src/collections/es/pastebin1
l---------src/collections/es/podcast1
l---------src/collections/es/screencast1
l---------src/collections/fr/aja1
l---------src/collections/fr/blog1
l---------src/collections/fr/diapositives1
l---------src/collections/fr/pastebin1
l---------src/collections/fr/podcast1
l---------src/collections/fr/screencast1
l---------src/collections/pt/blog1
l---------src/collections/pt/eslaides1
l---------src/collections/pt/pastebin1
l---------src/collections/pt/podcast1
l---------src/collections/pt/screencast1
-rw-r--r--src/comments.map17
-rw-r--r--src/comments.mbox3464
-rw-r--r--src/content/de/blog/index.adoc5
-rw-r--r--src/content/de/blog/kategorien.adoc6
-rw-r--r--src/content/de/folien/index.adoc7
-rw-r--r--src/content/de/folien/kategorien.adoc6
-rw-r--r--src/content/de/hhg/index.adoc11
-rw-r--r--src/content/de/hhg/kategorien.adoc6
-rw-r--r--src/content/de/index.adoc7
-rw-r--r--src/content/de/musik.adoc36
-rw-r--r--src/content/de/pastebin/index.adoc5
-rw-r--r--src/content/de/pastebin/kategorien.adoc6
-rw-r--r--src/content/de/podcast/index.adoc5
-rw-r--r--src/content/de/podcast/kategorien.adoc6
-rw-r--r--src/content/de/screencast/index.adoc5
-rw-r--r--src/content/de/screencast/kategorien.adoc6
-rw-r--r--src/content/de/ueber.adoc26
-rw-r--r--src/content/en/about.adoc28
-rw-r--r--src/content/en/blog/2018/08/01/npm-ci-reproducibility.adoc5
-rw-r--r--src/content/en/blog/2020/11/14/local-first-review.adoc2
-rw-r--r--src/content/en/index.adoc4
-rw-r--r--src/content/en/music.adoc33
-rw-r--r--src/content/en/pastebin/2021/06/22/curl-wget.adoc2
-rw-r--r--src/content/en/screencast/2021/02/07/autoqemu.adoc1
-rw-r--r--src/content/en/slide/2020/10/19/feature-flags.adoc234
-rw-r--r--src/content/en/slide/2020/10/19/feature-flags.eslaides230
-rw-r--r--src/content/en/slide/2020/11/14/local-first-hype.adoc208
-rw-r--r--src/content/en/slide/2020/11/14/local-first-hype.eslaides204
-rw-r--r--src/content/en/slide/categories.adoc2
-rw-r--r--src/content/en/slide/index.adoc4
-rw-r--r--src/content/eo/blog/index.adoc5
-rw-r--r--src/content/eo/blog/kategorioj.adoc6
-rw-r--r--src/content/eo/hml/index.adoc11
-rw-r--r--src/content/eo/hml/kategorioj.adoc6
-rw-r--r--src/content/eo/index.adoc7
-rw-r--r--src/content/eo/lumbildoj/index.adoc7
-rw-r--r--src/content/eo/lumbildoj/kategorioj.adoc6
-rw-r--r--src/content/eo/muziko.adoc36
-rw-r--r--src/content/eo/pastebin/index.adoc5
-rw-r--r--src/content/eo/pastebin/kategorioj.adoc6
-rw-r--r--src/content/eo/podkasto/index.adoc5
-rw-r--r--src/content/eo/podkasto/kategorioj.adoc6
-rw-r--r--src/content/eo/pri.adoc26
-rw-r--r--src/content/eo/screencast/index.adoc5
-rw-r--r--src/content/eo/screencast/kategorioj.adoc6
-rw-r--r--src/content/es/acerca.adoc26
-rw-r--r--src/content/es/blog/categorias.adoc6
-rw-r--r--src/content/es/blog/index.adoc5
-rw-r--r--src/content/es/diapositivas/categorias.adoc6
-rw-r--r--src/content/es/diapositivas/index.adoc7
-rw-r--r--src/content/es/haa/categorias.adoc6
-rw-r--r--src/content/es/haa/index.adoc11
-rw-r--r--src/content/es/index.adoc7
-rw-r--r--src/content/es/musica.adoc36
-rw-r--r--src/content/es/pastebin/categorias.adoc6
-rw-r--r--src/content/es/pastebin/index.adoc5
-rw-r--r--src/content/es/podcast/categorias.adoc6
-rw-r--r--src/content/es/podcast/index.adoc5
-rw-r--r--src/content/es/screencast/categorias.adoc6
-rw-r--r--src/content/es/screencast/index.adoc5
-rw-r--r--src/content/favicon.icobin1150 -> 1150 bytes
-rw-r--r--src/content/favicon.pngbin103 -> 106 bytes
-rw-r--r--src/content/fr/a-propos.adoc26
-rw-r--r--src/content/fr/aja/categories.adoc6
-rw-r--r--src/content/fr/aja/index.adoc11
-rw-r--r--src/content/fr/blog/categories.adoc6
-rw-r--r--src/content/fr/blog/index.adoc5
-rw-r--r--src/content/fr/diapositives/categories.adoc6
-rw-r--r--src/content/fr/diapositives/index.adoc7
-rw-r--r--src/content/fr/index.adoc7
-rw-r--r--src/content/fr/musique.adoc36
-rw-r--r--src/content/fr/pastebin/categories.adoc6
-rw-r--r--src/content/fr/pastebin/index.adoc5
-rw-r--r--src/content/fr/podcast/categories.adoc6
-rw-r--r--src/content/fr/podcast/index.adoc5
-rw-r--r--src/content/fr/screencast/categories.adoc6
-rw-r--r--src/content/fr/screencast/index.adoc5
-rw-r--r--src/content/img/favicon.svg140
-rw-r--r--src/content/img/logo/dark.svg130
-rw-r--r--src/content/img/logo/light.svg130
-rw-r--r--src/content/img/thumb/dark.svg62
-rw-r--r--src/content/img/thumb/light.svg62
-rw-r--r--src/content/img/wordmark/dark.svg94
-rw-r--r--src/content/img/wordmark/light.svg94
-rw-r--r--src/content/music/choro-da-saudade.ly233
-rw-r--r--src/content/music/choro-da-saudade.oggbin0 -> 3603216 bytes
-rw-r--r--src/content/music/common.ly.include15
-rw-r--r--src/content/music/dengoso.ly310
-rw-r--r--src/content/music/dengoso.oggbin0 -> 2433599 bytes
-rw-r--r--src/content/music/guitar.ly.include33
-rw-r--r--src/content/music/marcha-dos-marinheiros.ly78
-rw-r--r--src/content/music/marcha-dos-marinheiros.oggbin0 -> 691938 bytes
-rw-r--r--src/content/pt/blog/categorias.adoc6
-rw-r--r--src/content/pt/blog/index.adoc5
-rw-r--r--src/content/pt/eslaides/categorias.adoc6
-rw-r--r--src/content/pt/eslaides/index.adoc7
-rw-r--r--src/content/pt/index.adoc7
-rw-r--r--src/content/pt/musica.adoc36
-rw-r--r--src/content/pt/pastebin/categorias.adoc6
-rw-r--r--src/content/pt/pastebin/index.adoc5
-rw-r--r--src/content/pt/podcast/categorias.adoc6
-rw-r--r--src/content/pt/podcast/index.adoc5
-rw-r--r--src/content/pt/screencast/categorias.adoc6
-rw-r--r--src/content/pt/screencast/index.adoc5
-rw-r--r--src/content/pt/sobre.adoc18
-rw-r--r--src/content/public.asc.txt80
-rw-r--r--src/content/style.css229
-rw-r--r--src/headers/de.txt14
-rw-r--r--src/headers/eo.txt14
-rw-r--r--src/headers/es.txt14
-rw-r--r--src/headers/fr.txt14
-rw-r--r--src/headers/pt.txt10
-rw-r--r--src/linkonly-dirs.txt5
-rw-r--r--src/names/categories/de.txt2
-rw-r--r--src/names/categories/eo.txt2
-rw-r--r--src/names/categories/es.txt2
-rw-r--r--src/names/category/de.txt2
-rw-r--r--src/names/category/eo.txt2
-rw-r--r--src/names/category/es.txt2
-rw-r--r--src/names/category/fr.txt2
-rw-r--r--src/names/feed/de.txt1
-rw-r--r--src/names/feed/en.txt1
-rw-r--r--src/names/feed/eo.txt1
-rw-r--r--src/names/feed/es.txt1
-rw-r--r--src/names/feed/fr.txt1
-rw-r--r--src/names/feed/pt.txt1
-rw-r--r--src/names/feed/ref.txt1
-rw-r--r--src/names/feedicon_alt/de.txt1
-rw-r--r--src/names/feedicon_alt/en.txt1
-rw-r--r--src/names/feedicon_alt/eo.txt1
-rw-r--r--src/names/feedicon_alt/es.txt1
-rw-r--r--src/names/feedicon_alt/fr.txt1
-rw-r--r--src/names/feedicon_alt/pt.txt1
-rw-r--r--src/names/feedicon_alt/ref.txt1
-rw-r--r--src/names/language/de.txt1
-rw-r--r--src/names/language/en.txt1
-rw-r--r--src/names/language/eo.txt1
-rw-r--r--src/names/language/es.txt1
-rw-r--r--src/names/language/fr.txt1
-rw-r--r--src/names/language/pt.txt1
-rw-r--r--src/names/tagline/de.txt1
-rw-r--r--src/names/tagline/en.txt1
-rw-r--r--src/names/tagline/eo.txt1
-rw-r--r--src/names/tagline/es.txt1
-rw-r--r--src/names/tagline/fr.txt1
-rw-r--r--src/names/tagline/pt.txt1
-rw-r--r--src/names/tagline/ref.txt1
l---------src/pages/de1
l---------src/pages/eo1
l---------src/pages/es1
l---------src/pages/fr1
-rw-r--r--src/static.conf2
-rw-r--r--src/symlinks.txt334
196 files changed, 10972 insertions, 1963 deletions
diff --git a/.gitignore b/.gitignore
index 874fc80..67e08c1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,23 +1,35 @@
*.htmlbody
-*.embedded-config
*.conf
+*.json
*.snippets
*.links
*.caslinks
+*.comments
*.html
*.indexentry
+*.homeentry
*.feedentry
*.mapentry
*.sortdata
*.categorydata
*.txt
+## ...but these two hold sources, not artifacts.
+!/src/headers/*.txt
+!/src/names/*/*.txt
*.xml
*.htmlheader
*.htmlfooter
*.htmllisting
*.ps
*.pdf
+*.midi
*.gz
*.sentinel
*.torrent
/src/content/.gitignore
+/src/comments/
+/src/comments.stamp
+/src/translations.map
+## Server logs, copied here for analysis.
+/access.log
+/error.log
diff --git a/Makefile b/Makefile
index a3f44e4..bdd7e30 100644
--- a/Makefile
+++ b/Makefile
@@ -3,435 +3,22 @@ DATE = 1970-01-01
VERSION = 0.1.0
NAME = euandre.org
NAME_UC = $(NAME)
+## Content is partitioned by language, so mkwb conf derives a
+## page's language from its path rather than treating the site as
+## flat. Only whether this is empty matters, not what is in it.
LANGUAGES = en
-## Installation prefix. Defaults to "/usr".
-PREFIX = /usr
-BINDIR = $(PREFIX)/bin
-LIBDIR = $(PREFIX)/lib
-INCLUDEDIR = $(PREFIX)/include
-SRCDIR = $(PREFIX)/src/$(NAME)
-SHAREDIR = $(PREFIX)/share
-LOCALEDIR = $(SHAREDIR)/locale
-MANDIR = $(SHAREDIR)/man
-DOCDIR = $(SHAREDIR)/doc/$(NAME)
-HTMLDIR = $(SHAREDIR)/html/$(NAME)
-EXEC = ./
-## Where to store the installation. Empty by default.
-DESTDIR =
-LDLIBS =
-PUBURL = public.asc.txt
-FFMFLAGS = -y -hide_banner -loglevel warning
-BASEURL = /
-
-
-
-.SUFFIXES:
-.SUFFIXES: .adoc .conf .snippets .indexentry .feedentry .mapentry .sortdata .xml
-.SUFFIXES: .htmlbody .htmlheader .htmlfooter .htmllisting .html .links .caslinks
-.SUFFIXES: .txt .categorydata .gz .torrent .flac .ogg .ps .pdf .sentinel
-
-.adoc.conf:
- mkwb conf src/global.conf $< > $@
-
-.adoc.htmlbody:
- mkwb htmlbody $< > $@
-
-.htmlbody.html:
- mkwb html $< > $@
-
-.conf.htmlheader:
- mkwb html -H $< > $@
-
-.conf.htmlfooter:
- mkwb html -F $< > $@
-
-.adoc.snippets:
- mkwb snippets $< > $@
-
-.conf.indexentry:
- mkwb indexentry $< > $@
-
-.htmlbody.feedentry:
- mkwb feedentry $< > $@
-
-.conf.mapentry:
- mkwb mapentry $< > $@
-
-.conf.sortdata:
- mkwb sortdata $< > $@
-
-.conf.categorydata:
- mkwb categorydata $< > $@
-
-.adoc.links:
- mkwb links $< > $@
-
-.links.caslinks:
- grep -Ev '^(link|image):' $< | xargs -I_ sh -c '\
- printf "%s\n" "_" | sha256sum | \
- printf "%s\t%s\n" "`cut -d" " -f1`" "_"' > $@
-
-.flac.ogg:
- ffmpeg $(FFMFLAGS) -i $< -ar 48000 -vn -c:a libvorbis -b:a 320k $@
-
-.adoc.ps:
- eslaides < $< > $@
-
-.ps.pdf:
- ps2pdf - < $< > $@
+PORT = 3333
all:
include deps.mk
-
-listings.adoc = $(categories.adoc) $(indexes.adoc)
-sources.adoc = $(articles.adoc) $(listings.adoc) $(pages.adoc)
-sources.htmlbody = $(sources.adoc:.adoc=.htmlbody)
-sources.html = $(sources.adoc:.adoc=.html)
-sources.snippets = $(sources.adoc:.adoc=.snippets)
-sources.snippets.gz = $(sources.adoc:.adoc=.snippets.gz)
-sources.conf = $(sources.adoc:.adoc=.conf)
-sources.links = $(sources.adoc:.adoc=.links)
-sources.caslinks = $(sources.adoc:.adoc=.caslinks)
-sources.mapentry = $(sources.adoc:.adoc=.mapentry)
-articles.indexentry = $(articles.adoc:.adoc=.indexentry)
-articles.feedentry = $(articles.adoc:.adoc=.feedentry)
-articles.sortdata = $(articles.adoc:.adoc=.sortdata)
-articles.categorydata = $(articles.adoc:.adoc=.categorydata)
-listings.htmlheader = $(listings.adoc:.adoc=.htmlheader)
-listings.htmlfooter = $(listings.adoc:.adoc=.htmlfooter)
-listings.html = $(listings.adoc:.adoc=.html)
-indexes.htmllisting = $(indexes.adoc:.adoc=.htmllisting)
-categories.htmllisting = $(categories.adoc:.adoc=.htmllisting)
-categories.txt = $(categories.adoc:.adoc=.txt)
-categories.xml = $(categories.adoc:.adoc=.xml)
-categories.xml.gz = $(categories.adoc:.adoc=.xml.gz)
-sources.media.torrent = $(sources.media:=.torrent)
-slides.ps = $(slides.adoc:.adoc=.ps)
-slides.pdf = $(slides.adoc:.adoc=.pdf)
-
-sources = \
- $(sources.adoc) \
- $(sources.extras) \
- $(images.svg) \
- src/content/favicon.ico \
- src/content/favicon.png \
- src/content/style.css \
- src/content/$(PUBURL) \
-
-dynamic-contents = \
- $(sources.html) \
- $(slides.pdf) \
- $(feeds.xml) \
- $(sources.media.torrent) \
- src/content/sitemap.xml \
-
-static-contents = \
- $(sources.extras) \
- $(images.svg) \
- src/content/favicon.ico \
- src/content/favicon.png \
- src/content/style.css \
- src/content/$(PUBURL) \
- src/content/.well-known/security.txt \
-
-dynamic-contents.gz = $(dynamic-contents:=.gz)
-
-static-contents.gz = \
- $(images.svg:=.gz) \
- src/content/favicon.ico.gz \
- src/content/style.css.gz \
- src/content/$(PUBURL).gz \
- src/content/.well-known/security.txt.gz \
-
-contents.gz = \
- $(dynamic-contents.gz) \
- $(static-contents.gz) \
-
-contents = \
- $(dynamic-contents) \
- $(static-contents) \
-
-all-filelists = \
- $(sources.snippets) \
- $(sources.snippets.gz) \
- $(categories.xml) \
- $(categories.xml.gz) \
-
-all-contents = \
- $(contents) \
- $(contents.gz) \
-
-captured-assets = \
- src/content/$(PUBURL) \
- src/content/favicon.ico \
- src/content/favicon.png \
-
-captured-assets.sentinel = $(captured-assets:=.sentinel)
-
-
-derived-assets = \
- $(dynamic-contents) \
- $(contents.gz) \
- $(sources.html) \
- $(sources.htmlbody) \
- $(sources.snippets) \
- $(sources.snippets.gz) \
- $(sources.conf) \
- $(sources.links) \
- $(sources.caslinks) \
- $(sources.mapentry) \
- src/dyn.conf \
- src/base.conf \
- src/global.conf \
- $(articles.indexentry) \
- $(articles.feedentry) \
- $(articles.sortdata) \
- $(articles.categorydata) \
- $(listings.htmlheader) \
- $(listings.htmlfooter) \
- $(listings.html) \
- $(indexes.htmllisting) \
- $(categories.htmllisting) \
- $(categories.txt) \
- $(categories.xml) \
- $(categories.xml.gz) \
- $(slides.ps) \
- $(slides.pdf) \
- email.txt \
- baseurl.txt \
- fingerprint.txt \
- expiry.txt \
- expiry-epoch.txt \
- now.txt \
- src/content/.well-known/security.txt \
- src/all-contents.txt \
- src/all-filelists.txt \
- src/all-symlinks.txt \
- src/install.txt \
- src/sort-expected.txt \
- src/sort-given.txt \
- src/sources.txt \
- install.txt \
- sources.txt \
- src/content/.gitignore \
- $(captured-assets.sentinel) \
-
-side-assets = \
- src/collections/*/*/*/*/*/*.html.*.txt \
- src/collections/*/*/*/*/*/*.txt.gz \
- src/collections/*/*/index.html.*.txt \
- src/collections/*/*/sortdata.txt \
- src/collections/*/*/feed.*.xml \
- src/collections/*/*/feed.*.xml.gz \
- src/collections/*/*/*.sortdata \
- src/pages/*/*.html.*.txt \
- src/content/.well-known/ \
- `cat src/all-symlinks.txt 2>/dev/null` \
- `cat src/linkonly-dirs.txt 2>/dev/null` \
-
-
-
-## Default target. Builds all artifacts required for testing
-## and installation.
-all: $(derived-assets)
-all: $(captured-assets)
-
-
$(derived-assets): Makefile deps.mk
-$(sources.conf): src/global.conf
-
-
-src/content/.gitignore: src/symlinks.txt
- cd src/content/ && mkwb symlinks ../symlinks.txt > $(@F)
-
-src/dyn.conf: email.txt baseurl.txt fingerprint.txt
- printf "export url_pre='%s'\n" "`cat baseurl.txt`" > $@
- printf "export email='%s'\n" "`cat email.txt`" >> $@
- printf "export publickey='%s'\n" "`cat fingerprint.txt`" >> $@
- printf "export publickey_url='$(PUBURL)'\n" >> $@
- printf 'export sourcecode_url="$$url_pre/git/$(NAME)"\n' >> $@
-
-src/base.conf: src/dyn.conf src/static.conf
- cat src/dyn.conf src/static.conf > $@
-
-src/global.conf: src/base.conf
- mkwb conf -G src/base.conf > $@
-
-$(listings.html):
- cat $*.htmlheader $*.htmlbody $*.htmllisting $*.htmlfooter > $@
-
-$(indexes.htmllisting):
- mkwb indexbody $*.conf > $@
-
-$(categories.htmllisting):
- mkwb categoriesbody $*.conf > $@
-
-$(categories.txt): src/global.conf
- mkwb categories src/global.conf $(@D) > $@
-
-$(categories.xml):
- for f in `cat $*.txt`; do \
- c="`printf '%s\n' "$$f" | cut -d. -f2`"; \
- mkwb feed src/global.conf "$$f" > $(@D)/feed."$$c".xml; \
- printf '%s\n' $(@D)/feed."$$c".xml; \
- done > $@
-
-$(feeds.xml):
- mkwb feed src/global.conf $(@D)/sortdata.txt > $@
-
-$(contents.gz):
- gzip -9fk $*
- touch $@
-
-$(sources.snippets.gz) $(categories.xml.gz):
- if [ -s $* ]; then gzip -9fk `cat $*`; fi
- sed 's/$$/.gz/' $* > $@
-
-src/content/$(PUBURL).gz: src/content/$(PUBURL).sentinel
-src/content/$(PUBURL).sentinel: email.txt
- gpg --export --armour "`cat email.txt`" | ifne ifnew $*
- touch $@
-src/content/favicon.ico.gz: src/content/favicon.ico.sentinel
-src/content/favicon.ico.sentinel: src/content/img/favicon.svg
- convert src/content/img/favicon.svg -strip ico:- | ifnew $*
- touch $@
-src/content/favicon.png.sentinel: src/content/img/favicon.svg
- convert src/content/img/favicon.svg -strip png:- | ifnew $*
- touch $@
-
-$(sources.media.torrent):
- F="`printf '%s\n' $* | cut -d/ -f3-`" && \
- mktorrent -xfd -n $(*F) -o $@ -w "https://$(NAME)$(BASEURL)$${F}" $*
-
-src/content/sitemap.xml.gz: src/content/sitemap.xml
-src/content/sitemap.xml: $(sources.mapentry)
- mkwb sitemap $(sources.mapentry) > $@
-
-email.txt: meta.capim
- cat meta.capim | awk '$$1 == ":email" && $$0=$$2' | tr -d '"' > $@
-
-baseurl.txt: meta.capim
- cat meta.capim | awk '$$1 == ":baseurl" && $$0=$$2' | tr -d '"' > $@
-
-fingerprint.txt: src/content/$(PUBURL)
- gpg --always-trust --no-keyring --show-key --with-colons \
- src/content/$(PUBURL) | \
- awk -F: '/^pub:/ { print $$5 }' > $@
-
-expiry.txt: src/content/$(PUBURL)
- gpg --always-trust --no-keyring --show-key --with-colons \
- src/content/$(PUBURL) | \
- awk -F: '/^pub:/ { print $$7 }' | \
- xargs -I% date -Is -d@% > $@
-
-expiry-epoch.txt: expiry.txt
- date -d "`cat expiry.txt`" '+%s' > $@
-
-now.txt:
- now > $@
-
-src/content/.well-known/security.txt.gz: src/content/.well-known/security.txt
-src/content/.well-known/security.txt: email.txt baseurl.txt expiry.txt
- mkdir -p $(@D)
- printf 'Contact: mailto:%s\n' "`cat email.txt`" > $@
- printf 'Expires: %s\n' "`cat expiry.txt`" >> $@
- printf 'Encryption: %s/$(PUBURL)\n' "`cat baseurl.txt`" >> $@
- printf 'Preferred-Languages: en, pt, fr, eo, es, de\n' >> $@
-
-src/sources.txt:
- printf '%s\n' $(sources) > $@
-
-src/all-contents.txt:
- printf '%s\n' $(all-contents) > $@
-
-src/all-filelists.txt: src/all-symlinks.txt
- printf '%s\n' $(all-filelists) src/all-symlinks.txt > $@
-
-src/all-symlinks.txt: src/content/.gitignore
- sed 's|^|src/content|' src/content/.gitignore > $@
-
-src/install.txt: src/all-contents.txt src/all-filelists.txt $(all-filelists)
- cat src/all-contents.txt `cat src/all-filelists.txt` > $@
-
-sources.txt: src/sources.txt
-install.txt: src/install.txt
-sources.txt install.txt:
- sed 's|^src/content/||' src/$(@F) > $@
-
-
-
-src/sort-expected.txt:
- dirname $(articles.adoc) | env LANG=POSIX.UTF-8 sort | uniq -c | \
- awk '{ printf "%s\t%s\n", $$2, $$1 }' > $@
-
-src/sort-given.txt: $(sources.conf) src/sort-expected.txt
- awk '{ \
- "grep \"^export sort=\" " $$1 "/*.conf | wc -l" | getline cnt; \
- printf "%s\t%s\n", $$1, cnt; \
- }' src/sort-expected.txt > $@
-
-check-unit-sorting: src/sort-expected.txt src/sort-given.txt
- diff -U10 src/sort-expected.txt src/sort-given.txt
-
-
-.SUFFIXES: .updatedat-check
-sources.updatedat-check = $(sources.adoc:.adoc=.updatedat-check)
-$(sources.updatedat-check):
- . ./$*.conf && if [ -n "$$updatedat_epoch" ] && \
- [ "$$updatedat_epoch" -le "$$date_epoch" ]; then exit 3; fi
-
-check-unit-updatedat: $(sources.updatedat-check)
-
-
-.SUFFIXES: .links-internal-check
-sources.links-internal-check = $(sources.adoc:.adoc=.links-internal-check)
-$(sources.links-internal-check): $(sources.html) $(slides.pdf)
- grep -E '^(link|image):' $*.links | cut -d: -f2- | \
- xargs -I% test -e $(*D)/%
-
-check-unit-links-internal: $(sources.links-internal-check)
-
-
-check-unit-links-external:
-
-
-symlink-deps = \
- $(sources.html) \
- $(feeds.xml) \
- $(categories.xml) \
- src/content/.well-known/security.txt \
- $(sources.media.torrent) \
-
-check-unit-links-symlinks: src/all-symlinks.txt $(symlink-deps)
- find `cat src/all-symlinks.txt` | xargs -n1 test -e
-
-
-check-unit-links: check-unit-links-internal check-unit-links-external
-check-unit-links: check-unit-links-symlinks
-
-
-MAXSIZE = 52428800 # from spec: https://www.sitemaps.org/protocol.html
-check-unit-sitemap-size: src/content/sitemap.xml
- test "`stat --printf='%s' src/content/sitemap.xml`" -le $(MAXSIZE)
-
-check-unit-sitemap-count: src/content/sitemap.xml
- test "`grep -cF '</url>' src/content/sitemap.xml`" -le 50000
-
-check-unit-sitemap: check-unit-sitemap-count check-unit-sitemap-size
-
-
-check-unit-expiry: expiry-epoch.txt now.txt
- test "`cat expiry-epoch.txt`" -gt "`cat now.txt`"
-
-
-check-unit: check-unit-sorting check-unit-updatedat check-unit-links
-check-unit: check-unit-sitemap check-unit-expiry
+## ---- what this site has beyond the canonical rules -------------
integration-tests = \
@@ -441,47 +28,15 @@ $(integration-tests): ALWAYS
check-integration: $(integration-tests)
-
-## Run all tests. Each test suite is isolated, so that a parallel
-## build can run tests at the same time. The required artifacts
-## are created if missing.
check: check-unit check-integration
-
+## Regenerate every translated page from the catalogues. Not part
+## of "all": it rewrites tracked sources.
i18n:
po4a po/po4a.cfg
-
-## Remove *all* derived artifacts produced during the build.
-## A dedicated test asserts that this is always true.
-clean:
- rm -rf $(derived-assets) $(side-assets)
-
-
-## Installs into $(DESTDIR)$(PREFIX). Its dependency target
-## ensures that all installable artifacts are crafted beforehand.
-install: all
- rsync --delete --mkpath -a --files-from=install.txt src/content/ \
- '$(DESTDIR)$(HTMLDIR)'
- rsync --delete --mkpath -a --files-from=sources.txt src/content/ \
- '$(DESTDIR)$(SRCDIR)'
-
-## Uninstalls from $(DESTDIR)$(PREFIX). This is a perfect mirror
-## of the "install" target, and removes *all* that was installed.
-## A dedicated test asserts that this is always true.
-uninstall:
- rm -rf \
- '$(DESTDIR)$(SRCDIR)' \
- '$(DESTDIR)$(HTMLDIR)' \
-
-
-
-PORT = 3333
-## Run file server for local installed static files.
+## Serve the installed tree, to read it as it will be served.
run:
serve -n -p $(PORT) -d '$(DESTDIR)$(HTMLDIR)'
-
-
-ALWAYS:
diff --git a/README.adoc b/README.adoc
new file mode 100644
index 0000000..d4d9297
--- /dev/null
+++ b/README.adoc
@@ -0,0 +1,33 @@
+= euandre.org
+
+The source of https://euandre.org: a personal website of articles,
+notes, pastebins, podcasts, screencasts and slide decks, in six
+languages, built by *mkwb*(1) from asciidoc.
+
+The site is static in the strict sense --- every page, feed and
+sitemap is a file on disk, and serving it needs nothing but a web
+server. Translations are generated by *po4a*(1) from one catalogue
+per language, so a change to the English source propagates rather
+than drifting. Slide decks render to PDF through *eslaides*(1),
+and the music pages are engraved from LilyPond sources.
+
+== Build
+
+....
+make # the whole site under src/content/
+make check # seventeen consistency checks
+make install # into $(DESTDIR)$(HTMLDIR) and $(DESTDIR)$(SRCDIR)
+make i18n # regenerate translations from po/
+make run # serve the installed tree
+....
+
+The rules come from *mkwb*: `mkdeps.sh` ends with `mkwb rules`,
+which inlines them into `deps.mk`. What is here is only what is
+this site's --- its name, its languages, its port.
+
+Compression is not the build's business: `make install` ships
+content, and *gzipdir*(1) walks the installed tree afterwards.
+
+== Licence
+
+The content is CC BY-SA 4.0. The code is AGPL-3.0-or-later.
diff --git a/deps.mk b/deps.mk
index 43a93e9..e046e3f 100644
--- a/deps.mk
+++ b/deps.mk
@@ -1,8 +1,25 @@
pages.adoc = \
+ src/content/de/musik.adoc \
+ src/content/de/ueber.adoc \
src/content/en/about.adoc \
- src/content/en/index.adoc \
+ src/content/en/music.adoc \
+ src/content/eo/muziko.adoc \
+ src/content/eo/pri.adoc \
+ src/content/es/acerca.adoc \
+ src/content/es/musica.adoc \
+ src/content/fr/a-propos.adoc \
+ src/content/fr/musique.adoc \
+ src/content/pt/musica.adoc \
src/content/pt/sobre.adoc \
+homes.adoc = \
+ src/content/de/index.adoc \
+ src/content/en/index.adoc \
+ src/content/eo/index.adoc \
+ src/content/es/index.adoc \
+ src/content/fr/index.adoc \
+ src/content/pt/index.adoc \
+
articles.adoc = \
src/content/en/blog/2018/07/17/guix-nixos.adoc \
src/content/en/blog/2018/08/01/npm-ci-reproducibility.adoc \
@@ -45,6 +62,8 @@ articles.adoc = \
src/content/en/pastebin/2023/07/22/funcallable-amop.adoc \
src/content/en/podcast/2020/12/19/test-entry.adoc \
src/content/en/screencast/2021/02/07/autoqemu.adoc \
+ src/content/en/slide/2020/10/19/feature-flags.adoc \
+ src/content/en/slide/2020/11/14/local-first-hype.adoc \
src/content/en/til/2020/08/12/filename-timestamp.adoc \
src/content/en/til/2020/08/13/code-jekyll.adoc \
src/content/en/til/2020/08/14/browse-git.adoc \
@@ -70,33 +89,123 @@ articles.adoc = \
src/content/en/til/2021/08/11/js-bigint-reviver.adoc \
src/content/pt/hea/2020/08/12/arquivo-datado.adoc \
-slides.adoc = \
- src/content/en/slide/2020/10/19/feature-flags.adoc \
- src/content/en/slide/2020/11/14/local-first-hype.adoc \
+slides.eslaides = \
+ src/content/en/slide/2020/10/19/feature-flags.eslaides \
+ src/content/en/slide/2020/11/14/local-first-hype.eslaides \
categories.adoc = \
+ src/content/de/blog/kategorien.adoc \
+ src/content/de/folien/kategorien.adoc \
+ src/content/de/hhg/kategorien.adoc \
+ src/content/de/pastebin/kategorien.adoc \
+ src/content/de/podcast/kategorien.adoc \
+ src/content/de/screencast/kategorien.adoc \
src/content/en/blog/categories.adoc \
src/content/en/pastebin/categories.adoc \
src/content/en/podcast/categories.adoc \
src/content/en/screencast/categories.adoc \
+ src/content/en/slide/categories.adoc \
src/content/en/til/categories.adoc \
+ src/content/eo/blog/kategorioj.adoc \
+ src/content/eo/hml/kategorioj.adoc \
+ src/content/eo/lumbildoj/kategorioj.adoc \
+ src/content/eo/pastebin/kategorioj.adoc \
+ src/content/eo/podkasto/kategorioj.adoc \
+ src/content/eo/screencast/kategorioj.adoc \
+ src/content/es/blog/categorias.adoc \
+ src/content/es/diapositivas/categorias.adoc \
+ src/content/es/haa/categorias.adoc \
+ src/content/es/pastebin/categorias.adoc \
+ src/content/es/podcast/categorias.adoc \
+ src/content/es/screencast/categorias.adoc \
+ src/content/fr/aja/categories.adoc \
+ src/content/fr/blog/categories.adoc \
+ src/content/fr/diapositives/categories.adoc \
+ src/content/fr/pastebin/categories.adoc \
+ src/content/fr/podcast/categories.adoc \
+ src/content/fr/screencast/categories.adoc \
+ src/content/pt/blog/categorias.adoc \
+ src/content/pt/eslaides/categorias.adoc \
src/content/pt/hea/categorias.adoc \
+ src/content/pt/pastebin/categorias.adoc \
+ src/content/pt/podcast/categorias.adoc \
+ src/content/pt/screencast/categorias.adoc \
indexes.adoc = \
+ src/content/de/blog/index.adoc \
+ src/content/de/folien/index.adoc \
+ src/content/de/hhg/index.adoc \
+ src/content/de/pastebin/index.adoc \
+ src/content/de/podcast/index.adoc \
+ src/content/de/screencast/index.adoc \
src/content/en/blog/index.adoc \
src/content/en/pastebin/index.adoc \
src/content/en/podcast/index.adoc \
src/content/en/screencast/index.adoc \
+ src/content/en/slide/index.adoc \
src/content/en/til/index.adoc \
+ src/content/eo/blog/index.adoc \
+ src/content/eo/hml/index.adoc \
+ src/content/eo/lumbildoj/index.adoc \
+ src/content/eo/pastebin/index.adoc \
+ src/content/eo/podkasto/index.adoc \
+ src/content/eo/screencast/index.adoc \
+ src/content/es/blog/index.adoc \
+ src/content/es/diapositivas/index.adoc \
+ src/content/es/haa/index.adoc \
+ src/content/es/pastebin/index.adoc \
+ src/content/es/podcast/index.adoc \
+ src/content/es/screencast/index.adoc \
+ src/content/fr/aja/index.adoc \
+ src/content/fr/blog/index.adoc \
+ src/content/fr/diapositives/index.adoc \
+ src/content/fr/pastebin/index.adoc \
+ src/content/fr/podcast/index.adoc \
+ src/content/fr/screencast/index.adoc \
+ src/content/pt/blog/index.adoc \
+ src/content/pt/eslaides/index.adoc \
src/content/pt/hea/index.adoc \
+ src/content/pt/pastebin/index.adoc \
+ src/content/pt/podcast/index.adoc \
+ src/content/pt/screencast/index.adoc \
feeds.xml = \
+ src/content/de/blog/feed.xml \
+ src/content/de/folien/feed.xml \
+ src/content/de/hhg/feed.xml \
+ src/content/de/pastebin/feed.xml \
+ src/content/de/podcast/feed.xml \
+ src/content/de/screencast/feed.xml \
src/content/en/blog/feed.xml \
src/content/en/pastebin/feed.xml \
src/content/en/podcast/feed.xml \
src/content/en/screencast/feed.xml \
+ src/content/en/slide/feed.xml \
src/content/en/til/feed.xml \
+ src/content/eo/blog/feed.xml \
+ src/content/eo/hml/feed.xml \
+ src/content/eo/lumbildoj/feed.xml \
+ src/content/eo/pastebin/feed.xml \
+ src/content/eo/podkasto/feed.xml \
+ src/content/eo/screencast/feed.xml \
+ src/content/es/blog/feed.xml \
+ src/content/es/diapositivas/feed.xml \
+ src/content/es/haa/feed.xml \
+ src/content/es/pastebin/feed.xml \
+ src/content/es/podcast/feed.xml \
+ src/content/es/screencast/feed.xml \
+ src/content/fr/aja/feed.xml \
+ src/content/fr/blog/feed.xml \
+ src/content/fr/diapositives/feed.xml \
+ src/content/fr/pastebin/feed.xml \
+ src/content/fr/podcast/feed.xml \
+ src/content/fr/screencast/feed.xml \
+ src/content/pt/blog/feed.xml \
+ src/content/pt/eslaides/feed.xml \
src/content/pt/hea/feed.xml \
+ src/content/pt/pastebin/feed.xml \
+ src/content/pt/podcast/feed.xml \
+ src/content/pt/screencast/feed.xml \
images.svg = \
src/content/img/atom.svg \
@@ -109,6 +218,10 @@ images.svg = \
src/content/img/lock/light.svg \
src/content/img/logo/dark.svg \
src/content/img/logo/light.svg \
+ src/content/img/thumb/dark.svg \
+ src/content/img/thumb/light.svg \
+ src/content/img/wordmark/dark.svg \
+ src/content/img/wordmark/light.svg \
sources.media = \
src/content/en/podcast/2020/12/19/test-entry.flac \
@@ -143,421 +256,506 @@ sources.po = \
po/fr.po \
po/pt.po \
-src/content/en/about.html.gz: src/content/en/about.html
-src/content/en/index.html.gz: src/content/en/index.html
-src/content/pt/sobre.html.gz: src/content/pt/sobre.html
-src/content/en/blog/2018/07/17/guix-nixos.html.gz: src/content/en/blog/2018/07/17/guix-nixos.html
-src/content/en/blog/2018/08/01/npm-ci-reproducibility.html.gz: src/content/en/blog/2018/08/01/npm-ci-reproducibility.html
-src/content/en/blog/2018/12/21/ytdl-subs.html.gz: src/content/en/blog/2018/12/21/ytdl-subs.html
-src/content/en/blog/2019/06/02/nixos-stateless-workstation.html.gz: src/content/en/blog/2019/06/02/nixos-stateless-workstation.html
-src/content/en/blog/2020/08/10/guix-srht.html.gz: src/content/en/blog/2020/08/10/guix-srht.html
-src/content/en/blog/2020/08/31/database-i-wish-i-had.html.gz: src/content/en/blog/2020/08/31/database-i-wish-i-had.html
-src/content/en/blog/2020/10/05/cargo2nix.html.gz: src/content/en/blog/2020/10/05/cargo2nix.html
-src/content/en/blog/2020/10/05/swift2nix.html.gz: src/content/en/blog/2020/10/05/swift2nix.html
-src/content/en/blog/2020/10/19/feature-flags.html.gz: src/content/en/blog/2020/10/19/feature-flags.html
-src/content/en/blog/2020/10/20/wrong-interviewing.html.gz: src/content/en/blog/2020/10/20/wrong-interviewing.html
-src/content/en/blog/2020/11/07/diy-bugs.html.gz: src/content/en/blog/2020/11/07/diy-bugs.html
-src/content/en/blog/2020/11/08/paradigm-shift-review.html.gz: src/content/en/blog/2020/11/08/paradigm-shift-review.html
-src/content/en/blog/2020/11/12/database-parsers-trees.html.gz: src/content/en/blog/2020/11/12/database-parsers-trees.html
-src/content/en/blog/2020/11/14/local-first-review.html.gz: src/content/en/blog/2020/11/14/local-first-review.html
-src/content/en/blog/2021/01/26/remembering-ann.html.gz: src/content/en/blog/2021/01/26/remembering-ann.html
-src/content/en/blog/2021/02/17/fallible.html.gz: src/content/en/blog/2021/02/17/fallible.html
-src/content/en/blog/2021/04/29/relational-review.html.gz: src/content/en/blog/2021/04/29/relational-review.html
-src/content/en/pastebin/2016/04/05/rpn.html.gz: src/content/en/pastebin/2016/04/05/rpn.html
-src/content/en/pastebin/2018/07/11/nix-pinning.html.gz: src/content/en/pastebin/2018/07/11/nix-pinning.html
-src/content/en/pastebin/2018/07/13/guix-nixos-systemd.html.gz: src/content/en/pastebin/2018/07/13/guix-nixos-systemd.html
-src/content/en/pastebin/2018/07/13/guixbuilder-nixos.html.gz: src/content/en/pastebin/2018/07/13/guixbuilder-nixos.html
-src/content/en/pastebin/2018/07/13/guixbuilder.html.gz: src/content/en/pastebin/2018/07/13/guixbuilder.html
-src/content/en/pastebin/2018/07/13/nix-strpad.html.gz: src/content/en/pastebin/2018/07/13/nix-strpad.html
-src/content/en/pastebin/2018/07/25/nix-exps.html.gz: src/content/en/pastebin/2018/07/25/nix-exps.html
-src/content/en/pastebin/2018/07/25/nix-showdrv.html.gz: src/content/en/pastebin/2018/07/25/nix-showdrv.html
-src/content/en/pastebin/2019/06/08/inconsistent-hash.html.gz: src/content/en/pastebin/2019/06/08/inconsistent-hash.html
-src/content/en/pastebin/2019/12/29/raku-tuple-type.html.gz: src/content/en/pastebin/2019/12/29/raku-tuple-type.html
-src/content/en/pastebin/2020/01/04/guix-import-failure.html.gz: src/content/en/pastebin/2020/01/04/guix-import-failure.html
-src/content/en/pastebin/2020/02/14/guix-shebang.html.gz: src/content/en/pastebin/2020/02/14/guix-shebang.html
-src/content/en/pastebin/2020/11/27/guix-build-local.html.gz: src/content/en/pastebin/2020/11/27/guix-build-local.html
-src/content/en/pastebin/2020/12/15/guix-pack-fail.html.gz: src/content/en/pastebin/2020/12/15/guix-pack-fail.html
-src/content/en/pastebin/2021/04/03/naive-slugify-js.html.gz: src/content/en/pastebin/2021/04/03/naive-slugify-js.html
-src/content/en/pastebin/2021/06/08/reading-session-pt1.html.gz: src/content/en/pastebin/2021/06/08/reading-session-pt1.html
-src/content/en/pastebin/2021/06/22/curl-wget.html.gz: src/content/en/pastebin/2021/06/22/curl-wget.html
-src/content/en/pastebin/2021/08/11/h1-spacing.html.gz: src/content/en/pastebin/2021/08/11/h1-spacing.html
-src/content/en/pastebin/2021/09/02/sicp-3-19.html.gz: src/content/en/pastebin/2021/09/02/sicp-3-19.html
-src/content/en/pastebin/2021/09/03/sicp-persistent-queue.html.gz: src/content/en/pastebin/2021/09/03/sicp-persistent-queue.html
-src/content/en/pastebin/2022/07/14/git-cleanup.html.gz: src/content/en/pastebin/2022/07/14/git-cleanup.html
-src/content/en/pastebin/2023/07/22/funcallable-amop.html.gz: src/content/en/pastebin/2023/07/22/funcallable-amop.html
-src/content/en/podcast/2020/12/19/test-entry.html.gz: src/content/en/podcast/2020/12/19/test-entry.html
-src/content/en/screencast/2021/02/07/autoqemu.html.gz: src/content/en/screencast/2021/02/07/autoqemu.html
-src/content/en/til/2020/08/12/filename-timestamp.html.gz: src/content/en/til/2020/08/12/filename-timestamp.html
-src/content/en/til/2020/08/13/code-jekyll.html.gz: src/content/en/til/2020/08/13/code-jekyll.html
-src/content/en/til/2020/08/14/browse-git.html.gz: src/content/en/til/2020/08/14/browse-git.html
-src/content/en/til/2020/08/16/git-search.html.gz: src/content/en/til/2020/08/16/git-search.html
-src/content/en/til/2020/08/28/grep-online.html.gz: src/content/en/til/2020/08/28/grep-online.html
-src/content/en/til/2020/09/04/cli-email-fun-profit.html.gz: src/content/en/til/2020/09/04/cli-email-fun-profit.html
-src/content/en/til/2020/09/05/oldschool-pr.html.gz: src/content/en/til/2020/09/05/oldschool-pr.html
-src/content/en/til/2020/10/11/search-git-history.html.gz: src/content/en/til/2020/10/11/search-git-history.html
-src/content/en/til/2020/11/08/find-broken-symlink.html.gz: src/content/en/til/2020/11/08/find-broken-symlink.html
-src/content/en/til/2020/11/12/diy-nix-bash-ci.html.gz: src/content/en/til/2020/11/12/diy-nix-bash-ci.html
-src/content/en/til/2020/11/12/git-bisect-automation.html.gz: src/content/en/til/2020/11/12/git-bisect-automation.html
-src/content/en/til/2020/11/12/useful-bashvars.html.gz: src/content/en/til/2020/11/12/useful-bashvars.html
-src/content/en/til/2020/11/14/gpodder-media.html.gz: src/content/en/til/2020/11/14/gpodder-media.html
-src/content/en/til/2020/11/30/git-notes-ci.html.gz: src/content/en/til/2020/11/30/git-notes-ci.html
-src/content/en/til/2020/12/15/shellcheck-repo.html.gz: src/content/en/til/2020/12/15/shellcheck-repo.html
-src/content/en/til/2020/12/29/svg.html.gz: src/content/en/til/2020/12/29/svg.html
-src/content/en/til/2021/01/12/curl-awk-emails.html.gz: src/content/en/til/2021/01/12/curl-awk-emails.html
-src/content/en/til/2021/01/17/posix-shebang.html.gz: src/content/en/til/2021/01/17/posix-shebang.html
-src/content/en/til/2021/04/24/cl-generic-precedence.html.gz: src/content/en/til/2021/04/24/cl-generic-precedence.html
-src/content/en/til/2021/04/24/clojure-autocurry.html.gz: src/content/en/til/2021/04/24/clojure-autocurry.html
-src/content/en/til/2021/04/24/scm-nif.html.gz: src/content/en/til/2021/04/24/scm-nif.html
-src/content/en/til/2021/07/23/git-tls-gpg.html.gz: src/content/en/til/2021/07/23/git-tls-gpg.html
-src/content/en/til/2021/08/11/js-bigint-reviver.html.gz: src/content/en/til/2021/08/11/js-bigint-reviver.html
-src/content/pt/hea/2020/08/12/arquivo-datado.html.gz: src/content/pt/hea/2020/08/12/arquivo-datado.html
-src/content/en/blog/index.html.gz: src/content/en/blog/index.html
-src/content/en/pastebin/index.html.gz: src/content/en/pastebin/index.html
-src/content/en/podcast/index.html.gz: src/content/en/podcast/index.html
-src/content/en/screencast/index.html.gz: src/content/en/screencast/index.html
-src/content/en/til/index.html.gz: src/content/en/til/index.html
-src/content/pt/hea/index.html.gz: src/content/pt/hea/index.html
-src/content/en/blog/categories.html.gz: src/content/en/blog/categories.html
-src/content/en/pastebin/categories.html.gz: src/content/en/pastebin/categories.html
-src/content/en/podcast/categories.html.gz: src/content/en/podcast/categories.html
-src/content/en/screencast/categories.html.gz: src/content/en/screencast/categories.html
-src/content/en/til/categories.html.gz: src/content/en/til/categories.html
-src/content/pt/hea/categorias.html.gz: src/content/pt/hea/categorias.html
-src/content/en/about.snippets.gz: src/content/en/about.snippets
-src/content/en/index.snippets.gz: src/content/en/index.snippets
-src/content/pt/sobre.snippets.gz: src/content/pt/sobre.snippets
-src/content/en/blog/2018/07/17/guix-nixos.snippets.gz: src/content/en/blog/2018/07/17/guix-nixos.snippets
-src/content/en/blog/2018/08/01/npm-ci-reproducibility.snippets.gz: src/content/en/blog/2018/08/01/npm-ci-reproducibility.snippets
-src/content/en/blog/2018/12/21/ytdl-subs.snippets.gz: src/content/en/blog/2018/12/21/ytdl-subs.snippets
-src/content/en/blog/2019/06/02/nixos-stateless-workstation.snippets.gz: src/content/en/blog/2019/06/02/nixos-stateless-workstation.snippets
-src/content/en/blog/2020/08/10/guix-srht.snippets.gz: src/content/en/blog/2020/08/10/guix-srht.snippets
-src/content/en/blog/2020/08/31/database-i-wish-i-had.snippets.gz: src/content/en/blog/2020/08/31/database-i-wish-i-had.snippets
-src/content/en/blog/2020/10/05/cargo2nix.snippets.gz: src/content/en/blog/2020/10/05/cargo2nix.snippets
-src/content/en/blog/2020/10/05/swift2nix.snippets.gz: src/content/en/blog/2020/10/05/swift2nix.snippets
-src/content/en/blog/2020/10/19/feature-flags.snippets.gz: src/content/en/blog/2020/10/19/feature-flags.snippets
-src/content/en/blog/2020/10/20/wrong-interviewing.snippets.gz: src/content/en/blog/2020/10/20/wrong-interviewing.snippets
-src/content/en/blog/2020/11/07/diy-bugs.snippets.gz: src/content/en/blog/2020/11/07/diy-bugs.snippets
-src/content/en/blog/2020/11/08/paradigm-shift-review.snippets.gz: src/content/en/blog/2020/11/08/paradigm-shift-review.snippets
-src/content/en/blog/2020/11/12/database-parsers-trees.snippets.gz: src/content/en/blog/2020/11/12/database-parsers-trees.snippets
-src/content/en/blog/2020/11/14/local-first-review.snippets.gz: src/content/en/blog/2020/11/14/local-first-review.snippets
-src/content/en/blog/2021/01/26/remembering-ann.snippets.gz: src/content/en/blog/2021/01/26/remembering-ann.snippets
-src/content/en/blog/2021/02/17/fallible.snippets.gz: src/content/en/blog/2021/02/17/fallible.snippets
-src/content/en/blog/2021/04/29/relational-review.snippets.gz: src/content/en/blog/2021/04/29/relational-review.snippets
-src/content/en/pastebin/2016/04/05/rpn.snippets.gz: src/content/en/pastebin/2016/04/05/rpn.snippets
-src/content/en/pastebin/2018/07/11/nix-pinning.snippets.gz: src/content/en/pastebin/2018/07/11/nix-pinning.snippets
-src/content/en/pastebin/2018/07/13/guix-nixos-systemd.snippets.gz: src/content/en/pastebin/2018/07/13/guix-nixos-systemd.snippets
-src/content/en/pastebin/2018/07/13/guixbuilder-nixos.snippets.gz: src/content/en/pastebin/2018/07/13/guixbuilder-nixos.snippets
-src/content/en/pastebin/2018/07/13/guixbuilder.snippets.gz: src/content/en/pastebin/2018/07/13/guixbuilder.snippets
-src/content/en/pastebin/2018/07/13/nix-strpad.snippets.gz: src/content/en/pastebin/2018/07/13/nix-strpad.snippets
-src/content/en/pastebin/2018/07/25/nix-exps.snippets.gz: src/content/en/pastebin/2018/07/25/nix-exps.snippets
-src/content/en/pastebin/2018/07/25/nix-showdrv.snippets.gz: src/content/en/pastebin/2018/07/25/nix-showdrv.snippets
-src/content/en/pastebin/2019/06/08/inconsistent-hash.snippets.gz: src/content/en/pastebin/2019/06/08/inconsistent-hash.snippets
-src/content/en/pastebin/2019/12/29/raku-tuple-type.snippets.gz: src/content/en/pastebin/2019/12/29/raku-tuple-type.snippets
-src/content/en/pastebin/2020/01/04/guix-import-failure.snippets.gz: src/content/en/pastebin/2020/01/04/guix-import-failure.snippets
-src/content/en/pastebin/2020/02/14/guix-shebang.snippets.gz: src/content/en/pastebin/2020/02/14/guix-shebang.snippets
-src/content/en/pastebin/2020/11/27/guix-build-local.snippets.gz: src/content/en/pastebin/2020/11/27/guix-build-local.snippets
-src/content/en/pastebin/2020/12/15/guix-pack-fail.snippets.gz: src/content/en/pastebin/2020/12/15/guix-pack-fail.snippets
-src/content/en/pastebin/2021/04/03/naive-slugify-js.snippets.gz: src/content/en/pastebin/2021/04/03/naive-slugify-js.snippets
-src/content/en/pastebin/2021/06/08/reading-session-pt1.snippets.gz: src/content/en/pastebin/2021/06/08/reading-session-pt1.snippets
-src/content/en/pastebin/2021/06/22/curl-wget.snippets.gz: src/content/en/pastebin/2021/06/22/curl-wget.snippets
-src/content/en/pastebin/2021/08/11/h1-spacing.snippets.gz: src/content/en/pastebin/2021/08/11/h1-spacing.snippets
-src/content/en/pastebin/2021/09/02/sicp-3-19.snippets.gz: src/content/en/pastebin/2021/09/02/sicp-3-19.snippets
-src/content/en/pastebin/2021/09/03/sicp-persistent-queue.snippets.gz: src/content/en/pastebin/2021/09/03/sicp-persistent-queue.snippets
-src/content/en/pastebin/2022/07/14/git-cleanup.snippets.gz: src/content/en/pastebin/2022/07/14/git-cleanup.snippets
-src/content/en/pastebin/2023/07/22/funcallable-amop.snippets.gz: src/content/en/pastebin/2023/07/22/funcallable-amop.snippets
-src/content/en/podcast/2020/12/19/test-entry.snippets.gz: src/content/en/podcast/2020/12/19/test-entry.snippets
-src/content/en/screencast/2021/02/07/autoqemu.snippets.gz: src/content/en/screencast/2021/02/07/autoqemu.snippets
-src/content/en/til/2020/08/12/filename-timestamp.snippets.gz: src/content/en/til/2020/08/12/filename-timestamp.snippets
-src/content/en/til/2020/08/13/code-jekyll.snippets.gz: src/content/en/til/2020/08/13/code-jekyll.snippets
-src/content/en/til/2020/08/14/browse-git.snippets.gz: src/content/en/til/2020/08/14/browse-git.snippets
-src/content/en/til/2020/08/16/git-search.snippets.gz: src/content/en/til/2020/08/16/git-search.snippets
-src/content/en/til/2020/08/28/grep-online.snippets.gz: src/content/en/til/2020/08/28/grep-online.snippets
-src/content/en/til/2020/09/04/cli-email-fun-profit.snippets.gz: src/content/en/til/2020/09/04/cli-email-fun-profit.snippets
-src/content/en/til/2020/09/05/oldschool-pr.snippets.gz: src/content/en/til/2020/09/05/oldschool-pr.snippets
-src/content/en/til/2020/10/11/search-git-history.snippets.gz: src/content/en/til/2020/10/11/search-git-history.snippets
-src/content/en/til/2020/11/08/find-broken-symlink.snippets.gz: src/content/en/til/2020/11/08/find-broken-symlink.snippets
-src/content/en/til/2020/11/12/diy-nix-bash-ci.snippets.gz: src/content/en/til/2020/11/12/diy-nix-bash-ci.snippets
-src/content/en/til/2020/11/12/git-bisect-automation.snippets.gz: src/content/en/til/2020/11/12/git-bisect-automation.snippets
-src/content/en/til/2020/11/12/useful-bashvars.snippets.gz: src/content/en/til/2020/11/12/useful-bashvars.snippets
-src/content/en/til/2020/11/14/gpodder-media.snippets.gz: src/content/en/til/2020/11/14/gpodder-media.snippets
-src/content/en/til/2020/11/30/git-notes-ci.snippets.gz: src/content/en/til/2020/11/30/git-notes-ci.snippets
-src/content/en/til/2020/12/15/shellcheck-repo.snippets.gz: src/content/en/til/2020/12/15/shellcheck-repo.snippets
-src/content/en/til/2020/12/29/svg.snippets.gz: src/content/en/til/2020/12/29/svg.snippets
-src/content/en/til/2021/01/12/curl-awk-emails.snippets.gz: src/content/en/til/2021/01/12/curl-awk-emails.snippets
-src/content/en/til/2021/01/17/posix-shebang.snippets.gz: src/content/en/til/2021/01/17/posix-shebang.snippets
-src/content/en/til/2021/04/24/cl-generic-precedence.snippets.gz: src/content/en/til/2021/04/24/cl-generic-precedence.snippets
-src/content/en/til/2021/04/24/clojure-autocurry.snippets.gz: src/content/en/til/2021/04/24/clojure-autocurry.snippets
-src/content/en/til/2021/04/24/scm-nif.snippets.gz: src/content/en/til/2021/04/24/scm-nif.snippets
-src/content/en/til/2021/07/23/git-tls-gpg.snippets.gz: src/content/en/til/2021/07/23/git-tls-gpg.snippets
-src/content/en/til/2021/08/11/js-bigint-reviver.snippets.gz: src/content/en/til/2021/08/11/js-bigint-reviver.snippets
-src/content/pt/hea/2020/08/12/arquivo-datado.snippets.gz: src/content/pt/hea/2020/08/12/arquivo-datado.snippets
-src/content/en/blog/index.snippets.gz: src/content/en/blog/index.snippets
-src/content/en/pastebin/index.snippets.gz: src/content/en/pastebin/index.snippets
-src/content/en/podcast/index.snippets.gz: src/content/en/podcast/index.snippets
-src/content/en/screencast/index.snippets.gz: src/content/en/screencast/index.snippets
-src/content/en/til/index.snippets.gz: src/content/en/til/index.snippets
-src/content/pt/hea/index.snippets.gz: src/content/pt/hea/index.snippets
-src/content/en/blog/categories.snippets.gz: src/content/en/blog/categories.snippets
-src/content/en/pastebin/categories.snippets.gz: src/content/en/pastebin/categories.snippets
-src/content/en/podcast/categories.snippets.gz: src/content/en/podcast/categories.snippets
-src/content/en/screencast/categories.snippets.gz: src/content/en/screencast/categories.snippets
-src/content/en/til/categories.snippets.gz: src/content/en/til/categories.snippets
-src/content/pt/hea/categorias.snippets.gz: src/content/pt/hea/categorias.snippets
-src/content/en/slide/2020/10/19/feature-flags.pdf.gz: src/content/en/slide/2020/10/19/feature-flags.pdf
-src/content/en/slide/2020/11/14/local-first-hype.pdf.gz: src/content/en/slide/2020/11/14/local-first-hype.pdf
-src/content/en/blog/feed.xml.gz: src/content/en/blog/feed.xml
-src/content/en/pastebin/feed.xml.gz: src/content/en/pastebin/feed.xml
-src/content/en/podcast/feed.xml.gz: src/content/en/podcast/feed.xml
-src/content/en/screencast/feed.xml.gz: src/content/en/screencast/feed.xml
-src/content/en/til/feed.xml.gz: src/content/en/til/feed.xml
-src/content/pt/hea/feed.xml.gz: src/content/pt/hea/feed.xml
-src/content/en/podcast/2020/12/19/test-entry.flac.torrent.gz: src/content/en/podcast/2020/12/19/test-entry.flac.torrent
-src/content/en/podcast/2020/12/19/test-entry.ogg.torrent.gz: src/content/en/podcast/2020/12/19/test-entry.ogg.torrent
-src/content/en/screencast/2021/02/07/autoqemu.webm.torrent.gz: src/content/en/screencast/2021/02/07/autoqemu.webm.torrent
+music.ly = \
+ src/content/music/choro-da-saudade.ly \
+ src/content/music/dengoso.ly \
+ src/content/music/marcha-dos-marinheiros.ly \
+
+music.ogg = \
+ src/content/music/choro-da-saudade.ogg \
+ src/content/music/dengoso.ogg \
+ src/content/music/marcha-dos-marinheiros.ogg \
+
+music.include = \
+ src/content/music/common.ly.include \
+ src/content/music/guitar.ly.include \
-src/content/en/about.htmlbody src/content/en/about.snippets src/content/en/about.conf: src/content/en/about.adoc
-src/content/en/index.htmlbody src/content/en/index.snippets src/content/en/index.conf: src/content/en/index.adoc
-src/content/pt/sobre.htmlbody src/content/pt/sobre.snippets src/content/pt/sobre.conf: src/content/pt/sobre.adoc
-src/content/en/blog/2018/07/17/guix-nixos.htmlbody src/content/en/blog/2018/07/17/guix-nixos.snippets src/content/en/blog/2018/07/17/guix-nixos.conf: src/content/en/blog/2018/07/17/guix-nixos.adoc
-src/content/en/blog/2018/08/01/npm-ci-reproducibility.htmlbody src/content/en/blog/2018/08/01/npm-ci-reproducibility.snippets src/content/en/blog/2018/08/01/npm-ci-reproducibility.conf: src/content/en/blog/2018/08/01/npm-ci-reproducibility.adoc
-src/content/en/blog/2018/12/21/ytdl-subs.htmlbody src/content/en/blog/2018/12/21/ytdl-subs.snippets src/content/en/blog/2018/12/21/ytdl-subs.conf: src/content/en/blog/2018/12/21/ytdl-subs.adoc
-src/content/en/blog/2019/06/02/nixos-stateless-workstation.htmlbody src/content/en/blog/2019/06/02/nixos-stateless-workstation.snippets src/content/en/blog/2019/06/02/nixos-stateless-workstation.conf: src/content/en/blog/2019/06/02/nixos-stateless-workstation.adoc
-src/content/en/blog/2020/08/10/guix-srht.htmlbody src/content/en/blog/2020/08/10/guix-srht.snippets src/content/en/blog/2020/08/10/guix-srht.conf: src/content/en/blog/2020/08/10/guix-srht.adoc
-src/content/en/blog/2020/08/31/database-i-wish-i-had.htmlbody src/content/en/blog/2020/08/31/database-i-wish-i-had.snippets src/content/en/blog/2020/08/31/database-i-wish-i-had.conf: src/content/en/blog/2020/08/31/database-i-wish-i-had.adoc
-src/content/en/blog/2020/10/05/cargo2nix.htmlbody src/content/en/blog/2020/10/05/cargo2nix.snippets src/content/en/blog/2020/10/05/cargo2nix.conf: src/content/en/blog/2020/10/05/cargo2nix.adoc
-src/content/en/blog/2020/10/05/swift2nix.htmlbody src/content/en/blog/2020/10/05/swift2nix.snippets src/content/en/blog/2020/10/05/swift2nix.conf: src/content/en/blog/2020/10/05/swift2nix.adoc
-src/content/en/blog/2020/10/19/feature-flags.htmlbody src/content/en/blog/2020/10/19/feature-flags.snippets src/content/en/blog/2020/10/19/feature-flags.conf: src/content/en/blog/2020/10/19/feature-flags.adoc
-src/content/en/blog/2020/10/20/wrong-interviewing.htmlbody src/content/en/blog/2020/10/20/wrong-interviewing.snippets src/content/en/blog/2020/10/20/wrong-interviewing.conf: src/content/en/blog/2020/10/20/wrong-interviewing.adoc
-src/content/en/blog/2020/11/07/diy-bugs.htmlbody src/content/en/blog/2020/11/07/diy-bugs.snippets src/content/en/blog/2020/11/07/diy-bugs.conf: src/content/en/blog/2020/11/07/diy-bugs.adoc
-src/content/en/blog/2020/11/08/paradigm-shift-review.htmlbody src/content/en/blog/2020/11/08/paradigm-shift-review.snippets src/content/en/blog/2020/11/08/paradigm-shift-review.conf: src/content/en/blog/2020/11/08/paradigm-shift-review.adoc
-src/content/en/blog/2020/11/12/database-parsers-trees.htmlbody src/content/en/blog/2020/11/12/database-parsers-trees.snippets src/content/en/blog/2020/11/12/database-parsers-trees.conf: src/content/en/blog/2020/11/12/database-parsers-trees.adoc
-src/content/en/blog/2020/11/14/local-first-review.htmlbody src/content/en/blog/2020/11/14/local-first-review.snippets src/content/en/blog/2020/11/14/local-first-review.conf: src/content/en/blog/2020/11/14/local-first-review.adoc
-src/content/en/blog/2021/01/26/remembering-ann.htmlbody src/content/en/blog/2021/01/26/remembering-ann.snippets src/content/en/blog/2021/01/26/remembering-ann.conf: src/content/en/blog/2021/01/26/remembering-ann.adoc
-src/content/en/blog/2021/02/17/fallible.htmlbody src/content/en/blog/2021/02/17/fallible.snippets src/content/en/blog/2021/02/17/fallible.conf: src/content/en/blog/2021/02/17/fallible.adoc
-src/content/en/blog/2021/04/29/relational-review.htmlbody src/content/en/blog/2021/04/29/relational-review.snippets src/content/en/blog/2021/04/29/relational-review.conf: src/content/en/blog/2021/04/29/relational-review.adoc
-src/content/en/pastebin/2016/04/05/rpn.htmlbody src/content/en/pastebin/2016/04/05/rpn.snippets src/content/en/pastebin/2016/04/05/rpn.conf: src/content/en/pastebin/2016/04/05/rpn.adoc
-src/content/en/pastebin/2018/07/11/nix-pinning.htmlbody src/content/en/pastebin/2018/07/11/nix-pinning.snippets src/content/en/pastebin/2018/07/11/nix-pinning.conf: src/content/en/pastebin/2018/07/11/nix-pinning.adoc
-src/content/en/pastebin/2018/07/13/guix-nixos-systemd.htmlbody src/content/en/pastebin/2018/07/13/guix-nixos-systemd.snippets src/content/en/pastebin/2018/07/13/guix-nixos-systemd.conf: src/content/en/pastebin/2018/07/13/guix-nixos-systemd.adoc
-src/content/en/pastebin/2018/07/13/guixbuilder-nixos.htmlbody src/content/en/pastebin/2018/07/13/guixbuilder-nixos.snippets src/content/en/pastebin/2018/07/13/guixbuilder-nixos.conf: src/content/en/pastebin/2018/07/13/guixbuilder-nixos.adoc
-src/content/en/pastebin/2018/07/13/guixbuilder.htmlbody src/content/en/pastebin/2018/07/13/guixbuilder.snippets src/content/en/pastebin/2018/07/13/guixbuilder.conf: src/content/en/pastebin/2018/07/13/guixbuilder.adoc
-src/content/en/pastebin/2018/07/13/nix-strpad.htmlbody src/content/en/pastebin/2018/07/13/nix-strpad.snippets src/content/en/pastebin/2018/07/13/nix-strpad.conf: src/content/en/pastebin/2018/07/13/nix-strpad.adoc
-src/content/en/pastebin/2018/07/25/nix-exps.htmlbody src/content/en/pastebin/2018/07/25/nix-exps.snippets src/content/en/pastebin/2018/07/25/nix-exps.conf: src/content/en/pastebin/2018/07/25/nix-exps.adoc
-src/content/en/pastebin/2018/07/25/nix-showdrv.htmlbody src/content/en/pastebin/2018/07/25/nix-showdrv.snippets src/content/en/pastebin/2018/07/25/nix-showdrv.conf: src/content/en/pastebin/2018/07/25/nix-showdrv.adoc
-src/content/en/pastebin/2019/06/08/inconsistent-hash.htmlbody src/content/en/pastebin/2019/06/08/inconsistent-hash.snippets src/content/en/pastebin/2019/06/08/inconsistent-hash.conf: src/content/en/pastebin/2019/06/08/inconsistent-hash.adoc
-src/content/en/pastebin/2019/12/29/raku-tuple-type.htmlbody src/content/en/pastebin/2019/12/29/raku-tuple-type.snippets src/content/en/pastebin/2019/12/29/raku-tuple-type.conf: src/content/en/pastebin/2019/12/29/raku-tuple-type.adoc
-src/content/en/pastebin/2020/01/04/guix-import-failure.htmlbody src/content/en/pastebin/2020/01/04/guix-import-failure.snippets src/content/en/pastebin/2020/01/04/guix-import-failure.conf: src/content/en/pastebin/2020/01/04/guix-import-failure.adoc
-src/content/en/pastebin/2020/02/14/guix-shebang.htmlbody src/content/en/pastebin/2020/02/14/guix-shebang.snippets src/content/en/pastebin/2020/02/14/guix-shebang.conf: src/content/en/pastebin/2020/02/14/guix-shebang.adoc
-src/content/en/pastebin/2020/11/27/guix-build-local.htmlbody src/content/en/pastebin/2020/11/27/guix-build-local.snippets src/content/en/pastebin/2020/11/27/guix-build-local.conf: src/content/en/pastebin/2020/11/27/guix-build-local.adoc
-src/content/en/pastebin/2020/12/15/guix-pack-fail.htmlbody src/content/en/pastebin/2020/12/15/guix-pack-fail.snippets src/content/en/pastebin/2020/12/15/guix-pack-fail.conf: src/content/en/pastebin/2020/12/15/guix-pack-fail.adoc
-src/content/en/pastebin/2021/04/03/naive-slugify-js.htmlbody src/content/en/pastebin/2021/04/03/naive-slugify-js.snippets src/content/en/pastebin/2021/04/03/naive-slugify-js.conf: src/content/en/pastebin/2021/04/03/naive-slugify-js.adoc
-src/content/en/pastebin/2021/06/08/reading-session-pt1.htmlbody src/content/en/pastebin/2021/06/08/reading-session-pt1.snippets src/content/en/pastebin/2021/06/08/reading-session-pt1.conf: src/content/en/pastebin/2021/06/08/reading-session-pt1.adoc
-src/content/en/pastebin/2021/06/22/curl-wget.htmlbody src/content/en/pastebin/2021/06/22/curl-wget.snippets src/content/en/pastebin/2021/06/22/curl-wget.conf: src/content/en/pastebin/2021/06/22/curl-wget.adoc
-src/content/en/pastebin/2021/08/11/h1-spacing.htmlbody src/content/en/pastebin/2021/08/11/h1-spacing.snippets src/content/en/pastebin/2021/08/11/h1-spacing.conf: src/content/en/pastebin/2021/08/11/h1-spacing.adoc
-src/content/en/pastebin/2021/09/02/sicp-3-19.htmlbody src/content/en/pastebin/2021/09/02/sicp-3-19.snippets src/content/en/pastebin/2021/09/02/sicp-3-19.conf: src/content/en/pastebin/2021/09/02/sicp-3-19.adoc
-src/content/en/pastebin/2021/09/03/sicp-persistent-queue.htmlbody src/content/en/pastebin/2021/09/03/sicp-persistent-queue.snippets src/content/en/pastebin/2021/09/03/sicp-persistent-queue.conf: src/content/en/pastebin/2021/09/03/sicp-persistent-queue.adoc
-src/content/en/pastebin/2022/07/14/git-cleanup.htmlbody src/content/en/pastebin/2022/07/14/git-cleanup.snippets src/content/en/pastebin/2022/07/14/git-cleanup.conf: src/content/en/pastebin/2022/07/14/git-cleanup.adoc
-src/content/en/pastebin/2023/07/22/funcallable-amop.htmlbody src/content/en/pastebin/2023/07/22/funcallable-amop.snippets src/content/en/pastebin/2023/07/22/funcallable-amop.conf: src/content/en/pastebin/2023/07/22/funcallable-amop.adoc
-src/content/en/podcast/2020/12/19/test-entry.htmlbody src/content/en/podcast/2020/12/19/test-entry.snippets src/content/en/podcast/2020/12/19/test-entry.conf: src/content/en/podcast/2020/12/19/test-entry.adoc
-src/content/en/screencast/2021/02/07/autoqemu.htmlbody src/content/en/screencast/2021/02/07/autoqemu.snippets src/content/en/screencast/2021/02/07/autoqemu.conf: src/content/en/screencast/2021/02/07/autoqemu.adoc
-src/content/en/til/2020/08/12/filename-timestamp.htmlbody src/content/en/til/2020/08/12/filename-timestamp.snippets src/content/en/til/2020/08/12/filename-timestamp.conf: src/content/en/til/2020/08/12/filename-timestamp.adoc
-src/content/en/til/2020/08/13/code-jekyll.htmlbody src/content/en/til/2020/08/13/code-jekyll.snippets src/content/en/til/2020/08/13/code-jekyll.conf: src/content/en/til/2020/08/13/code-jekyll.adoc
-src/content/en/til/2020/08/14/browse-git.htmlbody src/content/en/til/2020/08/14/browse-git.snippets src/content/en/til/2020/08/14/browse-git.conf: src/content/en/til/2020/08/14/browse-git.adoc
-src/content/en/til/2020/08/16/git-search.htmlbody src/content/en/til/2020/08/16/git-search.snippets src/content/en/til/2020/08/16/git-search.conf: src/content/en/til/2020/08/16/git-search.adoc
-src/content/en/til/2020/08/28/grep-online.htmlbody src/content/en/til/2020/08/28/grep-online.snippets src/content/en/til/2020/08/28/grep-online.conf: src/content/en/til/2020/08/28/grep-online.adoc
-src/content/en/til/2020/09/04/cli-email-fun-profit.htmlbody src/content/en/til/2020/09/04/cli-email-fun-profit.snippets src/content/en/til/2020/09/04/cli-email-fun-profit.conf: src/content/en/til/2020/09/04/cli-email-fun-profit.adoc
-src/content/en/til/2020/09/05/oldschool-pr.htmlbody src/content/en/til/2020/09/05/oldschool-pr.snippets src/content/en/til/2020/09/05/oldschool-pr.conf: src/content/en/til/2020/09/05/oldschool-pr.adoc
-src/content/en/til/2020/10/11/search-git-history.htmlbody src/content/en/til/2020/10/11/search-git-history.snippets src/content/en/til/2020/10/11/search-git-history.conf: src/content/en/til/2020/10/11/search-git-history.adoc
-src/content/en/til/2020/11/08/find-broken-symlink.htmlbody src/content/en/til/2020/11/08/find-broken-symlink.snippets src/content/en/til/2020/11/08/find-broken-symlink.conf: src/content/en/til/2020/11/08/find-broken-symlink.adoc
-src/content/en/til/2020/11/12/diy-nix-bash-ci.htmlbody src/content/en/til/2020/11/12/diy-nix-bash-ci.snippets src/content/en/til/2020/11/12/diy-nix-bash-ci.conf: src/content/en/til/2020/11/12/diy-nix-bash-ci.adoc
-src/content/en/til/2020/11/12/git-bisect-automation.htmlbody src/content/en/til/2020/11/12/git-bisect-automation.snippets src/content/en/til/2020/11/12/git-bisect-automation.conf: src/content/en/til/2020/11/12/git-bisect-automation.adoc
-src/content/en/til/2020/11/12/useful-bashvars.htmlbody src/content/en/til/2020/11/12/useful-bashvars.snippets src/content/en/til/2020/11/12/useful-bashvars.conf: src/content/en/til/2020/11/12/useful-bashvars.adoc
-src/content/en/til/2020/11/14/gpodder-media.htmlbody src/content/en/til/2020/11/14/gpodder-media.snippets src/content/en/til/2020/11/14/gpodder-media.conf: src/content/en/til/2020/11/14/gpodder-media.adoc
-src/content/en/til/2020/11/30/git-notes-ci.htmlbody src/content/en/til/2020/11/30/git-notes-ci.snippets src/content/en/til/2020/11/30/git-notes-ci.conf: src/content/en/til/2020/11/30/git-notes-ci.adoc
-src/content/en/til/2020/12/15/shellcheck-repo.htmlbody src/content/en/til/2020/12/15/shellcheck-repo.snippets src/content/en/til/2020/12/15/shellcheck-repo.conf: src/content/en/til/2020/12/15/shellcheck-repo.adoc
-src/content/en/til/2020/12/29/svg.htmlbody src/content/en/til/2020/12/29/svg.snippets src/content/en/til/2020/12/29/svg.conf: src/content/en/til/2020/12/29/svg.adoc
-src/content/en/til/2021/01/12/curl-awk-emails.htmlbody src/content/en/til/2021/01/12/curl-awk-emails.snippets src/content/en/til/2021/01/12/curl-awk-emails.conf: src/content/en/til/2021/01/12/curl-awk-emails.adoc
-src/content/en/til/2021/01/17/posix-shebang.htmlbody src/content/en/til/2021/01/17/posix-shebang.snippets src/content/en/til/2021/01/17/posix-shebang.conf: src/content/en/til/2021/01/17/posix-shebang.adoc
-src/content/en/til/2021/04/24/cl-generic-precedence.htmlbody src/content/en/til/2021/04/24/cl-generic-precedence.snippets src/content/en/til/2021/04/24/cl-generic-precedence.conf: src/content/en/til/2021/04/24/cl-generic-precedence.adoc
-src/content/en/til/2021/04/24/clojure-autocurry.htmlbody src/content/en/til/2021/04/24/clojure-autocurry.snippets src/content/en/til/2021/04/24/clojure-autocurry.conf: src/content/en/til/2021/04/24/clojure-autocurry.adoc
-src/content/en/til/2021/04/24/scm-nif.htmlbody src/content/en/til/2021/04/24/scm-nif.snippets src/content/en/til/2021/04/24/scm-nif.conf: src/content/en/til/2021/04/24/scm-nif.adoc
-src/content/en/til/2021/07/23/git-tls-gpg.htmlbody src/content/en/til/2021/07/23/git-tls-gpg.snippets src/content/en/til/2021/07/23/git-tls-gpg.conf: src/content/en/til/2021/07/23/git-tls-gpg.adoc
-src/content/en/til/2021/08/11/js-bigint-reviver.htmlbody src/content/en/til/2021/08/11/js-bigint-reviver.snippets src/content/en/til/2021/08/11/js-bigint-reviver.conf: src/content/en/til/2021/08/11/js-bigint-reviver.adoc
-src/content/pt/hea/2020/08/12/arquivo-datado.htmlbody src/content/pt/hea/2020/08/12/arquivo-datado.snippets src/content/pt/hea/2020/08/12/arquivo-datado.conf: src/content/pt/hea/2020/08/12/arquivo-datado.adoc
-src/content/en/blog/index.htmlbody src/content/en/blog/index.snippets src/content/en/blog/index.conf: src/content/en/blog/index.adoc
-src/content/en/pastebin/index.htmlbody src/content/en/pastebin/index.snippets src/content/en/pastebin/index.conf: src/content/en/pastebin/index.adoc
-src/content/en/podcast/index.htmlbody src/content/en/podcast/index.snippets src/content/en/podcast/index.conf: src/content/en/podcast/index.adoc
-src/content/en/screencast/index.htmlbody src/content/en/screencast/index.snippets src/content/en/screencast/index.conf: src/content/en/screencast/index.adoc
-src/content/en/til/index.htmlbody src/content/en/til/index.snippets src/content/en/til/index.conf: src/content/en/til/index.adoc
-src/content/pt/hea/index.htmlbody src/content/pt/hea/index.snippets src/content/pt/hea/index.conf: src/content/pt/hea/index.adoc
-src/content/en/blog/categories.htmlbody src/content/en/blog/categories.snippets src/content/en/blog/categories.conf: src/content/en/blog/categories.adoc
-src/content/en/pastebin/categories.htmlbody src/content/en/pastebin/categories.snippets src/content/en/pastebin/categories.conf: src/content/en/pastebin/categories.adoc
-src/content/en/podcast/categories.htmlbody src/content/en/podcast/categories.snippets src/content/en/podcast/categories.conf: src/content/en/podcast/categories.adoc
-src/content/en/screencast/categories.htmlbody src/content/en/screencast/categories.snippets src/content/en/screencast/categories.conf: src/content/en/screencast/categories.adoc
-src/content/en/til/categories.htmlbody src/content/en/til/categories.snippets src/content/en/til/categories.conf: src/content/en/til/categories.adoc
-src/content/pt/hea/categorias.htmlbody src/content/pt/hea/categorias.snippets src/content/pt/hea/categorias.conf: src/content/pt/hea/categorias.adoc
-src/content/en/about.html: src/content/en/about.conf src/content/en/about.htmlbody
-src/content/en/index.html: src/content/en/index.conf src/content/en/index.htmlbody
-src/content/pt/sobre.html: src/content/pt/sobre.conf src/content/pt/sobre.htmlbody
-src/content/en/blog/2018/07/17/guix-nixos.html: src/content/en/blog/2018/07/17/guix-nixos.conf src/content/en/blog/2018/07/17/guix-nixos.htmlbody
-src/content/en/blog/2018/08/01/npm-ci-reproducibility.html: src/content/en/blog/2018/08/01/npm-ci-reproducibility.conf src/content/en/blog/2018/08/01/npm-ci-reproducibility.htmlbody
-src/content/en/blog/2018/12/21/ytdl-subs.html: src/content/en/blog/2018/12/21/ytdl-subs.conf src/content/en/blog/2018/12/21/ytdl-subs.htmlbody
-src/content/en/blog/2019/06/02/nixos-stateless-workstation.html: src/content/en/blog/2019/06/02/nixos-stateless-workstation.conf src/content/en/blog/2019/06/02/nixos-stateless-workstation.htmlbody
-src/content/en/blog/2020/08/10/guix-srht.html: src/content/en/blog/2020/08/10/guix-srht.conf src/content/en/blog/2020/08/10/guix-srht.htmlbody
-src/content/en/blog/2020/08/31/database-i-wish-i-had.html: src/content/en/blog/2020/08/31/database-i-wish-i-had.conf src/content/en/blog/2020/08/31/database-i-wish-i-had.htmlbody
-src/content/en/blog/2020/10/05/cargo2nix.html: src/content/en/blog/2020/10/05/cargo2nix.conf src/content/en/blog/2020/10/05/cargo2nix.htmlbody
-src/content/en/blog/2020/10/05/swift2nix.html: src/content/en/blog/2020/10/05/swift2nix.conf src/content/en/blog/2020/10/05/swift2nix.htmlbody
-src/content/en/blog/2020/10/19/feature-flags.html: src/content/en/blog/2020/10/19/feature-flags.conf src/content/en/blog/2020/10/19/feature-flags.htmlbody
-src/content/en/blog/2020/10/20/wrong-interviewing.html: src/content/en/blog/2020/10/20/wrong-interviewing.conf src/content/en/blog/2020/10/20/wrong-interviewing.htmlbody
-src/content/en/blog/2020/11/07/diy-bugs.html: src/content/en/blog/2020/11/07/diy-bugs.conf src/content/en/blog/2020/11/07/diy-bugs.htmlbody
-src/content/en/blog/2020/11/08/paradigm-shift-review.html: src/content/en/blog/2020/11/08/paradigm-shift-review.conf src/content/en/blog/2020/11/08/paradigm-shift-review.htmlbody
-src/content/en/blog/2020/11/12/database-parsers-trees.html: src/content/en/blog/2020/11/12/database-parsers-trees.conf src/content/en/blog/2020/11/12/database-parsers-trees.htmlbody
-src/content/en/blog/2020/11/14/local-first-review.html: src/content/en/blog/2020/11/14/local-first-review.conf src/content/en/blog/2020/11/14/local-first-review.htmlbody
-src/content/en/blog/2021/01/26/remembering-ann.html: src/content/en/blog/2021/01/26/remembering-ann.conf src/content/en/blog/2021/01/26/remembering-ann.htmlbody
-src/content/en/blog/2021/02/17/fallible.html: src/content/en/blog/2021/02/17/fallible.conf src/content/en/blog/2021/02/17/fallible.htmlbody
-src/content/en/blog/2021/04/29/relational-review.html: src/content/en/blog/2021/04/29/relational-review.conf src/content/en/blog/2021/04/29/relational-review.htmlbody
-src/content/en/pastebin/2016/04/05/rpn.html: src/content/en/pastebin/2016/04/05/rpn.conf src/content/en/pastebin/2016/04/05/rpn.htmlbody
-src/content/en/pastebin/2018/07/11/nix-pinning.html: src/content/en/pastebin/2018/07/11/nix-pinning.conf src/content/en/pastebin/2018/07/11/nix-pinning.htmlbody
-src/content/en/pastebin/2018/07/13/guix-nixos-systemd.html: src/content/en/pastebin/2018/07/13/guix-nixos-systemd.conf src/content/en/pastebin/2018/07/13/guix-nixos-systemd.htmlbody
-src/content/en/pastebin/2018/07/13/guixbuilder-nixos.html: src/content/en/pastebin/2018/07/13/guixbuilder-nixos.conf src/content/en/pastebin/2018/07/13/guixbuilder-nixos.htmlbody
-src/content/en/pastebin/2018/07/13/guixbuilder.html: src/content/en/pastebin/2018/07/13/guixbuilder.conf src/content/en/pastebin/2018/07/13/guixbuilder.htmlbody
-src/content/en/pastebin/2018/07/13/nix-strpad.html: src/content/en/pastebin/2018/07/13/nix-strpad.conf src/content/en/pastebin/2018/07/13/nix-strpad.htmlbody
-src/content/en/pastebin/2018/07/25/nix-exps.html: src/content/en/pastebin/2018/07/25/nix-exps.conf src/content/en/pastebin/2018/07/25/nix-exps.htmlbody
-src/content/en/pastebin/2018/07/25/nix-showdrv.html: src/content/en/pastebin/2018/07/25/nix-showdrv.conf src/content/en/pastebin/2018/07/25/nix-showdrv.htmlbody
-src/content/en/pastebin/2019/06/08/inconsistent-hash.html: src/content/en/pastebin/2019/06/08/inconsistent-hash.conf src/content/en/pastebin/2019/06/08/inconsistent-hash.htmlbody
-src/content/en/pastebin/2019/12/29/raku-tuple-type.html: src/content/en/pastebin/2019/12/29/raku-tuple-type.conf src/content/en/pastebin/2019/12/29/raku-tuple-type.htmlbody
-src/content/en/pastebin/2020/01/04/guix-import-failure.html: src/content/en/pastebin/2020/01/04/guix-import-failure.conf src/content/en/pastebin/2020/01/04/guix-import-failure.htmlbody
-src/content/en/pastebin/2020/02/14/guix-shebang.html: src/content/en/pastebin/2020/02/14/guix-shebang.conf src/content/en/pastebin/2020/02/14/guix-shebang.htmlbody
-src/content/en/pastebin/2020/11/27/guix-build-local.html: src/content/en/pastebin/2020/11/27/guix-build-local.conf src/content/en/pastebin/2020/11/27/guix-build-local.htmlbody
-src/content/en/pastebin/2020/12/15/guix-pack-fail.html: src/content/en/pastebin/2020/12/15/guix-pack-fail.conf src/content/en/pastebin/2020/12/15/guix-pack-fail.htmlbody
-src/content/en/pastebin/2021/04/03/naive-slugify-js.html: src/content/en/pastebin/2021/04/03/naive-slugify-js.conf src/content/en/pastebin/2021/04/03/naive-slugify-js.htmlbody
-src/content/en/pastebin/2021/06/08/reading-session-pt1.html: src/content/en/pastebin/2021/06/08/reading-session-pt1.conf src/content/en/pastebin/2021/06/08/reading-session-pt1.htmlbody
-src/content/en/pastebin/2021/06/22/curl-wget.html: src/content/en/pastebin/2021/06/22/curl-wget.conf src/content/en/pastebin/2021/06/22/curl-wget.htmlbody
-src/content/en/pastebin/2021/08/11/h1-spacing.html: src/content/en/pastebin/2021/08/11/h1-spacing.conf src/content/en/pastebin/2021/08/11/h1-spacing.htmlbody
-src/content/en/pastebin/2021/09/02/sicp-3-19.html: src/content/en/pastebin/2021/09/02/sicp-3-19.conf src/content/en/pastebin/2021/09/02/sicp-3-19.htmlbody
-src/content/en/pastebin/2021/09/03/sicp-persistent-queue.html: src/content/en/pastebin/2021/09/03/sicp-persistent-queue.conf src/content/en/pastebin/2021/09/03/sicp-persistent-queue.htmlbody
-src/content/en/pastebin/2022/07/14/git-cleanup.html: src/content/en/pastebin/2022/07/14/git-cleanup.conf src/content/en/pastebin/2022/07/14/git-cleanup.htmlbody
-src/content/en/pastebin/2023/07/22/funcallable-amop.html: src/content/en/pastebin/2023/07/22/funcallable-amop.conf src/content/en/pastebin/2023/07/22/funcallable-amop.htmlbody
-src/content/en/podcast/2020/12/19/test-entry.html: src/content/en/podcast/2020/12/19/test-entry.conf src/content/en/podcast/2020/12/19/test-entry.htmlbody
-src/content/en/screencast/2021/02/07/autoqemu.html: src/content/en/screencast/2021/02/07/autoqemu.conf src/content/en/screencast/2021/02/07/autoqemu.htmlbody
-src/content/en/til/2020/08/12/filename-timestamp.html: src/content/en/til/2020/08/12/filename-timestamp.conf src/content/en/til/2020/08/12/filename-timestamp.htmlbody
-src/content/en/til/2020/08/13/code-jekyll.html: src/content/en/til/2020/08/13/code-jekyll.conf src/content/en/til/2020/08/13/code-jekyll.htmlbody
-src/content/en/til/2020/08/14/browse-git.html: src/content/en/til/2020/08/14/browse-git.conf src/content/en/til/2020/08/14/browse-git.htmlbody
-src/content/en/til/2020/08/16/git-search.html: src/content/en/til/2020/08/16/git-search.conf src/content/en/til/2020/08/16/git-search.htmlbody
-src/content/en/til/2020/08/28/grep-online.html: src/content/en/til/2020/08/28/grep-online.conf src/content/en/til/2020/08/28/grep-online.htmlbody
-src/content/en/til/2020/09/04/cli-email-fun-profit.html: src/content/en/til/2020/09/04/cli-email-fun-profit.conf src/content/en/til/2020/09/04/cli-email-fun-profit.htmlbody
-src/content/en/til/2020/09/05/oldschool-pr.html: src/content/en/til/2020/09/05/oldschool-pr.conf src/content/en/til/2020/09/05/oldschool-pr.htmlbody
-src/content/en/til/2020/10/11/search-git-history.html: src/content/en/til/2020/10/11/search-git-history.conf src/content/en/til/2020/10/11/search-git-history.htmlbody
-src/content/en/til/2020/11/08/find-broken-symlink.html: src/content/en/til/2020/11/08/find-broken-symlink.conf src/content/en/til/2020/11/08/find-broken-symlink.htmlbody
-src/content/en/til/2020/11/12/diy-nix-bash-ci.html: src/content/en/til/2020/11/12/diy-nix-bash-ci.conf src/content/en/til/2020/11/12/diy-nix-bash-ci.htmlbody
-src/content/en/til/2020/11/12/git-bisect-automation.html: src/content/en/til/2020/11/12/git-bisect-automation.conf src/content/en/til/2020/11/12/git-bisect-automation.htmlbody
-src/content/en/til/2020/11/12/useful-bashvars.html: src/content/en/til/2020/11/12/useful-bashvars.conf src/content/en/til/2020/11/12/useful-bashvars.htmlbody
-src/content/en/til/2020/11/14/gpodder-media.html: src/content/en/til/2020/11/14/gpodder-media.conf src/content/en/til/2020/11/14/gpodder-media.htmlbody
-src/content/en/til/2020/11/30/git-notes-ci.html: src/content/en/til/2020/11/30/git-notes-ci.conf src/content/en/til/2020/11/30/git-notes-ci.htmlbody
-src/content/en/til/2020/12/15/shellcheck-repo.html: src/content/en/til/2020/12/15/shellcheck-repo.conf src/content/en/til/2020/12/15/shellcheck-repo.htmlbody
-src/content/en/til/2020/12/29/svg.html: src/content/en/til/2020/12/29/svg.conf src/content/en/til/2020/12/29/svg.htmlbody
-src/content/en/til/2021/01/12/curl-awk-emails.html: src/content/en/til/2021/01/12/curl-awk-emails.conf src/content/en/til/2021/01/12/curl-awk-emails.htmlbody
-src/content/en/til/2021/01/17/posix-shebang.html: src/content/en/til/2021/01/17/posix-shebang.conf src/content/en/til/2021/01/17/posix-shebang.htmlbody
-src/content/en/til/2021/04/24/cl-generic-precedence.html: src/content/en/til/2021/04/24/cl-generic-precedence.conf src/content/en/til/2021/04/24/cl-generic-precedence.htmlbody
-src/content/en/til/2021/04/24/clojure-autocurry.html: src/content/en/til/2021/04/24/clojure-autocurry.conf src/content/en/til/2021/04/24/clojure-autocurry.htmlbody
-src/content/en/til/2021/04/24/scm-nif.html: src/content/en/til/2021/04/24/scm-nif.conf src/content/en/til/2021/04/24/scm-nif.htmlbody
-src/content/en/til/2021/07/23/git-tls-gpg.html: src/content/en/til/2021/07/23/git-tls-gpg.conf src/content/en/til/2021/07/23/git-tls-gpg.htmlbody
-src/content/en/til/2021/08/11/js-bigint-reviver.html: src/content/en/til/2021/08/11/js-bigint-reviver.conf src/content/en/til/2021/08/11/js-bigint-reviver.htmlbody
-src/content/pt/hea/2020/08/12/arquivo-datado.html: src/content/pt/hea/2020/08/12/arquivo-datado.conf src/content/pt/hea/2020/08/12/arquivo-datado.htmlbody
-src/content/en/blog/index.html: src/content/en/blog/index.conf src/content/en/blog/index.htmlbody
-src/content/en/pastebin/index.html: src/content/en/pastebin/index.conf src/content/en/pastebin/index.htmlbody
-src/content/en/podcast/index.html: src/content/en/podcast/index.conf src/content/en/podcast/index.htmlbody
-src/content/en/screencast/index.html: src/content/en/screencast/index.conf src/content/en/screencast/index.htmlbody
-src/content/en/til/index.html: src/content/en/til/index.conf src/content/en/til/index.htmlbody
-src/content/pt/hea/index.html: src/content/pt/hea/index.conf src/content/pt/hea/index.htmlbody
-src/content/en/blog/categories.html: src/content/en/blog/categories.conf src/content/en/blog/categories.htmlbody
-src/content/en/pastebin/categories.html: src/content/en/pastebin/categories.conf src/content/en/pastebin/categories.htmlbody
-src/content/en/podcast/categories.html: src/content/en/podcast/categories.conf src/content/en/podcast/categories.htmlbody
-src/content/en/screencast/categories.html: src/content/en/screencast/categories.conf src/content/en/screencast/categories.htmlbody
-src/content/en/til/categories.html: src/content/en/til/categories.conf src/content/en/til/categories.htmlbody
-src/content/pt/hea/categorias.html: src/content/pt/hea/categorias.conf src/content/pt/hea/categorias.htmlbody
+src/content/music/choro-da-saudade.pdf: src/content/music/choro-da-saudade.midi
+src/content/music/dengoso.pdf: src/content/music/dengoso.midi
+src/content/music/marcha-dos-marinheiros.pdf: src/content/music/marcha-dos-marinheiros.midi
+src/content/de/musik.htmlbody src/content/de/musik.snippets src/content/de/musik.json: src/content/de/musik.adoc
+src/content/de/ueber.htmlbody src/content/de/ueber.snippets src/content/de/ueber.json: src/content/de/ueber.adoc
+src/content/en/about.htmlbody src/content/en/about.snippets src/content/en/about.json: src/content/en/about.adoc
+src/content/en/music.htmlbody src/content/en/music.snippets src/content/en/music.json: src/content/en/music.adoc
+src/content/eo/muziko.htmlbody src/content/eo/muziko.snippets src/content/eo/muziko.json: src/content/eo/muziko.adoc
+src/content/eo/pri.htmlbody src/content/eo/pri.snippets src/content/eo/pri.json: src/content/eo/pri.adoc
+src/content/es/acerca.htmlbody src/content/es/acerca.snippets src/content/es/acerca.json: src/content/es/acerca.adoc
+src/content/es/musica.htmlbody src/content/es/musica.snippets src/content/es/musica.json: src/content/es/musica.adoc
+src/content/fr/a-propos.htmlbody src/content/fr/a-propos.snippets src/content/fr/a-propos.json: src/content/fr/a-propos.adoc
+src/content/fr/musique.htmlbody src/content/fr/musique.snippets src/content/fr/musique.json: src/content/fr/musique.adoc
+src/content/pt/musica.htmlbody src/content/pt/musica.snippets src/content/pt/musica.json: src/content/pt/musica.adoc
+src/content/pt/sobre.htmlbody src/content/pt/sobre.snippets src/content/pt/sobre.json: src/content/pt/sobre.adoc
+src/content/en/blog/2018/07/17/guix-nixos.htmlbody src/content/en/blog/2018/07/17/guix-nixos.snippets src/content/en/blog/2018/07/17/guix-nixos.json: src/content/en/blog/2018/07/17/guix-nixos.adoc
+src/content/en/blog/2018/08/01/npm-ci-reproducibility.htmlbody src/content/en/blog/2018/08/01/npm-ci-reproducibility.snippets src/content/en/blog/2018/08/01/npm-ci-reproducibility.json: src/content/en/blog/2018/08/01/npm-ci-reproducibility.adoc
+src/content/en/blog/2018/12/21/ytdl-subs.htmlbody src/content/en/blog/2018/12/21/ytdl-subs.snippets src/content/en/blog/2018/12/21/ytdl-subs.json: src/content/en/blog/2018/12/21/ytdl-subs.adoc
+src/content/en/blog/2019/06/02/nixos-stateless-workstation.htmlbody src/content/en/blog/2019/06/02/nixos-stateless-workstation.snippets src/content/en/blog/2019/06/02/nixos-stateless-workstation.json: src/content/en/blog/2019/06/02/nixos-stateless-workstation.adoc
+src/content/en/blog/2020/08/10/guix-srht.htmlbody src/content/en/blog/2020/08/10/guix-srht.snippets src/content/en/blog/2020/08/10/guix-srht.json: src/content/en/blog/2020/08/10/guix-srht.adoc
+src/content/en/blog/2020/08/31/database-i-wish-i-had.htmlbody src/content/en/blog/2020/08/31/database-i-wish-i-had.snippets src/content/en/blog/2020/08/31/database-i-wish-i-had.json: src/content/en/blog/2020/08/31/database-i-wish-i-had.adoc
+src/content/en/blog/2020/10/05/cargo2nix.htmlbody src/content/en/blog/2020/10/05/cargo2nix.snippets src/content/en/blog/2020/10/05/cargo2nix.json: src/content/en/blog/2020/10/05/cargo2nix.adoc
+src/content/en/blog/2020/10/05/swift2nix.htmlbody src/content/en/blog/2020/10/05/swift2nix.snippets src/content/en/blog/2020/10/05/swift2nix.json: src/content/en/blog/2020/10/05/swift2nix.adoc
+src/content/en/blog/2020/10/19/feature-flags.htmlbody src/content/en/blog/2020/10/19/feature-flags.snippets src/content/en/blog/2020/10/19/feature-flags.json: src/content/en/blog/2020/10/19/feature-flags.adoc
+src/content/en/blog/2020/10/20/wrong-interviewing.htmlbody src/content/en/blog/2020/10/20/wrong-interviewing.snippets src/content/en/blog/2020/10/20/wrong-interviewing.json: src/content/en/blog/2020/10/20/wrong-interviewing.adoc
+src/content/en/blog/2020/11/07/diy-bugs.htmlbody src/content/en/blog/2020/11/07/diy-bugs.snippets src/content/en/blog/2020/11/07/diy-bugs.json: src/content/en/blog/2020/11/07/diy-bugs.adoc
+src/content/en/blog/2020/11/08/paradigm-shift-review.htmlbody src/content/en/blog/2020/11/08/paradigm-shift-review.snippets src/content/en/blog/2020/11/08/paradigm-shift-review.json: src/content/en/blog/2020/11/08/paradigm-shift-review.adoc
+src/content/en/blog/2020/11/12/database-parsers-trees.htmlbody src/content/en/blog/2020/11/12/database-parsers-trees.snippets src/content/en/blog/2020/11/12/database-parsers-trees.json: src/content/en/blog/2020/11/12/database-parsers-trees.adoc
+src/content/en/blog/2020/11/14/local-first-review.htmlbody src/content/en/blog/2020/11/14/local-first-review.snippets src/content/en/blog/2020/11/14/local-first-review.json: src/content/en/blog/2020/11/14/local-first-review.adoc
+src/content/en/blog/2021/01/26/remembering-ann.htmlbody src/content/en/blog/2021/01/26/remembering-ann.snippets src/content/en/blog/2021/01/26/remembering-ann.json: src/content/en/blog/2021/01/26/remembering-ann.adoc
+src/content/en/blog/2021/02/17/fallible.htmlbody src/content/en/blog/2021/02/17/fallible.snippets src/content/en/blog/2021/02/17/fallible.json: src/content/en/blog/2021/02/17/fallible.adoc
+src/content/en/blog/2021/04/29/relational-review.htmlbody src/content/en/blog/2021/04/29/relational-review.snippets src/content/en/blog/2021/04/29/relational-review.json: src/content/en/blog/2021/04/29/relational-review.adoc
+src/content/en/pastebin/2016/04/05/rpn.htmlbody src/content/en/pastebin/2016/04/05/rpn.snippets src/content/en/pastebin/2016/04/05/rpn.json: src/content/en/pastebin/2016/04/05/rpn.adoc
+src/content/en/pastebin/2018/07/11/nix-pinning.htmlbody src/content/en/pastebin/2018/07/11/nix-pinning.snippets src/content/en/pastebin/2018/07/11/nix-pinning.json: src/content/en/pastebin/2018/07/11/nix-pinning.adoc
+src/content/en/pastebin/2018/07/13/guix-nixos-systemd.htmlbody src/content/en/pastebin/2018/07/13/guix-nixos-systemd.snippets src/content/en/pastebin/2018/07/13/guix-nixos-systemd.json: src/content/en/pastebin/2018/07/13/guix-nixos-systemd.adoc
+src/content/en/pastebin/2018/07/13/guixbuilder-nixos.htmlbody src/content/en/pastebin/2018/07/13/guixbuilder-nixos.snippets src/content/en/pastebin/2018/07/13/guixbuilder-nixos.json: src/content/en/pastebin/2018/07/13/guixbuilder-nixos.adoc
+src/content/en/pastebin/2018/07/13/guixbuilder.htmlbody src/content/en/pastebin/2018/07/13/guixbuilder.snippets src/content/en/pastebin/2018/07/13/guixbuilder.json: src/content/en/pastebin/2018/07/13/guixbuilder.adoc
+src/content/en/pastebin/2018/07/13/nix-strpad.htmlbody src/content/en/pastebin/2018/07/13/nix-strpad.snippets src/content/en/pastebin/2018/07/13/nix-strpad.json: src/content/en/pastebin/2018/07/13/nix-strpad.adoc
+src/content/en/pastebin/2018/07/25/nix-exps.htmlbody src/content/en/pastebin/2018/07/25/nix-exps.snippets src/content/en/pastebin/2018/07/25/nix-exps.json: src/content/en/pastebin/2018/07/25/nix-exps.adoc
+src/content/en/pastebin/2018/07/25/nix-showdrv.htmlbody src/content/en/pastebin/2018/07/25/nix-showdrv.snippets src/content/en/pastebin/2018/07/25/nix-showdrv.json: src/content/en/pastebin/2018/07/25/nix-showdrv.adoc
+src/content/en/pastebin/2019/06/08/inconsistent-hash.htmlbody src/content/en/pastebin/2019/06/08/inconsistent-hash.snippets src/content/en/pastebin/2019/06/08/inconsistent-hash.json: src/content/en/pastebin/2019/06/08/inconsistent-hash.adoc
+src/content/en/pastebin/2019/12/29/raku-tuple-type.htmlbody src/content/en/pastebin/2019/12/29/raku-tuple-type.snippets src/content/en/pastebin/2019/12/29/raku-tuple-type.json: src/content/en/pastebin/2019/12/29/raku-tuple-type.adoc
+src/content/en/pastebin/2020/01/04/guix-import-failure.htmlbody src/content/en/pastebin/2020/01/04/guix-import-failure.snippets src/content/en/pastebin/2020/01/04/guix-import-failure.json: src/content/en/pastebin/2020/01/04/guix-import-failure.adoc
+src/content/en/pastebin/2020/02/14/guix-shebang.htmlbody src/content/en/pastebin/2020/02/14/guix-shebang.snippets src/content/en/pastebin/2020/02/14/guix-shebang.json: src/content/en/pastebin/2020/02/14/guix-shebang.adoc
+src/content/en/pastebin/2020/11/27/guix-build-local.htmlbody src/content/en/pastebin/2020/11/27/guix-build-local.snippets src/content/en/pastebin/2020/11/27/guix-build-local.json: src/content/en/pastebin/2020/11/27/guix-build-local.adoc
+src/content/en/pastebin/2020/12/15/guix-pack-fail.htmlbody src/content/en/pastebin/2020/12/15/guix-pack-fail.snippets src/content/en/pastebin/2020/12/15/guix-pack-fail.json: src/content/en/pastebin/2020/12/15/guix-pack-fail.adoc
+src/content/en/pastebin/2021/04/03/naive-slugify-js.htmlbody src/content/en/pastebin/2021/04/03/naive-slugify-js.snippets src/content/en/pastebin/2021/04/03/naive-slugify-js.json: src/content/en/pastebin/2021/04/03/naive-slugify-js.adoc
+src/content/en/pastebin/2021/06/08/reading-session-pt1.htmlbody src/content/en/pastebin/2021/06/08/reading-session-pt1.snippets src/content/en/pastebin/2021/06/08/reading-session-pt1.json: src/content/en/pastebin/2021/06/08/reading-session-pt1.adoc
+src/content/en/pastebin/2021/06/22/curl-wget.htmlbody src/content/en/pastebin/2021/06/22/curl-wget.snippets src/content/en/pastebin/2021/06/22/curl-wget.json: src/content/en/pastebin/2021/06/22/curl-wget.adoc
+src/content/en/pastebin/2021/08/11/h1-spacing.htmlbody src/content/en/pastebin/2021/08/11/h1-spacing.snippets src/content/en/pastebin/2021/08/11/h1-spacing.json: src/content/en/pastebin/2021/08/11/h1-spacing.adoc
+src/content/en/pastebin/2021/09/02/sicp-3-19.htmlbody src/content/en/pastebin/2021/09/02/sicp-3-19.snippets src/content/en/pastebin/2021/09/02/sicp-3-19.json: src/content/en/pastebin/2021/09/02/sicp-3-19.adoc
+src/content/en/pastebin/2021/09/03/sicp-persistent-queue.htmlbody src/content/en/pastebin/2021/09/03/sicp-persistent-queue.snippets src/content/en/pastebin/2021/09/03/sicp-persistent-queue.json: src/content/en/pastebin/2021/09/03/sicp-persistent-queue.adoc
+src/content/en/pastebin/2022/07/14/git-cleanup.htmlbody src/content/en/pastebin/2022/07/14/git-cleanup.snippets src/content/en/pastebin/2022/07/14/git-cleanup.json: src/content/en/pastebin/2022/07/14/git-cleanup.adoc
+src/content/en/pastebin/2023/07/22/funcallable-amop.htmlbody src/content/en/pastebin/2023/07/22/funcallable-amop.snippets src/content/en/pastebin/2023/07/22/funcallable-amop.json: src/content/en/pastebin/2023/07/22/funcallable-amop.adoc
+src/content/en/podcast/2020/12/19/test-entry.htmlbody src/content/en/podcast/2020/12/19/test-entry.snippets src/content/en/podcast/2020/12/19/test-entry.json: src/content/en/podcast/2020/12/19/test-entry.adoc
+src/content/en/screencast/2021/02/07/autoqemu.htmlbody src/content/en/screencast/2021/02/07/autoqemu.snippets src/content/en/screencast/2021/02/07/autoqemu.json: src/content/en/screencast/2021/02/07/autoqemu.adoc
+src/content/en/slide/2020/10/19/feature-flags.htmlbody src/content/en/slide/2020/10/19/feature-flags.snippets src/content/en/slide/2020/10/19/feature-flags.json: src/content/en/slide/2020/10/19/feature-flags.adoc
+src/content/en/slide/2020/11/14/local-first-hype.htmlbody src/content/en/slide/2020/11/14/local-first-hype.snippets src/content/en/slide/2020/11/14/local-first-hype.json: src/content/en/slide/2020/11/14/local-first-hype.adoc
+src/content/en/til/2020/08/12/filename-timestamp.htmlbody src/content/en/til/2020/08/12/filename-timestamp.snippets src/content/en/til/2020/08/12/filename-timestamp.json: src/content/en/til/2020/08/12/filename-timestamp.adoc
+src/content/en/til/2020/08/13/code-jekyll.htmlbody src/content/en/til/2020/08/13/code-jekyll.snippets src/content/en/til/2020/08/13/code-jekyll.json: src/content/en/til/2020/08/13/code-jekyll.adoc
+src/content/en/til/2020/08/14/browse-git.htmlbody src/content/en/til/2020/08/14/browse-git.snippets src/content/en/til/2020/08/14/browse-git.json: src/content/en/til/2020/08/14/browse-git.adoc
+src/content/en/til/2020/08/16/git-search.htmlbody src/content/en/til/2020/08/16/git-search.snippets src/content/en/til/2020/08/16/git-search.json: src/content/en/til/2020/08/16/git-search.adoc
+src/content/en/til/2020/08/28/grep-online.htmlbody src/content/en/til/2020/08/28/grep-online.snippets src/content/en/til/2020/08/28/grep-online.json: src/content/en/til/2020/08/28/grep-online.adoc
+src/content/en/til/2020/09/04/cli-email-fun-profit.htmlbody src/content/en/til/2020/09/04/cli-email-fun-profit.snippets src/content/en/til/2020/09/04/cli-email-fun-profit.json: src/content/en/til/2020/09/04/cli-email-fun-profit.adoc
+src/content/en/til/2020/09/05/oldschool-pr.htmlbody src/content/en/til/2020/09/05/oldschool-pr.snippets src/content/en/til/2020/09/05/oldschool-pr.json: src/content/en/til/2020/09/05/oldschool-pr.adoc
+src/content/en/til/2020/10/11/search-git-history.htmlbody src/content/en/til/2020/10/11/search-git-history.snippets src/content/en/til/2020/10/11/search-git-history.json: src/content/en/til/2020/10/11/search-git-history.adoc
+src/content/en/til/2020/11/08/find-broken-symlink.htmlbody src/content/en/til/2020/11/08/find-broken-symlink.snippets src/content/en/til/2020/11/08/find-broken-symlink.json: src/content/en/til/2020/11/08/find-broken-symlink.adoc
+src/content/en/til/2020/11/12/diy-nix-bash-ci.htmlbody src/content/en/til/2020/11/12/diy-nix-bash-ci.snippets src/content/en/til/2020/11/12/diy-nix-bash-ci.json: src/content/en/til/2020/11/12/diy-nix-bash-ci.adoc
+src/content/en/til/2020/11/12/git-bisect-automation.htmlbody src/content/en/til/2020/11/12/git-bisect-automation.snippets src/content/en/til/2020/11/12/git-bisect-automation.json: src/content/en/til/2020/11/12/git-bisect-automation.adoc
+src/content/en/til/2020/11/12/useful-bashvars.htmlbody src/content/en/til/2020/11/12/useful-bashvars.snippets src/content/en/til/2020/11/12/useful-bashvars.json: src/content/en/til/2020/11/12/useful-bashvars.adoc
+src/content/en/til/2020/11/14/gpodder-media.htmlbody src/content/en/til/2020/11/14/gpodder-media.snippets src/content/en/til/2020/11/14/gpodder-media.json: src/content/en/til/2020/11/14/gpodder-media.adoc
+src/content/en/til/2020/11/30/git-notes-ci.htmlbody src/content/en/til/2020/11/30/git-notes-ci.snippets src/content/en/til/2020/11/30/git-notes-ci.json: src/content/en/til/2020/11/30/git-notes-ci.adoc
+src/content/en/til/2020/12/15/shellcheck-repo.htmlbody src/content/en/til/2020/12/15/shellcheck-repo.snippets src/content/en/til/2020/12/15/shellcheck-repo.json: src/content/en/til/2020/12/15/shellcheck-repo.adoc
+src/content/en/til/2020/12/29/svg.htmlbody src/content/en/til/2020/12/29/svg.snippets src/content/en/til/2020/12/29/svg.json: src/content/en/til/2020/12/29/svg.adoc
+src/content/en/til/2021/01/12/curl-awk-emails.htmlbody src/content/en/til/2021/01/12/curl-awk-emails.snippets src/content/en/til/2021/01/12/curl-awk-emails.json: src/content/en/til/2021/01/12/curl-awk-emails.adoc
+src/content/en/til/2021/01/17/posix-shebang.htmlbody src/content/en/til/2021/01/17/posix-shebang.snippets src/content/en/til/2021/01/17/posix-shebang.json: src/content/en/til/2021/01/17/posix-shebang.adoc
+src/content/en/til/2021/04/24/cl-generic-precedence.htmlbody src/content/en/til/2021/04/24/cl-generic-precedence.snippets src/content/en/til/2021/04/24/cl-generic-precedence.json: src/content/en/til/2021/04/24/cl-generic-precedence.adoc
+src/content/en/til/2021/04/24/clojure-autocurry.htmlbody src/content/en/til/2021/04/24/clojure-autocurry.snippets src/content/en/til/2021/04/24/clojure-autocurry.json: src/content/en/til/2021/04/24/clojure-autocurry.adoc
+src/content/en/til/2021/04/24/scm-nif.htmlbody src/content/en/til/2021/04/24/scm-nif.snippets src/content/en/til/2021/04/24/scm-nif.json: src/content/en/til/2021/04/24/scm-nif.adoc
+src/content/en/til/2021/07/23/git-tls-gpg.htmlbody src/content/en/til/2021/07/23/git-tls-gpg.snippets src/content/en/til/2021/07/23/git-tls-gpg.json: src/content/en/til/2021/07/23/git-tls-gpg.adoc
+src/content/en/til/2021/08/11/js-bigint-reviver.htmlbody src/content/en/til/2021/08/11/js-bigint-reviver.snippets src/content/en/til/2021/08/11/js-bigint-reviver.json: src/content/en/til/2021/08/11/js-bigint-reviver.adoc
+src/content/pt/hea/2020/08/12/arquivo-datado.htmlbody src/content/pt/hea/2020/08/12/arquivo-datado.snippets src/content/pt/hea/2020/08/12/arquivo-datado.json: src/content/pt/hea/2020/08/12/arquivo-datado.adoc
+src/content/de/blog/index.htmlbody src/content/de/blog/index.snippets src/content/de/blog/index.json: src/content/de/blog/index.adoc
+src/content/de/folien/index.htmlbody src/content/de/folien/index.snippets src/content/de/folien/index.json: src/content/de/folien/index.adoc
+src/content/de/hhg/index.htmlbody src/content/de/hhg/index.snippets src/content/de/hhg/index.json: src/content/de/hhg/index.adoc
+src/content/de/pastebin/index.htmlbody src/content/de/pastebin/index.snippets src/content/de/pastebin/index.json: src/content/de/pastebin/index.adoc
+src/content/de/podcast/index.htmlbody src/content/de/podcast/index.snippets src/content/de/podcast/index.json: src/content/de/podcast/index.adoc
+src/content/de/screencast/index.htmlbody src/content/de/screencast/index.snippets src/content/de/screencast/index.json: src/content/de/screencast/index.adoc
+src/content/en/blog/index.htmlbody src/content/en/blog/index.snippets src/content/en/blog/index.json: src/content/en/blog/index.adoc
+src/content/en/pastebin/index.htmlbody src/content/en/pastebin/index.snippets src/content/en/pastebin/index.json: src/content/en/pastebin/index.adoc
+src/content/en/podcast/index.htmlbody src/content/en/podcast/index.snippets src/content/en/podcast/index.json: src/content/en/podcast/index.adoc
+src/content/en/screencast/index.htmlbody src/content/en/screencast/index.snippets src/content/en/screencast/index.json: src/content/en/screencast/index.adoc
+src/content/en/slide/index.htmlbody src/content/en/slide/index.snippets src/content/en/slide/index.json: src/content/en/slide/index.adoc
+src/content/en/til/index.htmlbody src/content/en/til/index.snippets src/content/en/til/index.json: src/content/en/til/index.adoc
+src/content/eo/blog/index.htmlbody src/content/eo/blog/index.snippets src/content/eo/blog/index.json: src/content/eo/blog/index.adoc
+src/content/eo/hml/index.htmlbody src/content/eo/hml/index.snippets src/content/eo/hml/index.json: src/content/eo/hml/index.adoc
+src/content/eo/lumbildoj/index.htmlbody src/content/eo/lumbildoj/index.snippets src/content/eo/lumbildoj/index.json: src/content/eo/lumbildoj/index.adoc
+src/content/eo/pastebin/index.htmlbody src/content/eo/pastebin/index.snippets src/content/eo/pastebin/index.json: src/content/eo/pastebin/index.adoc
+src/content/eo/podkasto/index.htmlbody src/content/eo/podkasto/index.snippets src/content/eo/podkasto/index.json: src/content/eo/podkasto/index.adoc
+src/content/eo/screencast/index.htmlbody src/content/eo/screencast/index.snippets src/content/eo/screencast/index.json: src/content/eo/screencast/index.adoc
+src/content/es/blog/index.htmlbody src/content/es/blog/index.snippets src/content/es/blog/index.json: src/content/es/blog/index.adoc
+src/content/es/diapositivas/index.htmlbody src/content/es/diapositivas/index.snippets src/content/es/diapositivas/index.json: src/content/es/diapositivas/index.adoc
+src/content/es/haa/index.htmlbody src/content/es/haa/index.snippets src/content/es/haa/index.json: src/content/es/haa/index.adoc
+src/content/es/pastebin/index.htmlbody src/content/es/pastebin/index.snippets src/content/es/pastebin/index.json: src/content/es/pastebin/index.adoc
+src/content/es/podcast/index.htmlbody src/content/es/podcast/index.snippets src/content/es/podcast/index.json: src/content/es/podcast/index.adoc
+src/content/es/screencast/index.htmlbody src/content/es/screencast/index.snippets src/content/es/screencast/index.json: src/content/es/screencast/index.adoc
+src/content/fr/aja/index.htmlbody src/content/fr/aja/index.snippets src/content/fr/aja/index.json: src/content/fr/aja/index.adoc
+src/content/fr/blog/index.htmlbody src/content/fr/blog/index.snippets src/content/fr/blog/index.json: src/content/fr/blog/index.adoc
+src/content/fr/diapositives/index.htmlbody src/content/fr/diapositives/index.snippets src/content/fr/diapositives/index.json: src/content/fr/diapositives/index.adoc
+src/content/fr/pastebin/index.htmlbody src/content/fr/pastebin/index.snippets src/content/fr/pastebin/index.json: src/content/fr/pastebin/index.adoc
+src/content/fr/podcast/index.htmlbody src/content/fr/podcast/index.snippets src/content/fr/podcast/index.json: src/content/fr/podcast/index.adoc
+src/content/fr/screencast/index.htmlbody src/content/fr/screencast/index.snippets src/content/fr/screencast/index.json: src/content/fr/screencast/index.adoc
+src/content/pt/blog/index.htmlbody src/content/pt/blog/index.snippets src/content/pt/blog/index.json: src/content/pt/blog/index.adoc
+src/content/pt/eslaides/index.htmlbody src/content/pt/eslaides/index.snippets src/content/pt/eslaides/index.json: src/content/pt/eslaides/index.adoc
+src/content/pt/hea/index.htmlbody src/content/pt/hea/index.snippets src/content/pt/hea/index.json: src/content/pt/hea/index.adoc
+src/content/pt/pastebin/index.htmlbody src/content/pt/pastebin/index.snippets src/content/pt/pastebin/index.json: src/content/pt/pastebin/index.adoc
+src/content/pt/podcast/index.htmlbody src/content/pt/podcast/index.snippets src/content/pt/podcast/index.json: src/content/pt/podcast/index.adoc
+src/content/pt/screencast/index.htmlbody src/content/pt/screencast/index.snippets src/content/pt/screencast/index.json: src/content/pt/screencast/index.adoc
+src/content/de/blog/kategorien.htmlbody src/content/de/blog/kategorien.snippets src/content/de/blog/kategorien.json: src/content/de/blog/kategorien.adoc
+src/content/de/folien/kategorien.htmlbody src/content/de/folien/kategorien.snippets src/content/de/folien/kategorien.json: src/content/de/folien/kategorien.adoc
+src/content/de/hhg/kategorien.htmlbody src/content/de/hhg/kategorien.snippets src/content/de/hhg/kategorien.json: src/content/de/hhg/kategorien.adoc
+src/content/de/pastebin/kategorien.htmlbody src/content/de/pastebin/kategorien.snippets src/content/de/pastebin/kategorien.json: src/content/de/pastebin/kategorien.adoc
+src/content/de/podcast/kategorien.htmlbody src/content/de/podcast/kategorien.snippets src/content/de/podcast/kategorien.json: src/content/de/podcast/kategorien.adoc
+src/content/de/screencast/kategorien.htmlbody src/content/de/screencast/kategorien.snippets src/content/de/screencast/kategorien.json: src/content/de/screencast/kategorien.adoc
+src/content/en/blog/categories.htmlbody src/content/en/blog/categories.snippets src/content/en/blog/categories.json: src/content/en/blog/categories.adoc
+src/content/en/pastebin/categories.htmlbody src/content/en/pastebin/categories.snippets src/content/en/pastebin/categories.json: src/content/en/pastebin/categories.adoc
+src/content/en/podcast/categories.htmlbody src/content/en/podcast/categories.snippets src/content/en/podcast/categories.json: src/content/en/podcast/categories.adoc
+src/content/en/screencast/categories.htmlbody src/content/en/screencast/categories.snippets src/content/en/screencast/categories.json: src/content/en/screencast/categories.adoc
+src/content/en/slide/categories.htmlbody src/content/en/slide/categories.snippets src/content/en/slide/categories.json: src/content/en/slide/categories.adoc
+src/content/en/til/categories.htmlbody src/content/en/til/categories.snippets src/content/en/til/categories.json: src/content/en/til/categories.adoc
+src/content/eo/blog/kategorioj.htmlbody src/content/eo/blog/kategorioj.snippets src/content/eo/blog/kategorioj.json: src/content/eo/blog/kategorioj.adoc
+src/content/eo/hml/kategorioj.htmlbody src/content/eo/hml/kategorioj.snippets src/content/eo/hml/kategorioj.json: src/content/eo/hml/kategorioj.adoc
+src/content/eo/lumbildoj/kategorioj.htmlbody src/content/eo/lumbildoj/kategorioj.snippets src/content/eo/lumbildoj/kategorioj.json: src/content/eo/lumbildoj/kategorioj.adoc
+src/content/eo/pastebin/kategorioj.htmlbody src/content/eo/pastebin/kategorioj.snippets src/content/eo/pastebin/kategorioj.json: src/content/eo/pastebin/kategorioj.adoc
+src/content/eo/podkasto/kategorioj.htmlbody src/content/eo/podkasto/kategorioj.snippets src/content/eo/podkasto/kategorioj.json: src/content/eo/podkasto/kategorioj.adoc
+src/content/eo/screencast/kategorioj.htmlbody src/content/eo/screencast/kategorioj.snippets src/content/eo/screencast/kategorioj.json: src/content/eo/screencast/kategorioj.adoc
+src/content/es/blog/categorias.htmlbody src/content/es/blog/categorias.snippets src/content/es/blog/categorias.json: src/content/es/blog/categorias.adoc
+src/content/es/diapositivas/categorias.htmlbody src/content/es/diapositivas/categorias.snippets src/content/es/diapositivas/categorias.json: src/content/es/diapositivas/categorias.adoc
+src/content/es/haa/categorias.htmlbody src/content/es/haa/categorias.snippets src/content/es/haa/categorias.json: src/content/es/haa/categorias.adoc
+src/content/es/pastebin/categorias.htmlbody src/content/es/pastebin/categorias.snippets src/content/es/pastebin/categorias.json: src/content/es/pastebin/categorias.adoc
+src/content/es/podcast/categorias.htmlbody src/content/es/podcast/categorias.snippets src/content/es/podcast/categorias.json: src/content/es/podcast/categorias.adoc
+src/content/es/screencast/categorias.htmlbody src/content/es/screencast/categorias.snippets src/content/es/screencast/categorias.json: src/content/es/screencast/categorias.adoc
+src/content/fr/aja/categories.htmlbody src/content/fr/aja/categories.snippets src/content/fr/aja/categories.json: src/content/fr/aja/categories.adoc
+src/content/fr/blog/categories.htmlbody src/content/fr/blog/categories.snippets src/content/fr/blog/categories.json: src/content/fr/blog/categories.adoc
+src/content/fr/diapositives/categories.htmlbody src/content/fr/diapositives/categories.snippets src/content/fr/diapositives/categories.json: src/content/fr/diapositives/categories.adoc
+src/content/fr/pastebin/categories.htmlbody src/content/fr/pastebin/categories.snippets src/content/fr/pastebin/categories.json: src/content/fr/pastebin/categories.adoc
+src/content/fr/podcast/categories.htmlbody src/content/fr/podcast/categories.snippets src/content/fr/podcast/categories.json: src/content/fr/podcast/categories.adoc
+src/content/fr/screencast/categories.htmlbody src/content/fr/screencast/categories.snippets src/content/fr/screencast/categories.json: src/content/fr/screencast/categories.adoc
+src/content/pt/blog/categorias.htmlbody src/content/pt/blog/categorias.snippets src/content/pt/blog/categorias.json: src/content/pt/blog/categorias.adoc
+src/content/pt/eslaides/categorias.htmlbody src/content/pt/eslaides/categorias.snippets src/content/pt/eslaides/categorias.json: src/content/pt/eslaides/categorias.adoc
+src/content/pt/hea/categorias.htmlbody src/content/pt/hea/categorias.snippets src/content/pt/hea/categorias.json: src/content/pt/hea/categorias.adoc
+src/content/pt/pastebin/categorias.htmlbody src/content/pt/pastebin/categorias.snippets src/content/pt/pastebin/categorias.json: src/content/pt/pastebin/categorias.adoc
+src/content/pt/podcast/categorias.htmlbody src/content/pt/podcast/categorias.snippets src/content/pt/podcast/categorias.json: src/content/pt/podcast/categorias.adoc
+src/content/pt/screencast/categorias.htmlbody src/content/pt/screencast/categorias.snippets src/content/pt/screencast/categorias.json: src/content/pt/screencast/categorias.adoc
+src/content/de/index.htmlbody src/content/de/index.snippets src/content/de/index.json: src/content/de/index.adoc
+src/content/en/index.htmlbody src/content/en/index.snippets src/content/en/index.json: src/content/en/index.adoc
+src/content/eo/index.htmlbody src/content/eo/index.snippets src/content/eo/index.json: src/content/eo/index.adoc
+src/content/es/index.htmlbody src/content/es/index.snippets src/content/es/index.json: src/content/es/index.adoc
+src/content/fr/index.htmlbody src/content/fr/index.snippets src/content/fr/index.json: src/content/fr/index.adoc
+src/content/pt/index.htmlbody src/content/pt/index.snippets src/content/pt/index.json: src/content/pt/index.adoc
+src/content/de/musik.html: src/content/de/musik.json src/content/de/musik.htmlbody
+src/content/de/ueber.html: src/content/de/ueber.json src/content/de/ueber.htmlbody
+src/content/en/about.html: src/content/en/about.json src/content/en/about.htmlbody
+src/content/en/music.html: src/content/en/music.json src/content/en/music.htmlbody
+src/content/eo/muziko.html: src/content/eo/muziko.json src/content/eo/muziko.htmlbody
+src/content/eo/pri.html: src/content/eo/pri.json src/content/eo/pri.htmlbody
+src/content/es/acerca.html: src/content/es/acerca.json src/content/es/acerca.htmlbody
+src/content/es/musica.html: src/content/es/musica.json src/content/es/musica.htmlbody
+src/content/fr/a-propos.html: src/content/fr/a-propos.json src/content/fr/a-propos.htmlbody
+src/content/fr/musique.html: src/content/fr/musique.json src/content/fr/musique.htmlbody
+src/content/pt/musica.html: src/content/pt/musica.json src/content/pt/musica.htmlbody
+src/content/pt/sobre.html: src/content/pt/sobre.json src/content/pt/sobre.htmlbody
+src/content/en/blog/2018/07/17/guix-nixos.html: src/content/en/blog/2018/07/17/guix-nixos.json src/content/en/blog/2018/07/17/guix-nixos.htmlbody
+src/content/en/blog/2018/08/01/npm-ci-reproducibility.html: src/content/en/blog/2018/08/01/npm-ci-reproducibility.json src/content/en/blog/2018/08/01/npm-ci-reproducibility.htmlbody
+src/content/en/blog/2018/12/21/ytdl-subs.html: src/content/en/blog/2018/12/21/ytdl-subs.json src/content/en/blog/2018/12/21/ytdl-subs.htmlbody
+src/content/en/blog/2019/06/02/nixos-stateless-workstation.html: src/content/en/blog/2019/06/02/nixos-stateless-workstation.json src/content/en/blog/2019/06/02/nixos-stateless-workstation.htmlbody
+src/content/en/blog/2020/08/10/guix-srht.html: src/content/en/blog/2020/08/10/guix-srht.json src/content/en/blog/2020/08/10/guix-srht.htmlbody
+src/content/en/blog/2020/08/31/database-i-wish-i-had.html: src/content/en/blog/2020/08/31/database-i-wish-i-had.json src/content/en/blog/2020/08/31/database-i-wish-i-had.htmlbody
+src/content/en/blog/2020/10/05/cargo2nix.html: src/content/en/blog/2020/10/05/cargo2nix.json src/content/en/blog/2020/10/05/cargo2nix.htmlbody
+src/content/en/blog/2020/10/05/swift2nix.html: src/content/en/blog/2020/10/05/swift2nix.json src/content/en/blog/2020/10/05/swift2nix.htmlbody
+src/content/en/blog/2020/10/19/feature-flags.html: src/content/en/blog/2020/10/19/feature-flags.json src/content/en/blog/2020/10/19/feature-flags.htmlbody
+src/content/en/blog/2020/10/20/wrong-interviewing.html: src/content/en/blog/2020/10/20/wrong-interviewing.json src/content/en/blog/2020/10/20/wrong-interviewing.htmlbody
+src/content/en/blog/2020/11/07/diy-bugs.html: src/content/en/blog/2020/11/07/diy-bugs.json src/content/en/blog/2020/11/07/diy-bugs.htmlbody
+src/content/en/blog/2020/11/08/paradigm-shift-review.html: src/content/en/blog/2020/11/08/paradigm-shift-review.json src/content/en/blog/2020/11/08/paradigm-shift-review.htmlbody
+src/content/en/blog/2020/11/12/database-parsers-trees.html: src/content/en/blog/2020/11/12/database-parsers-trees.json src/content/en/blog/2020/11/12/database-parsers-trees.htmlbody
+src/content/en/blog/2020/11/14/local-first-review.html: src/content/en/blog/2020/11/14/local-first-review.json src/content/en/blog/2020/11/14/local-first-review.htmlbody
+src/content/en/blog/2021/01/26/remembering-ann.html: src/content/en/blog/2021/01/26/remembering-ann.json src/content/en/blog/2021/01/26/remembering-ann.htmlbody
+src/content/en/blog/2021/02/17/fallible.html: src/content/en/blog/2021/02/17/fallible.json src/content/en/blog/2021/02/17/fallible.htmlbody
+src/content/en/blog/2021/04/29/relational-review.html: src/content/en/blog/2021/04/29/relational-review.json src/content/en/blog/2021/04/29/relational-review.htmlbody
+src/content/en/pastebin/2016/04/05/rpn.html: src/content/en/pastebin/2016/04/05/rpn.json src/content/en/pastebin/2016/04/05/rpn.htmlbody
+src/content/en/pastebin/2018/07/11/nix-pinning.html: src/content/en/pastebin/2018/07/11/nix-pinning.json src/content/en/pastebin/2018/07/11/nix-pinning.htmlbody
+src/content/en/pastebin/2018/07/13/guix-nixos-systemd.html: src/content/en/pastebin/2018/07/13/guix-nixos-systemd.json src/content/en/pastebin/2018/07/13/guix-nixos-systemd.htmlbody
+src/content/en/pastebin/2018/07/13/guixbuilder-nixos.html: src/content/en/pastebin/2018/07/13/guixbuilder-nixos.json src/content/en/pastebin/2018/07/13/guixbuilder-nixos.htmlbody
+src/content/en/pastebin/2018/07/13/guixbuilder.html: src/content/en/pastebin/2018/07/13/guixbuilder.json src/content/en/pastebin/2018/07/13/guixbuilder.htmlbody
+src/content/en/pastebin/2018/07/13/nix-strpad.html: src/content/en/pastebin/2018/07/13/nix-strpad.json src/content/en/pastebin/2018/07/13/nix-strpad.htmlbody
+src/content/en/pastebin/2018/07/25/nix-exps.html: src/content/en/pastebin/2018/07/25/nix-exps.json src/content/en/pastebin/2018/07/25/nix-exps.htmlbody
+src/content/en/pastebin/2018/07/25/nix-showdrv.html: src/content/en/pastebin/2018/07/25/nix-showdrv.json src/content/en/pastebin/2018/07/25/nix-showdrv.htmlbody
+src/content/en/pastebin/2019/06/08/inconsistent-hash.html: src/content/en/pastebin/2019/06/08/inconsistent-hash.json src/content/en/pastebin/2019/06/08/inconsistent-hash.htmlbody
+src/content/en/pastebin/2019/12/29/raku-tuple-type.html: src/content/en/pastebin/2019/12/29/raku-tuple-type.json src/content/en/pastebin/2019/12/29/raku-tuple-type.htmlbody
+src/content/en/pastebin/2020/01/04/guix-import-failure.html: src/content/en/pastebin/2020/01/04/guix-import-failure.json src/content/en/pastebin/2020/01/04/guix-import-failure.htmlbody
+src/content/en/pastebin/2020/02/14/guix-shebang.html: src/content/en/pastebin/2020/02/14/guix-shebang.json src/content/en/pastebin/2020/02/14/guix-shebang.htmlbody
+src/content/en/pastebin/2020/11/27/guix-build-local.html: src/content/en/pastebin/2020/11/27/guix-build-local.json src/content/en/pastebin/2020/11/27/guix-build-local.htmlbody
+src/content/en/pastebin/2020/12/15/guix-pack-fail.html: src/content/en/pastebin/2020/12/15/guix-pack-fail.json src/content/en/pastebin/2020/12/15/guix-pack-fail.htmlbody
+src/content/en/pastebin/2021/04/03/naive-slugify-js.html: src/content/en/pastebin/2021/04/03/naive-slugify-js.json src/content/en/pastebin/2021/04/03/naive-slugify-js.htmlbody
+src/content/en/pastebin/2021/06/08/reading-session-pt1.html: src/content/en/pastebin/2021/06/08/reading-session-pt1.json src/content/en/pastebin/2021/06/08/reading-session-pt1.htmlbody
+src/content/en/pastebin/2021/06/22/curl-wget.html: src/content/en/pastebin/2021/06/22/curl-wget.json src/content/en/pastebin/2021/06/22/curl-wget.htmlbody
+src/content/en/pastebin/2021/08/11/h1-spacing.html: src/content/en/pastebin/2021/08/11/h1-spacing.json src/content/en/pastebin/2021/08/11/h1-spacing.htmlbody
+src/content/en/pastebin/2021/09/02/sicp-3-19.html: src/content/en/pastebin/2021/09/02/sicp-3-19.json src/content/en/pastebin/2021/09/02/sicp-3-19.htmlbody
+src/content/en/pastebin/2021/09/03/sicp-persistent-queue.html: src/content/en/pastebin/2021/09/03/sicp-persistent-queue.json src/content/en/pastebin/2021/09/03/sicp-persistent-queue.htmlbody
+src/content/en/pastebin/2022/07/14/git-cleanup.html: src/content/en/pastebin/2022/07/14/git-cleanup.json src/content/en/pastebin/2022/07/14/git-cleanup.htmlbody
+src/content/en/pastebin/2023/07/22/funcallable-amop.html: src/content/en/pastebin/2023/07/22/funcallable-amop.json src/content/en/pastebin/2023/07/22/funcallable-amop.htmlbody
+src/content/en/podcast/2020/12/19/test-entry.html: src/content/en/podcast/2020/12/19/test-entry.json src/content/en/podcast/2020/12/19/test-entry.htmlbody
+src/content/en/screencast/2021/02/07/autoqemu.html: src/content/en/screencast/2021/02/07/autoqemu.json src/content/en/screencast/2021/02/07/autoqemu.htmlbody
+src/content/en/slide/2020/10/19/feature-flags.html: src/content/en/slide/2020/10/19/feature-flags.json src/content/en/slide/2020/10/19/feature-flags.htmlbody
+src/content/en/slide/2020/11/14/local-first-hype.html: src/content/en/slide/2020/11/14/local-first-hype.json src/content/en/slide/2020/11/14/local-first-hype.htmlbody
+src/content/en/til/2020/08/12/filename-timestamp.html: src/content/en/til/2020/08/12/filename-timestamp.json src/content/en/til/2020/08/12/filename-timestamp.htmlbody
+src/content/en/til/2020/08/13/code-jekyll.html: src/content/en/til/2020/08/13/code-jekyll.json src/content/en/til/2020/08/13/code-jekyll.htmlbody
+src/content/en/til/2020/08/14/browse-git.html: src/content/en/til/2020/08/14/browse-git.json src/content/en/til/2020/08/14/browse-git.htmlbody
+src/content/en/til/2020/08/16/git-search.html: src/content/en/til/2020/08/16/git-search.json src/content/en/til/2020/08/16/git-search.htmlbody
+src/content/en/til/2020/08/28/grep-online.html: src/content/en/til/2020/08/28/grep-online.json src/content/en/til/2020/08/28/grep-online.htmlbody
+src/content/en/til/2020/09/04/cli-email-fun-profit.html: src/content/en/til/2020/09/04/cli-email-fun-profit.json src/content/en/til/2020/09/04/cli-email-fun-profit.htmlbody
+src/content/en/til/2020/09/05/oldschool-pr.html: src/content/en/til/2020/09/05/oldschool-pr.json src/content/en/til/2020/09/05/oldschool-pr.htmlbody
+src/content/en/til/2020/10/11/search-git-history.html: src/content/en/til/2020/10/11/search-git-history.json src/content/en/til/2020/10/11/search-git-history.htmlbody
+src/content/en/til/2020/11/08/find-broken-symlink.html: src/content/en/til/2020/11/08/find-broken-symlink.json src/content/en/til/2020/11/08/find-broken-symlink.htmlbody
+src/content/en/til/2020/11/12/diy-nix-bash-ci.html: src/content/en/til/2020/11/12/diy-nix-bash-ci.json src/content/en/til/2020/11/12/diy-nix-bash-ci.htmlbody
+src/content/en/til/2020/11/12/git-bisect-automation.html: src/content/en/til/2020/11/12/git-bisect-automation.json src/content/en/til/2020/11/12/git-bisect-automation.htmlbody
+src/content/en/til/2020/11/12/useful-bashvars.html: src/content/en/til/2020/11/12/useful-bashvars.json src/content/en/til/2020/11/12/useful-bashvars.htmlbody
+src/content/en/til/2020/11/14/gpodder-media.html: src/content/en/til/2020/11/14/gpodder-media.json src/content/en/til/2020/11/14/gpodder-media.htmlbody
+src/content/en/til/2020/11/30/git-notes-ci.html: src/content/en/til/2020/11/30/git-notes-ci.json src/content/en/til/2020/11/30/git-notes-ci.htmlbody
+src/content/en/til/2020/12/15/shellcheck-repo.html: src/content/en/til/2020/12/15/shellcheck-repo.json src/content/en/til/2020/12/15/shellcheck-repo.htmlbody
+src/content/en/til/2020/12/29/svg.html: src/content/en/til/2020/12/29/svg.json src/content/en/til/2020/12/29/svg.htmlbody
+src/content/en/til/2021/01/12/curl-awk-emails.html: src/content/en/til/2021/01/12/curl-awk-emails.json src/content/en/til/2021/01/12/curl-awk-emails.htmlbody
+src/content/en/til/2021/01/17/posix-shebang.html: src/content/en/til/2021/01/17/posix-shebang.json src/content/en/til/2021/01/17/posix-shebang.htmlbody
+src/content/en/til/2021/04/24/cl-generic-precedence.html: src/content/en/til/2021/04/24/cl-generic-precedence.json src/content/en/til/2021/04/24/cl-generic-precedence.htmlbody
+src/content/en/til/2021/04/24/clojure-autocurry.html: src/content/en/til/2021/04/24/clojure-autocurry.json src/content/en/til/2021/04/24/clojure-autocurry.htmlbody
+src/content/en/til/2021/04/24/scm-nif.html: src/content/en/til/2021/04/24/scm-nif.json src/content/en/til/2021/04/24/scm-nif.htmlbody
+src/content/en/til/2021/07/23/git-tls-gpg.html: src/content/en/til/2021/07/23/git-tls-gpg.json src/content/en/til/2021/07/23/git-tls-gpg.htmlbody
+src/content/en/til/2021/08/11/js-bigint-reviver.html: src/content/en/til/2021/08/11/js-bigint-reviver.json src/content/en/til/2021/08/11/js-bigint-reviver.htmlbody
+src/content/pt/hea/2020/08/12/arquivo-datado.html: src/content/pt/hea/2020/08/12/arquivo-datado.json src/content/pt/hea/2020/08/12/arquivo-datado.htmlbody
+src/content/de/blog/index.html: src/content/de/blog/index.json src/content/de/blog/index.htmlbody
+src/content/de/folien/index.html: src/content/de/folien/index.json src/content/de/folien/index.htmlbody
+src/content/de/hhg/index.html: src/content/de/hhg/index.json src/content/de/hhg/index.htmlbody
+src/content/de/pastebin/index.html: src/content/de/pastebin/index.json src/content/de/pastebin/index.htmlbody
+src/content/de/podcast/index.html: src/content/de/podcast/index.json src/content/de/podcast/index.htmlbody
+src/content/de/screencast/index.html: src/content/de/screencast/index.json src/content/de/screencast/index.htmlbody
+src/content/en/blog/index.html: src/content/en/blog/index.json src/content/en/blog/index.htmlbody
+src/content/en/pastebin/index.html: src/content/en/pastebin/index.json src/content/en/pastebin/index.htmlbody
+src/content/en/podcast/index.html: src/content/en/podcast/index.json src/content/en/podcast/index.htmlbody
+src/content/en/screencast/index.html: src/content/en/screencast/index.json src/content/en/screencast/index.htmlbody
+src/content/en/slide/index.html: src/content/en/slide/index.json src/content/en/slide/index.htmlbody
+src/content/en/til/index.html: src/content/en/til/index.json src/content/en/til/index.htmlbody
+src/content/eo/blog/index.html: src/content/eo/blog/index.json src/content/eo/blog/index.htmlbody
+src/content/eo/hml/index.html: src/content/eo/hml/index.json src/content/eo/hml/index.htmlbody
+src/content/eo/lumbildoj/index.html: src/content/eo/lumbildoj/index.json src/content/eo/lumbildoj/index.htmlbody
+src/content/eo/pastebin/index.html: src/content/eo/pastebin/index.json src/content/eo/pastebin/index.htmlbody
+src/content/eo/podkasto/index.html: src/content/eo/podkasto/index.json src/content/eo/podkasto/index.htmlbody
+src/content/eo/screencast/index.html: src/content/eo/screencast/index.json src/content/eo/screencast/index.htmlbody
+src/content/es/blog/index.html: src/content/es/blog/index.json src/content/es/blog/index.htmlbody
+src/content/es/diapositivas/index.html: src/content/es/diapositivas/index.json src/content/es/diapositivas/index.htmlbody
+src/content/es/haa/index.html: src/content/es/haa/index.json src/content/es/haa/index.htmlbody
+src/content/es/pastebin/index.html: src/content/es/pastebin/index.json src/content/es/pastebin/index.htmlbody
+src/content/es/podcast/index.html: src/content/es/podcast/index.json src/content/es/podcast/index.htmlbody
+src/content/es/screencast/index.html: src/content/es/screencast/index.json src/content/es/screencast/index.htmlbody
+src/content/fr/aja/index.html: src/content/fr/aja/index.json src/content/fr/aja/index.htmlbody
+src/content/fr/blog/index.html: src/content/fr/blog/index.json src/content/fr/blog/index.htmlbody
+src/content/fr/diapositives/index.html: src/content/fr/diapositives/index.json src/content/fr/diapositives/index.htmlbody
+src/content/fr/pastebin/index.html: src/content/fr/pastebin/index.json src/content/fr/pastebin/index.htmlbody
+src/content/fr/podcast/index.html: src/content/fr/podcast/index.json src/content/fr/podcast/index.htmlbody
+src/content/fr/screencast/index.html: src/content/fr/screencast/index.json src/content/fr/screencast/index.htmlbody
+src/content/pt/blog/index.html: src/content/pt/blog/index.json src/content/pt/blog/index.htmlbody
+src/content/pt/eslaides/index.html: src/content/pt/eslaides/index.json src/content/pt/eslaides/index.htmlbody
+src/content/pt/hea/index.html: src/content/pt/hea/index.json src/content/pt/hea/index.htmlbody
+src/content/pt/pastebin/index.html: src/content/pt/pastebin/index.json src/content/pt/pastebin/index.htmlbody
+src/content/pt/podcast/index.html: src/content/pt/podcast/index.json src/content/pt/podcast/index.htmlbody
+src/content/pt/screencast/index.html: src/content/pt/screencast/index.json src/content/pt/screencast/index.htmlbody
+src/content/de/blog/kategorien.html: src/content/de/blog/kategorien.json src/content/de/blog/kategorien.htmlbody
+src/content/de/folien/kategorien.html: src/content/de/folien/kategorien.json src/content/de/folien/kategorien.htmlbody
+src/content/de/hhg/kategorien.html: src/content/de/hhg/kategorien.json src/content/de/hhg/kategorien.htmlbody
+src/content/de/pastebin/kategorien.html: src/content/de/pastebin/kategorien.json src/content/de/pastebin/kategorien.htmlbody
+src/content/de/podcast/kategorien.html: src/content/de/podcast/kategorien.json src/content/de/podcast/kategorien.htmlbody
+src/content/de/screencast/kategorien.html: src/content/de/screencast/kategorien.json src/content/de/screencast/kategorien.htmlbody
+src/content/en/blog/categories.html: src/content/en/blog/categories.json src/content/en/blog/categories.htmlbody
+src/content/en/pastebin/categories.html: src/content/en/pastebin/categories.json src/content/en/pastebin/categories.htmlbody
+src/content/en/podcast/categories.html: src/content/en/podcast/categories.json src/content/en/podcast/categories.htmlbody
+src/content/en/screencast/categories.html: src/content/en/screencast/categories.json src/content/en/screencast/categories.htmlbody
+src/content/en/slide/categories.html: src/content/en/slide/categories.json src/content/en/slide/categories.htmlbody
+src/content/en/til/categories.html: src/content/en/til/categories.json src/content/en/til/categories.htmlbody
+src/content/eo/blog/kategorioj.html: src/content/eo/blog/kategorioj.json src/content/eo/blog/kategorioj.htmlbody
+src/content/eo/hml/kategorioj.html: src/content/eo/hml/kategorioj.json src/content/eo/hml/kategorioj.htmlbody
+src/content/eo/lumbildoj/kategorioj.html: src/content/eo/lumbildoj/kategorioj.json src/content/eo/lumbildoj/kategorioj.htmlbody
+src/content/eo/pastebin/kategorioj.html: src/content/eo/pastebin/kategorioj.json src/content/eo/pastebin/kategorioj.htmlbody
+src/content/eo/podkasto/kategorioj.html: src/content/eo/podkasto/kategorioj.json src/content/eo/podkasto/kategorioj.htmlbody
+src/content/eo/screencast/kategorioj.html: src/content/eo/screencast/kategorioj.json src/content/eo/screencast/kategorioj.htmlbody
+src/content/es/blog/categorias.html: src/content/es/blog/categorias.json src/content/es/blog/categorias.htmlbody
+src/content/es/diapositivas/categorias.html: src/content/es/diapositivas/categorias.json src/content/es/diapositivas/categorias.htmlbody
+src/content/es/haa/categorias.html: src/content/es/haa/categorias.json src/content/es/haa/categorias.htmlbody
+src/content/es/pastebin/categorias.html: src/content/es/pastebin/categorias.json src/content/es/pastebin/categorias.htmlbody
+src/content/es/podcast/categorias.html: src/content/es/podcast/categorias.json src/content/es/podcast/categorias.htmlbody
+src/content/es/screencast/categorias.html: src/content/es/screencast/categorias.json src/content/es/screencast/categorias.htmlbody
+src/content/fr/aja/categories.html: src/content/fr/aja/categories.json src/content/fr/aja/categories.htmlbody
+src/content/fr/blog/categories.html: src/content/fr/blog/categories.json src/content/fr/blog/categories.htmlbody
+src/content/fr/diapositives/categories.html: src/content/fr/diapositives/categories.json src/content/fr/diapositives/categories.htmlbody
+src/content/fr/pastebin/categories.html: src/content/fr/pastebin/categories.json src/content/fr/pastebin/categories.htmlbody
+src/content/fr/podcast/categories.html: src/content/fr/podcast/categories.json src/content/fr/podcast/categories.htmlbody
+src/content/fr/screencast/categories.html: src/content/fr/screencast/categories.json src/content/fr/screencast/categories.htmlbody
+src/content/pt/blog/categorias.html: src/content/pt/blog/categorias.json src/content/pt/blog/categorias.htmlbody
+src/content/pt/eslaides/categorias.html: src/content/pt/eslaides/categorias.json src/content/pt/eslaides/categorias.htmlbody
+src/content/pt/hea/categorias.html: src/content/pt/hea/categorias.json src/content/pt/hea/categorias.htmlbody
+src/content/pt/pastebin/categorias.html: src/content/pt/pastebin/categorias.json src/content/pt/pastebin/categorias.htmlbody
+src/content/pt/podcast/categorias.html: src/content/pt/podcast/categorias.json src/content/pt/podcast/categorias.htmlbody
+src/content/pt/screencast/categorias.html: src/content/pt/screencast/categorias.json src/content/pt/screencast/categorias.htmlbody
+src/content/de/index.html: src/content/de/index.json src/content/de/index.htmlbody
+src/content/en/index.html: src/content/en/index.json src/content/en/index.htmlbody
+src/content/eo/index.html: src/content/eo/index.json src/content/eo/index.htmlbody
+src/content/es/index.html: src/content/es/index.json src/content/es/index.htmlbody
+src/content/fr/index.html: src/content/fr/index.json src/content/fr/index.htmlbody
+src/content/pt/index.html: src/content/pt/index.json src/content/pt/index.htmlbody
-src/content/en/about.updatedat-check: src/content/en/about.conf
-src/content/en/index.updatedat-check: src/content/en/index.conf
-src/content/pt/sobre.updatedat-check: src/content/pt/sobre.conf
-src/content/en/blog/2018/07/17/guix-nixos.updatedat-check: src/content/en/blog/2018/07/17/guix-nixos.conf
-src/content/en/blog/2018/08/01/npm-ci-reproducibility.updatedat-check: src/content/en/blog/2018/08/01/npm-ci-reproducibility.conf
-src/content/en/blog/2018/12/21/ytdl-subs.updatedat-check: src/content/en/blog/2018/12/21/ytdl-subs.conf
-src/content/en/blog/2019/06/02/nixos-stateless-workstation.updatedat-check: src/content/en/blog/2019/06/02/nixos-stateless-workstation.conf
-src/content/en/blog/2020/08/10/guix-srht.updatedat-check: src/content/en/blog/2020/08/10/guix-srht.conf
-src/content/en/blog/2020/08/31/database-i-wish-i-had.updatedat-check: src/content/en/blog/2020/08/31/database-i-wish-i-had.conf
-src/content/en/blog/2020/10/05/cargo2nix.updatedat-check: src/content/en/blog/2020/10/05/cargo2nix.conf
-src/content/en/blog/2020/10/05/swift2nix.updatedat-check: src/content/en/blog/2020/10/05/swift2nix.conf
-src/content/en/blog/2020/10/19/feature-flags.updatedat-check: src/content/en/blog/2020/10/19/feature-flags.conf
-src/content/en/blog/2020/10/20/wrong-interviewing.updatedat-check: src/content/en/blog/2020/10/20/wrong-interviewing.conf
-src/content/en/blog/2020/11/07/diy-bugs.updatedat-check: src/content/en/blog/2020/11/07/diy-bugs.conf
-src/content/en/blog/2020/11/08/paradigm-shift-review.updatedat-check: src/content/en/blog/2020/11/08/paradigm-shift-review.conf
-src/content/en/blog/2020/11/12/database-parsers-trees.updatedat-check: src/content/en/blog/2020/11/12/database-parsers-trees.conf
-src/content/en/blog/2020/11/14/local-first-review.updatedat-check: src/content/en/blog/2020/11/14/local-first-review.conf
-src/content/en/blog/2021/01/26/remembering-ann.updatedat-check: src/content/en/blog/2021/01/26/remembering-ann.conf
-src/content/en/blog/2021/02/17/fallible.updatedat-check: src/content/en/blog/2021/02/17/fallible.conf
-src/content/en/blog/2021/04/29/relational-review.updatedat-check: src/content/en/blog/2021/04/29/relational-review.conf
-src/content/en/pastebin/2016/04/05/rpn.updatedat-check: src/content/en/pastebin/2016/04/05/rpn.conf
-src/content/en/pastebin/2018/07/11/nix-pinning.updatedat-check: src/content/en/pastebin/2018/07/11/nix-pinning.conf
-src/content/en/pastebin/2018/07/13/guix-nixos-systemd.updatedat-check: src/content/en/pastebin/2018/07/13/guix-nixos-systemd.conf
-src/content/en/pastebin/2018/07/13/guixbuilder-nixos.updatedat-check: src/content/en/pastebin/2018/07/13/guixbuilder-nixos.conf
-src/content/en/pastebin/2018/07/13/guixbuilder.updatedat-check: src/content/en/pastebin/2018/07/13/guixbuilder.conf
-src/content/en/pastebin/2018/07/13/nix-strpad.updatedat-check: src/content/en/pastebin/2018/07/13/nix-strpad.conf
-src/content/en/pastebin/2018/07/25/nix-exps.updatedat-check: src/content/en/pastebin/2018/07/25/nix-exps.conf
-src/content/en/pastebin/2018/07/25/nix-showdrv.updatedat-check: src/content/en/pastebin/2018/07/25/nix-showdrv.conf
-src/content/en/pastebin/2019/06/08/inconsistent-hash.updatedat-check: src/content/en/pastebin/2019/06/08/inconsistent-hash.conf
-src/content/en/pastebin/2019/12/29/raku-tuple-type.updatedat-check: src/content/en/pastebin/2019/12/29/raku-tuple-type.conf
-src/content/en/pastebin/2020/01/04/guix-import-failure.updatedat-check: src/content/en/pastebin/2020/01/04/guix-import-failure.conf
-src/content/en/pastebin/2020/02/14/guix-shebang.updatedat-check: src/content/en/pastebin/2020/02/14/guix-shebang.conf
-src/content/en/pastebin/2020/11/27/guix-build-local.updatedat-check: src/content/en/pastebin/2020/11/27/guix-build-local.conf
-src/content/en/pastebin/2020/12/15/guix-pack-fail.updatedat-check: src/content/en/pastebin/2020/12/15/guix-pack-fail.conf
-src/content/en/pastebin/2021/04/03/naive-slugify-js.updatedat-check: src/content/en/pastebin/2021/04/03/naive-slugify-js.conf
-src/content/en/pastebin/2021/06/08/reading-session-pt1.updatedat-check: src/content/en/pastebin/2021/06/08/reading-session-pt1.conf
-src/content/en/pastebin/2021/06/22/curl-wget.updatedat-check: src/content/en/pastebin/2021/06/22/curl-wget.conf
-src/content/en/pastebin/2021/08/11/h1-spacing.updatedat-check: src/content/en/pastebin/2021/08/11/h1-spacing.conf
-src/content/en/pastebin/2021/09/02/sicp-3-19.updatedat-check: src/content/en/pastebin/2021/09/02/sicp-3-19.conf
-src/content/en/pastebin/2021/09/03/sicp-persistent-queue.updatedat-check: src/content/en/pastebin/2021/09/03/sicp-persistent-queue.conf
-src/content/en/pastebin/2022/07/14/git-cleanup.updatedat-check: src/content/en/pastebin/2022/07/14/git-cleanup.conf
-src/content/en/pastebin/2023/07/22/funcallable-amop.updatedat-check: src/content/en/pastebin/2023/07/22/funcallable-amop.conf
-src/content/en/podcast/2020/12/19/test-entry.updatedat-check: src/content/en/podcast/2020/12/19/test-entry.conf
-src/content/en/screencast/2021/02/07/autoqemu.updatedat-check: src/content/en/screencast/2021/02/07/autoqemu.conf
-src/content/en/til/2020/08/12/filename-timestamp.updatedat-check: src/content/en/til/2020/08/12/filename-timestamp.conf
-src/content/en/til/2020/08/13/code-jekyll.updatedat-check: src/content/en/til/2020/08/13/code-jekyll.conf
-src/content/en/til/2020/08/14/browse-git.updatedat-check: src/content/en/til/2020/08/14/browse-git.conf
-src/content/en/til/2020/08/16/git-search.updatedat-check: src/content/en/til/2020/08/16/git-search.conf
-src/content/en/til/2020/08/28/grep-online.updatedat-check: src/content/en/til/2020/08/28/grep-online.conf
-src/content/en/til/2020/09/04/cli-email-fun-profit.updatedat-check: src/content/en/til/2020/09/04/cli-email-fun-profit.conf
-src/content/en/til/2020/09/05/oldschool-pr.updatedat-check: src/content/en/til/2020/09/05/oldschool-pr.conf
-src/content/en/til/2020/10/11/search-git-history.updatedat-check: src/content/en/til/2020/10/11/search-git-history.conf
-src/content/en/til/2020/11/08/find-broken-symlink.updatedat-check: src/content/en/til/2020/11/08/find-broken-symlink.conf
-src/content/en/til/2020/11/12/diy-nix-bash-ci.updatedat-check: src/content/en/til/2020/11/12/diy-nix-bash-ci.conf
-src/content/en/til/2020/11/12/git-bisect-automation.updatedat-check: src/content/en/til/2020/11/12/git-bisect-automation.conf
-src/content/en/til/2020/11/12/useful-bashvars.updatedat-check: src/content/en/til/2020/11/12/useful-bashvars.conf
-src/content/en/til/2020/11/14/gpodder-media.updatedat-check: src/content/en/til/2020/11/14/gpodder-media.conf
-src/content/en/til/2020/11/30/git-notes-ci.updatedat-check: src/content/en/til/2020/11/30/git-notes-ci.conf
-src/content/en/til/2020/12/15/shellcheck-repo.updatedat-check: src/content/en/til/2020/12/15/shellcheck-repo.conf
-src/content/en/til/2020/12/29/svg.updatedat-check: src/content/en/til/2020/12/29/svg.conf
-src/content/en/til/2021/01/12/curl-awk-emails.updatedat-check: src/content/en/til/2021/01/12/curl-awk-emails.conf
-src/content/en/til/2021/01/17/posix-shebang.updatedat-check: src/content/en/til/2021/01/17/posix-shebang.conf
-src/content/en/til/2021/04/24/cl-generic-precedence.updatedat-check: src/content/en/til/2021/04/24/cl-generic-precedence.conf
-src/content/en/til/2021/04/24/clojure-autocurry.updatedat-check: src/content/en/til/2021/04/24/clojure-autocurry.conf
-src/content/en/til/2021/04/24/scm-nif.updatedat-check: src/content/en/til/2021/04/24/scm-nif.conf
-src/content/en/til/2021/07/23/git-tls-gpg.updatedat-check: src/content/en/til/2021/07/23/git-tls-gpg.conf
-src/content/en/til/2021/08/11/js-bigint-reviver.updatedat-check: src/content/en/til/2021/08/11/js-bigint-reviver.conf
-src/content/pt/hea/2020/08/12/arquivo-datado.updatedat-check: src/content/pt/hea/2020/08/12/arquivo-datado.conf
-src/content/en/blog/index.updatedat-check: src/content/en/blog/index.conf
-src/content/en/pastebin/index.updatedat-check: src/content/en/pastebin/index.conf
-src/content/en/podcast/index.updatedat-check: src/content/en/podcast/index.conf
-src/content/en/screencast/index.updatedat-check: src/content/en/screencast/index.conf
-src/content/en/til/index.updatedat-check: src/content/en/til/index.conf
-src/content/pt/hea/index.updatedat-check: src/content/pt/hea/index.conf
-src/content/en/blog/categories.updatedat-check: src/content/en/blog/categories.conf
-src/content/en/pastebin/categories.updatedat-check: src/content/en/pastebin/categories.conf
-src/content/en/podcast/categories.updatedat-check: src/content/en/podcast/categories.conf
-src/content/en/screencast/categories.updatedat-check: src/content/en/screencast/categories.conf
-src/content/en/til/categories.updatedat-check: src/content/en/til/categories.conf
-src/content/pt/hea/categorias.updatedat-check: src/content/pt/hea/categorias.conf
+src/content/de/musik.updatedat-check: src/content/de/musik.json
+src/content/de/ueber.updatedat-check: src/content/de/ueber.json
+src/content/en/about.updatedat-check: src/content/en/about.json
+src/content/en/music.updatedat-check: src/content/en/music.json
+src/content/eo/muziko.updatedat-check: src/content/eo/muziko.json
+src/content/eo/pri.updatedat-check: src/content/eo/pri.json
+src/content/es/acerca.updatedat-check: src/content/es/acerca.json
+src/content/es/musica.updatedat-check: src/content/es/musica.json
+src/content/fr/a-propos.updatedat-check: src/content/fr/a-propos.json
+src/content/fr/musique.updatedat-check: src/content/fr/musique.json
+src/content/pt/musica.updatedat-check: src/content/pt/musica.json
+src/content/pt/sobre.updatedat-check: src/content/pt/sobre.json
+src/content/en/blog/2018/07/17/guix-nixos.updatedat-check: src/content/en/blog/2018/07/17/guix-nixos.json
+src/content/en/blog/2018/08/01/npm-ci-reproducibility.updatedat-check: src/content/en/blog/2018/08/01/npm-ci-reproducibility.json
+src/content/en/blog/2018/12/21/ytdl-subs.updatedat-check: src/content/en/blog/2018/12/21/ytdl-subs.json
+src/content/en/blog/2019/06/02/nixos-stateless-workstation.updatedat-check: src/content/en/blog/2019/06/02/nixos-stateless-workstation.json
+src/content/en/blog/2020/08/10/guix-srht.updatedat-check: src/content/en/blog/2020/08/10/guix-srht.json
+src/content/en/blog/2020/08/31/database-i-wish-i-had.updatedat-check: src/content/en/blog/2020/08/31/database-i-wish-i-had.json
+src/content/en/blog/2020/10/05/cargo2nix.updatedat-check: src/content/en/blog/2020/10/05/cargo2nix.json
+src/content/en/blog/2020/10/05/swift2nix.updatedat-check: src/content/en/blog/2020/10/05/swift2nix.json
+src/content/en/blog/2020/10/19/feature-flags.updatedat-check: src/content/en/blog/2020/10/19/feature-flags.json
+src/content/en/blog/2020/10/20/wrong-interviewing.updatedat-check: src/content/en/blog/2020/10/20/wrong-interviewing.json
+src/content/en/blog/2020/11/07/diy-bugs.updatedat-check: src/content/en/blog/2020/11/07/diy-bugs.json
+src/content/en/blog/2020/11/08/paradigm-shift-review.updatedat-check: src/content/en/blog/2020/11/08/paradigm-shift-review.json
+src/content/en/blog/2020/11/12/database-parsers-trees.updatedat-check: src/content/en/blog/2020/11/12/database-parsers-trees.json
+src/content/en/blog/2020/11/14/local-first-review.updatedat-check: src/content/en/blog/2020/11/14/local-first-review.json
+src/content/en/blog/2021/01/26/remembering-ann.updatedat-check: src/content/en/blog/2021/01/26/remembering-ann.json
+src/content/en/blog/2021/02/17/fallible.updatedat-check: src/content/en/blog/2021/02/17/fallible.json
+src/content/en/blog/2021/04/29/relational-review.updatedat-check: src/content/en/blog/2021/04/29/relational-review.json
+src/content/en/pastebin/2016/04/05/rpn.updatedat-check: src/content/en/pastebin/2016/04/05/rpn.json
+src/content/en/pastebin/2018/07/11/nix-pinning.updatedat-check: src/content/en/pastebin/2018/07/11/nix-pinning.json
+src/content/en/pastebin/2018/07/13/guix-nixos-systemd.updatedat-check: src/content/en/pastebin/2018/07/13/guix-nixos-systemd.json
+src/content/en/pastebin/2018/07/13/guixbuilder-nixos.updatedat-check: src/content/en/pastebin/2018/07/13/guixbuilder-nixos.json
+src/content/en/pastebin/2018/07/13/guixbuilder.updatedat-check: src/content/en/pastebin/2018/07/13/guixbuilder.json
+src/content/en/pastebin/2018/07/13/nix-strpad.updatedat-check: src/content/en/pastebin/2018/07/13/nix-strpad.json
+src/content/en/pastebin/2018/07/25/nix-exps.updatedat-check: src/content/en/pastebin/2018/07/25/nix-exps.json
+src/content/en/pastebin/2018/07/25/nix-showdrv.updatedat-check: src/content/en/pastebin/2018/07/25/nix-showdrv.json
+src/content/en/pastebin/2019/06/08/inconsistent-hash.updatedat-check: src/content/en/pastebin/2019/06/08/inconsistent-hash.json
+src/content/en/pastebin/2019/12/29/raku-tuple-type.updatedat-check: src/content/en/pastebin/2019/12/29/raku-tuple-type.json
+src/content/en/pastebin/2020/01/04/guix-import-failure.updatedat-check: src/content/en/pastebin/2020/01/04/guix-import-failure.json
+src/content/en/pastebin/2020/02/14/guix-shebang.updatedat-check: src/content/en/pastebin/2020/02/14/guix-shebang.json
+src/content/en/pastebin/2020/11/27/guix-build-local.updatedat-check: src/content/en/pastebin/2020/11/27/guix-build-local.json
+src/content/en/pastebin/2020/12/15/guix-pack-fail.updatedat-check: src/content/en/pastebin/2020/12/15/guix-pack-fail.json
+src/content/en/pastebin/2021/04/03/naive-slugify-js.updatedat-check: src/content/en/pastebin/2021/04/03/naive-slugify-js.json
+src/content/en/pastebin/2021/06/08/reading-session-pt1.updatedat-check: src/content/en/pastebin/2021/06/08/reading-session-pt1.json
+src/content/en/pastebin/2021/06/22/curl-wget.updatedat-check: src/content/en/pastebin/2021/06/22/curl-wget.json
+src/content/en/pastebin/2021/08/11/h1-spacing.updatedat-check: src/content/en/pastebin/2021/08/11/h1-spacing.json
+src/content/en/pastebin/2021/09/02/sicp-3-19.updatedat-check: src/content/en/pastebin/2021/09/02/sicp-3-19.json
+src/content/en/pastebin/2021/09/03/sicp-persistent-queue.updatedat-check: src/content/en/pastebin/2021/09/03/sicp-persistent-queue.json
+src/content/en/pastebin/2022/07/14/git-cleanup.updatedat-check: src/content/en/pastebin/2022/07/14/git-cleanup.json
+src/content/en/pastebin/2023/07/22/funcallable-amop.updatedat-check: src/content/en/pastebin/2023/07/22/funcallable-amop.json
+src/content/en/podcast/2020/12/19/test-entry.updatedat-check: src/content/en/podcast/2020/12/19/test-entry.json
+src/content/en/screencast/2021/02/07/autoqemu.updatedat-check: src/content/en/screencast/2021/02/07/autoqemu.json
+src/content/en/slide/2020/10/19/feature-flags.updatedat-check: src/content/en/slide/2020/10/19/feature-flags.json
+src/content/en/slide/2020/11/14/local-first-hype.updatedat-check: src/content/en/slide/2020/11/14/local-first-hype.json
+src/content/en/til/2020/08/12/filename-timestamp.updatedat-check: src/content/en/til/2020/08/12/filename-timestamp.json
+src/content/en/til/2020/08/13/code-jekyll.updatedat-check: src/content/en/til/2020/08/13/code-jekyll.json
+src/content/en/til/2020/08/14/browse-git.updatedat-check: src/content/en/til/2020/08/14/browse-git.json
+src/content/en/til/2020/08/16/git-search.updatedat-check: src/content/en/til/2020/08/16/git-search.json
+src/content/en/til/2020/08/28/grep-online.updatedat-check: src/content/en/til/2020/08/28/grep-online.json
+src/content/en/til/2020/09/04/cli-email-fun-profit.updatedat-check: src/content/en/til/2020/09/04/cli-email-fun-profit.json
+src/content/en/til/2020/09/05/oldschool-pr.updatedat-check: src/content/en/til/2020/09/05/oldschool-pr.json
+src/content/en/til/2020/10/11/search-git-history.updatedat-check: src/content/en/til/2020/10/11/search-git-history.json
+src/content/en/til/2020/11/08/find-broken-symlink.updatedat-check: src/content/en/til/2020/11/08/find-broken-symlink.json
+src/content/en/til/2020/11/12/diy-nix-bash-ci.updatedat-check: src/content/en/til/2020/11/12/diy-nix-bash-ci.json
+src/content/en/til/2020/11/12/git-bisect-automation.updatedat-check: src/content/en/til/2020/11/12/git-bisect-automation.json
+src/content/en/til/2020/11/12/useful-bashvars.updatedat-check: src/content/en/til/2020/11/12/useful-bashvars.json
+src/content/en/til/2020/11/14/gpodder-media.updatedat-check: src/content/en/til/2020/11/14/gpodder-media.json
+src/content/en/til/2020/11/30/git-notes-ci.updatedat-check: src/content/en/til/2020/11/30/git-notes-ci.json
+src/content/en/til/2020/12/15/shellcheck-repo.updatedat-check: src/content/en/til/2020/12/15/shellcheck-repo.json
+src/content/en/til/2020/12/29/svg.updatedat-check: src/content/en/til/2020/12/29/svg.json
+src/content/en/til/2021/01/12/curl-awk-emails.updatedat-check: src/content/en/til/2021/01/12/curl-awk-emails.json
+src/content/en/til/2021/01/17/posix-shebang.updatedat-check: src/content/en/til/2021/01/17/posix-shebang.json
+src/content/en/til/2021/04/24/cl-generic-precedence.updatedat-check: src/content/en/til/2021/04/24/cl-generic-precedence.json
+src/content/en/til/2021/04/24/clojure-autocurry.updatedat-check: src/content/en/til/2021/04/24/clojure-autocurry.json
+src/content/en/til/2021/04/24/scm-nif.updatedat-check: src/content/en/til/2021/04/24/scm-nif.json
+src/content/en/til/2021/07/23/git-tls-gpg.updatedat-check: src/content/en/til/2021/07/23/git-tls-gpg.json
+src/content/en/til/2021/08/11/js-bigint-reviver.updatedat-check: src/content/en/til/2021/08/11/js-bigint-reviver.json
+src/content/pt/hea/2020/08/12/arquivo-datado.updatedat-check: src/content/pt/hea/2020/08/12/arquivo-datado.json
+src/content/de/blog/index.updatedat-check: src/content/de/blog/index.json
+src/content/de/folien/index.updatedat-check: src/content/de/folien/index.json
+src/content/de/hhg/index.updatedat-check: src/content/de/hhg/index.json
+src/content/de/pastebin/index.updatedat-check: src/content/de/pastebin/index.json
+src/content/de/podcast/index.updatedat-check: src/content/de/podcast/index.json
+src/content/de/screencast/index.updatedat-check: src/content/de/screencast/index.json
+src/content/en/blog/index.updatedat-check: src/content/en/blog/index.json
+src/content/en/pastebin/index.updatedat-check: src/content/en/pastebin/index.json
+src/content/en/podcast/index.updatedat-check: src/content/en/podcast/index.json
+src/content/en/screencast/index.updatedat-check: src/content/en/screencast/index.json
+src/content/en/slide/index.updatedat-check: src/content/en/slide/index.json
+src/content/en/til/index.updatedat-check: src/content/en/til/index.json
+src/content/eo/blog/index.updatedat-check: src/content/eo/blog/index.json
+src/content/eo/hml/index.updatedat-check: src/content/eo/hml/index.json
+src/content/eo/lumbildoj/index.updatedat-check: src/content/eo/lumbildoj/index.json
+src/content/eo/pastebin/index.updatedat-check: src/content/eo/pastebin/index.json
+src/content/eo/podkasto/index.updatedat-check: src/content/eo/podkasto/index.json
+src/content/eo/screencast/index.updatedat-check: src/content/eo/screencast/index.json
+src/content/es/blog/index.updatedat-check: src/content/es/blog/index.json
+src/content/es/diapositivas/index.updatedat-check: src/content/es/diapositivas/index.json
+src/content/es/haa/index.updatedat-check: src/content/es/haa/index.json
+src/content/es/pastebin/index.updatedat-check: src/content/es/pastebin/index.json
+src/content/es/podcast/index.updatedat-check: src/content/es/podcast/index.json
+src/content/es/screencast/index.updatedat-check: src/content/es/screencast/index.json
+src/content/fr/aja/index.updatedat-check: src/content/fr/aja/index.json
+src/content/fr/blog/index.updatedat-check: src/content/fr/blog/index.json
+src/content/fr/diapositives/index.updatedat-check: src/content/fr/diapositives/index.json
+src/content/fr/pastebin/index.updatedat-check: src/content/fr/pastebin/index.json
+src/content/fr/podcast/index.updatedat-check: src/content/fr/podcast/index.json
+src/content/fr/screencast/index.updatedat-check: src/content/fr/screencast/index.json
+src/content/pt/blog/index.updatedat-check: src/content/pt/blog/index.json
+src/content/pt/eslaides/index.updatedat-check: src/content/pt/eslaides/index.json
+src/content/pt/hea/index.updatedat-check: src/content/pt/hea/index.json
+src/content/pt/pastebin/index.updatedat-check: src/content/pt/pastebin/index.json
+src/content/pt/podcast/index.updatedat-check: src/content/pt/podcast/index.json
+src/content/pt/screencast/index.updatedat-check: src/content/pt/screencast/index.json
+src/content/de/blog/kategorien.updatedat-check: src/content/de/blog/kategorien.json
+src/content/de/folien/kategorien.updatedat-check: src/content/de/folien/kategorien.json
+src/content/de/hhg/kategorien.updatedat-check: src/content/de/hhg/kategorien.json
+src/content/de/pastebin/kategorien.updatedat-check: src/content/de/pastebin/kategorien.json
+src/content/de/podcast/kategorien.updatedat-check: src/content/de/podcast/kategorien.json
+src/content/de/screencast/kategorien.updatedat-check: src/content/de/screencast/kategorien.json
+src/content/en/blog/categories.updatedat-check: src/content/en/blog/categories.json
+src/content/en/pastebin/categories.updatedat-check: src/content/en/pastebin/categories.json
+src/content/en/podcast/categories.updatedat-check: src/content/en/podcast/categories.json
+src/content/en/screencast/categories.updatedat-check: src/content/en/screencast/categories.json
+src/content/en/slide/categories.updatedat-check: src/content/en/slide/categories.json
+src/content/en/til/categories.updatedat-check: src/content/en/til/categories.json
+src/content/eo/blog/kategorioj.updatedat-check: src/content/eo/blog/kategorioj.json
+src/content/eo/hml/kategorioj.updatedat-check: src/content/eo/hml/kategorioj.json
+src/content/eo/lumbildoj/kategorioj.updatedat-check: src/content/eo/lumbildoj/kategorioj.json
+src/content/eo/pastebin/kategorioj.updatedat-check: src/content/eo/pastebin/kategorioj.json
+src/content/eo/podkasto/kategorioj.updatedat-check: src/content/eo/podkasto/kategorioj.json
+src/content/eo/screencast/kategorioj.updatedat-check: src/content/eo/screencast/kategorioj.json
+src/content/es/blog/categorias.updatedat-check: src/content/es/blog/categorias.json
+src/content/es/diapositivas/categorias.updatedat-check: src/content/es/diapositivas/categorias.json
+src/content/es/haa/categorias.updatedat-check: src/content/es/haa/categorias.json
+src/content/es/pastebin/categorias.updatedat-check: src/content/es/pastebin/categorias.json
+src/content/es/podcast/categorias.updatedat-check: src/content/es/podcast/categorias.json
+src/content/es/screencast/categorias.updatedat-check: src/content/es/screencast/categorias.json
+src/content/fr/aja/categories.updatedat-check: src/content/fr/aja/categories.json
+src/content/fr/blog/categories.updatedat-check: src/content/fr/blog/categories.json
+src/content/fr/diapositives/categories.updatedat-check: src/content/fr/diapositives/categories.json
+src/content/fr/pastebin/categories.updatedat-check: src/content/fr/pastebin/categories.json
+src/content/fr/podcast/categories.updatedat-check: src/content/fr/podcast/categories.json
+src/content/fr/screencast/categories.updatedat-check: src/content/fr/screencast/categories.json
+src/content/pt/blog/categorias.updatedat-check: src/content/pt/blog/categorias.json
+src/content/pt/eslaides/categorias.updatedat-check: src/content/pt/eslaides/categorias.json
+src/content/pt/hea/categorias.updatedat-check: src/content/pt/hea/categorias.json
+src/content/pt/pastebin/categorias.updatedat-check: src/content/pt/pastebin/categorias.json
+src/content/pt/podcast/categorias.updatedat-check: src/content/pt/podcast/categorias.json
+src/content/pt/screencast/categorias.updatedat-check: src/content/pt/screencast/categorias.json
+src/content/de/index.updatedat-check: src/content/de/index.json
+src/content/en/index.updatedat-check: src/content/en/index.json
+src/content/eo/index.updatedat-check: src/content/eo/index.json
+src/content/es/index.updatedat-check: src/content/es/index.json
+src/content/fr/index.updatedat-check: src/content/fr/index.json
+src/content/pt/index.updatedat-check: src/content/pt/index.json
+src/content/de/musik.links-internal-check: src/content/de/musik.links
+src/content/de/ueber.links-internal-check: src/content/de/ueber.links
src/content/en/about.links-internal-check: src/content/en/about.links
-src/content/en/index.links-internal-check: src/content/en/index.links
+src/content/en/music.links-internal-check: src/content/en/music.links
+src/content/eo/muziko.links-internal-check: src/content/eo/muziko.links
+src/content/eo/pri.links-internal-check: src/content/eo/pri.links
+src/content/es/acerca.links-internal-check: src/content/es/acerca.links
+src/content/es/musica.links-internal-check: src/content/es/musica.links
+src/content/fr/a-propos.links-internal-check: src/content/fr/a-propos.links
+src/content/fr/musique.links-internal-check: src/content/fr/musique.links
+src/content/pt/musica.links-internal-check: src/content/pt/musica.links
src/content/pt/sobre.links-internal-check: src/content/pt/sobre.links
src/content/en/blog/2018/07/17/guix-nixos.links-internal-check: src/content/en/blog/2018/07/17/guix-nixos.links
src/content/en/blog/2018/08/01/npm-ci-reproducibility.links-internal-check: src/content/en/blog/2018/08/01/npm-ci-reproducibility.links
@@ -600,6 +798,8 @@ src/content/en/pastebin/2022/07/14/git-cleanup.links-internal-check: src/content
src/content/en/pastebin/2023/07/22/funcallable-amop.links-internal-check: src/content/en/pastebin/2023/07/22/funcallable-amop.links
src/content/en/podcast/2020/12/19/test-entry.links-internal-check: src/content/en/podcast/2020/12/19/test-entry.links
src/content/en/screencast/2021/02/07/autoqemu.links-internal-check: src/content/en/screencast/2021/02/07/autoqemu.links
+src/content/en/slide/2020/10/19/feature-flags.links-internal-check: src/content/en/slide/2020/10/19/feature-flags.links
+src/content/en/slide/2020/11/14/local-first-hype.links-internal-check: src/content/en/slide/2020/11/14/local-first-hype.links
src/content/en/til/2020/08/12/filename-timestamp.links-internal-check: src/content/en/til/2020/08/12/filename-timestamp.links
src/content/en/til/2020/08/13/code-jekyll.links-internal-check: src/content/en/til/2020/08/13/code-jekyll.links
src/content/en/til/2020/08/14/browse-git.links-internal-check: src/content/en/til/2020/08/14/browse-git.links
@@ -624,20 +824,95 @@ src/content/en/til/2021/04/24/scm-nif.links-internal-check: src/content/en/til/2
src/content/en/til/2021/07/23/git-tls-gpg.links-internal-check: src/content/en/til/2021/07/23/git-tls-gpg.links
src/content/en/til/2021/08/11/js-bigint-reviver.links-internal-check: src/content/en/til/2021/08/11/js-bigint-reviver.links
src/content/pt/hea/2020/08/12/arquivo-datado.links-internal-check: src/content/pt/hea/2020/08/12/arquivo-datado.links
+src/content/de/blog/index.links-internal-check: src/content/de/blog/index.links
+src/content/de/folien/index.links-internal-check: src/content/de/folien/index.links
+src/content/de/hhg/index.links-internal-check: src/content/de/hhg/index.links
+src/content/de/pastebin/index.links-internal-check: src/content/de/pastebin/index.links
+src/content/de/podcast/index.links-internal-check: src/content/de/podcast/index.links
+src/content/de/screencast/index.links-internal-check: src/content/de/screencast/index.links
src/content/en/blog/index.links-internal-check: src/content/en/blog/index.links
src/content/en/pastebin/index.links-internal-check: src/content/en/pastebin/index.links
src/content/en/podcast/index.links-internal-check: src/content/en/podcast/index.links
src/content/en/screencast/index.links-internal-check: src/content/en/screencast/index.links
+src/content/en/slide/index.links-internal-check: src/content/en/slide/index.links
src/content/en/til/index.links-internal-check: src/content/en/til/index.links
+src/content/eo/blog/index.links-internal-check: src/content/eo/blog/index.links
+src/content/eo/hml/index.links-internal-check: src/content/eo/hml/index.links
+src/content/eo/lumbildoj/index.links-internal-check: src/content/eo/lumbildoj/index.links
+src/content/eo/pastebin/index.links-internal-check: src/content/eo/pastebin/index.links
+src/content/eo/podkasto/index.links-internal-check: src/content/eo/podkasto/index.links
+src/content/eo/screencast/index.links-internal-check: src/content/eo/screencast/index.links
+src/content/es/blog/index.links-internal-check: src/content/es/blog/index.links
+src/content/es/diapositivas/index.links-internal-check: src/content/es/diapositivas/index.links
+src/content/es/haa/index.links-internal-check: src/content/es/haa/index.links
+src/content/es/pastebin/index.links-internal-check: src/content/es/pastebin/index.links
+src/content/es/podcast/index.links-internal-check: src/content/es/podcast/index.links
+src/content/es/screencast/index.links-internal-check: src/content/es/screencast/index.links
+src/content/fr/aja/index.links-internal-check: src/content/fr/aja/index.links
+src/content/fr/blog/index.links-internal-check: src/content/fr/blog/index.links
+src/content/fr/diapositives/index.links-internal-check: src/content/fr/diapositives/index.links
+src/content/fr/pastebin/index.links-internal-check: src/content/fr/pastebin/index.links
+src/content/fr/podcast/index.links-internal-check: src/content/fr/podcast/index.links
+src/content/fr/screencast/index.links-internal-check: src/content/fr/screencast/index.links
+src/content/pt/blog/index.links-internal-check: src/content/pt/blog/index.links
+src/content/pt/eslaides/index.links-internal-check: src/content/pt/eslaides/index.links
src/content/pt/hea/index.links-internal-check: src/content/pt/hea/index.links
+src/content/pt/pastebin/index.links-internal-check: src/content/pt/pastebin/index.links
+src/content/pt/podcast/index.links-internal-check: src/content/pt/podcast/index.links
+src/content/pt/screencast/index.links-internal-check: src/content/pt/screencast/index.links
+src/content/de/blog/kategorien.links-internal-check: src/content/de/blog/kategorien.links
+src/content/de/folien/kategorien.links-internal-check: src/content/de/folien/kategorien.links
+src/content/de/hhg/kategorien.links-internal-check: src/content/de/hhg/kategorien.links
+src/content/de/pastebin/kategorien.links-internal-check: src/content/de/pastebin/kategorien.links
+src/content/de/podcast/kategorien.links-internal-check: src/content/de/podcast/kategorien.links
+src/content/de/screencast/kategorien.links-internal-check: src/content/de/screencast/kategorien.links
src/content/en/blog/categories.links-internal-check: src/content/en/blog/categories.links
src/content/en/pastebin/categories.links-internal-check: src/content/en/pastebin/categories.links
src/content/en/podcast/categories.links-internal-check: src/content/en/podcast/categories.links
src/content/en/screencast/categories.links-internal-check: src/content/en/screencast/categories.links
+src/content/en/slide/categories.links-internal-check: src/content/en/slide/categories.links
src/content/en/til/categories.links-internal-check: src/content/en/til/categories.links
+src/content/eo/blog/kategorioj.links-internal-check: src/content/eo/blog/kategorioj.links
+src/content/eo/hml/kategorioj.links-internal-check: src/content/eo/hml/kategorioj.links
+src/content/eo/lumbildoj/kategorioj.links-internal-check: src/content/eo/lumbildoj/kategorioj.links
+src/content/eo/pastebin/kategorioj.links-internal-check: src/content/eo/pastebin/kategorioj.links
+src/content/eo/podkasto/kategorioj.links-internal-check: src/content/eo/podkasto/kategorioj.links
+src/content/eo/screencast/kategorioj.links-internal-check: src/content/eo/screencast/kategorioj.links
+src/content/es/blog/categorias.links-internal-check: src/content/es/blog/categorias.links
+src/content/es/diapositivas/categorias.links-internal-check: src/content/es/diapositivas/categorias.links
+src/content/es/haa/categorias.links-internal-check: src/content/es/haa/categorias.links
+src/content/es/pastebin/categorias.links-internal-check: src/content/es/pastebin/categorias.links
+src/content/es/podcast/categorias.links-internal-check: src/content/es/podcast/categorias.links
+src/content/es/screencast/categorias.links-internal-check: src/content/es/screencast/categorias.links
+src/content/fr/aja/categories.links-internal-check: src/content/fr/aja/categories.links
+src/content/fr/blog/categories.links-internal-check: src/content/fr/blog/categories.links
+src/content/fr/diapositives/categories.links-internal-check: src/content/fr/diapositives/categories.links
+src/content/fr/pastebin/categories.links-internal-check: src/content/fr/pastebin/categories.links
+src/content/fr/podcast/categories.links-internal-check: src/content/fr/podcast/categories.links
+src/content/fr/screencast/categories.links-internal-check: src/content/fr/screencast/categories.links
+src/content/pt/blog/categorias.links-internal-check: src/content/pt/blog/categorias.links
+src/content/pt/eslaides/categorias.links-internal-check: src/content/pt/eslaides/categorias.links
src/content/pt/hea/categorias.links-internal-check: src/content/pt/hea/categorias.links
+src/content/pt/pastebin/categorias.links-internal-check: src/content/pt/pastebin/categorias.links
+src/content/pt/podcast/categorias.links-internal-check: src/content/pt/podcast/categorias.links
+src/content/pt/screencast/categorias.links-internal-check: src/content/pt/screencast/categorias.links
+src/content/de/index.links-internal-check: src/content/de/index.links
+src/content/en/index.links-internal-check: src/content/en/index.links
+src/content/eo/index.links-internal-check: src/content/eo/index.links
+src/content/es/index.links-internal-check: src/content/es/index.links
+src/content/fr/index.links-internal-check: src/content/fr/index.links
+src/content/pt/index.links-internal-check: src/content/pt/index.links
+src/content/de/musik.caslinks: src/content/de/musik.links
+src/content/de/ueber.caslinks: src/content/de/ueber.links
src/content/en/about.caslinks: src/content/en/about.links
-src/content/en/index.caslinks: src/content/en/index.links
+src/content/en/music.caslinks: src/content/en/music.links
+src/content/eo/muziko.caslinks: src/content/eo/muziko.links
+src/content/eo/pri.caslinks: src/content/eo/pri.links
+src/content/es/acerca.caslinks: src/content/es/acerca.links
+src/content/es/musica.caslinks: src/content/es/musica.links
+src/content/fr/a-propos.caslinks: src/content/fr/a-propos.links
+src/content/fr/musique.caslinks: src/content/fr/musique.links
+src/content/pt/musica.caslinks: src/content/pt/musica.links
src/content/pt/sobre.caslinks: src/content/pt/sobre.links
src/content/en/blog/2018/07/17/guix-nixos.caslinks: src/content/en/blog/2018/07/17/guix-nixos.links
src/content/en/blog/2018/08/01/npm-ci-reproducibility.caslinks: src/content/en/blog/2018/08/01/npm-ci-reproducibility.links
@@ -680,6 +955,8 @@ src/content/en/pastebin/2022/07/14/git-cleanup.caslinks: src/content/en/pastebin
src/content/en/pastebin/2023/07/22/funcallable-amop.caslinks: src/content/en/pastebin/2023/07/22/funcallable-amop.links
src/content/en/podcast/2020/12/19/test-entry.caslinks: src/content/en/podcast/2020/12/19/test-entry.links
src/content/en/screencast/2021/02/07/autoqemu.caslinks: src/content/en/screencast/2021/02/07/autoqemu.links
+src/content/en/slide/2020/10/19/feature-flags.caslinks: src/content/en/slide/2020/10/19/feature-flags.links
+src/content/en/slide/2020/11/14/local-first-hype.caslinks: src/content/en/slide/2020/11/14/local-first-hype.links
src/content/en/til/2020/08/12/filename-timestamp.caslinks: src/content/en/til/2020/08/12/filename-timestamp.links
src/content/en/til/2020/08/13/code-jekyll.caslinks: src/content/en/til/2020/08/13/code-jekyll.links
src/content/en/til/2020/08/14/browse-git.caslinks: src/content/en/til/2020/08/14/browse-git.links
@@ -704,275 +981,755 @@ src/content/en/til/2021/04/24/scm-nif.caslinks: src/content/en/til/2021/04/24/sc
src/content/en/til/2021/07/23/git-tls-gpg.caslinks: src/content/en/til/2021/07/23/git-tls-gpg.links
src/content/en/til/2021/08/11/js-bigint-reviver.caslinks: src/content/en/til/2021/08/11/js-bigint-reviver.links
src/content/pt/hea/2020/08/12/arquivo-datado.caslinks: src/content/pt/hea/2020/08/12/arquivo-datado.links
+src/content/de/blog/index.caslinks: src/content/de/blog/index.links
+src/content/de/folien/index.caslinks: src/content/de/folien/index.links
+src/content/de/hhg/index.caslinks: src/content/de/hhg/index.links
+src/content/de/pastebin/index.caslinks: src/content/de/pastebin/index.links
+src/content/de/podcast/index.caslinks: src/content/de/podcast/index.links
+src/content/de/screencast/index.caslinks: src/content/de/screencast/index.links
src/content/en/blog/index.caslinks: src/content/en/blog/index.links
src/content/en/pastebin/index.caslinks: src/content/en/pastebin/index.links
src/content/en/podcast/index.caslinks: src/content/en/podcast/index.links
src/content/en/screencast/index.caslinks: src/content/en/screencast/index.links
+src/content/en/slide/index.caslinks: src/content/en/slide/index.links
src/content/en/til/index.caslinks: src/content/en/til/index.links
+src/content/eo/blog/index.caslinks: src/content/eo/blog/index.links
+src/content/eo/hml/index.caslinks: src/content/eo/hml/index.links
+src/content/eo/lumbildoj/index.caslinks: src/content/eo/lumbildoj/index.links
+src/content/eo/pastebin/index.caslinks: src/content/eo/pastebin/index.links
+src/content/eo/podkasto/index.caslinks: src/content/eo/podkasto/index.links
+src/content/eo/screencast/index.caslinks: src/content/eo/screencast/index.links
+src/content/es/blog/index.caslinks: src/content/es/blog/index.links
+src/content/es/diapositivas/index.caslinks: src/content/es/diapositivas/index.links
+src/content/es/haa/index.caslinks: src/content/es/haa/index.links
+src/content/es/pastebin/index.caslinks: src/content/es/pastebin/index.links
+src/content/es/podcast/index.caslinks: src/content/es/podcast/index.links
+src/content/es/screencast/index.caslinks: src/content/es/screencast/index.links
+src/content/fr/aja/index.caslinks: src/content/fr/aja/index.links
+src/content/fr/blog/index.caslinks: src/content/fr/blog/index.links
+src/content/fr/diapositives/index.caslinks: src/content/fr/diapositives/index.links
+src/content/fr/pastebin/index.caslinks: src/content/fr/pastebin/index.links
+src/content/fr/podcast/index.caslinks: src/content/fr/podcast/index.links
+src/content/fr/screencast/index.caslinks: src/content/fr/screencast/index.links
+src/content/pt/blog/index.caslinks: src/content/pt/blog/index.links
+src/content/pt/eslaides/index.caslinks: src/content/pt/eslaides/index.links
src/content/pt/hea/index.caslinks: src/content/pt/hea/index.links
+src/content/pt/pastebin/index.caslinks: src/content/pt/pastebin/index.links
+src/content/pt/podcast/index.caslinks: src/content/pt/podcast/index.links
+src/content/pt/screencast/index.caslinks: src/content/pt/screencast/index.links
+src/content/de/blog/kategorien.caslinks: src/content/de/blog/kategorien.links
+src/content/de/folien/kategorien.caslinks: src/content/de/folien/kategorien.links
+src/content/de/hhg/kategorien.caslinks: src/content/de/hhg/kategorien.links
+src/content/de/pastebin/kategorien.caslinks: src/content/de/pastebin/kategorien.links
+src/content/de/podcast/kategorien.caslinks: src/content/de/podcast/kategorien.links
+src/content/de/screencast/kategorien.caslinks: src/content/de/screencast/kategorien.links
src/content/en/blog/categories.caslinks: src/content/en/blog/categories.links
src/content/en/pastebin/categories.caslinks: src/content/en/pastebin/categories.links
src/content/en/podcast/categories.caslinks: src/content/en/podcast/categories.links
src/content/en/screencast/categories.caslinks: src/content/en/screencast/categories.links
+src/content/en/slide/categories.caslinks: src/content/en/slide/categories.links
src/content/en/til/categories.caslinks: src/content/en/til/categories.links
+src/content/eo/blog/kategorioj.caslinks: src/content/eo/blog/kategorioj.links
+src/content/eo/hml/kategorioj.caslinks: src/content/eo/hml/kategorioj.links
+src/content/eo/lumbildoj/kategorioj.caslinks: src/content/eo/lumbildoj/kategorioj.links
+src/content/eo/pastebin/kategorioj.caslinks: src/content/eo/pastebin/kategorioj.links
+src/content/eo/podkasto/kategorioj.caslinks: src/content/eo/podkasto/kategorioj.links
+src/content/eo/screencast/kategorioj.caslinks: src/content/eo/screencast/kategorioj.links
+src/content/es/blog/categorias.caslinks: src/content/es/blog/categorias.links
+src/content/es/diapositivas/categorias.caslinks: src/content/es/diapositivas/categorias.links
+src/content/es/haa/categorias.caslinks: src/content/es/haa/categorias.links
+src/content/es/pastebin/categorias.caslinks: src/content/es/pastebin/categorias.links
+src/content/es/podcast/categorias.caslinks: src/content/es/podcast/categorias.links
+src/content/es/screencast/categorias.caslinks: src/content/es/screencast/categorias.links
+src/content/fr/aja/categories.caslinks: src/content/fr/aja/categories.links
+src/content/fr/blog/categories.caslinks: src/content/fr/blog/categories.links
+src/content/fr/diapositives/categories.caslinks: src/content/fr/diapositives/categories.links
+src/content/fr/pastebin/categories.caslinks: src/content/fr/pastebin/categories.links
+src/content/fr/podcast/categories.caslinks: src/content/fr/podcast/categories.links
+src/content/fr/screencast/categories.caslinks: src/content/fr/screencast/categories.links
+src/content/pt/blog/categorias.caslinks: src/content/pt/blog/categorias.links
+src/content/pt/eslaides/categorias.caslinks: src/content/pt/eslaides/categorias.links
src/content/pt/hea/categorias.caslinks: src/content/pt/hea/categorias.links
+src/content/pt/pastebin/categorias.caslinks: src/content/pt/pastebin/categorias.links
+src/content/pt/podcast/categorias.caslinks: src/content/pt/podcast/categorias.links
+src/content/pt/screencast/categorias.caslinks: src/content/pt/screencast/categorias.links
+src/content/de/index.caslinks: src/content/de/index.links
+src/content/en/index.caslinks: src/content/en/index.links
+src/content/eo/index.caslinks: src/content/eo/index.links
+src/content/es/index.caslinks: src/content/es/index.links
+src/content/fr/index.caslinks: src/content/fr/index.links
+src/content/pt/index.caslinks: src/content/pt/index.links
-src/content/en/blog/2018/07/17/guix-nixos.feedentry: src/content/en/blog/2018/07/17/guix-nixos.conf src/content/en/blog/2018/07/17/guix-nixos.htmlbody
-src/content/en/blog/2018/08/01/npm-ci-reproducibility.feedentry: src/content/en/blog/2018/08/01/npm-ci-reproducibility.conf src/content/en/blog/2018/08/01/npm-ci-reproducibility.htmlbody
-src/content/en/blog/2018/12/21/ytdl-subs.feedentry: src/content/en/blog/2018/12/21/ytdl-subs.conf src/content/en/blog/2018/12/21/ytdl-subs.htmlbody
-src/content/en/blog/2019/06/02/nixos-stateless-workstation.feedentry: src/content/en/blog/2019/06/02/nixos-stateless-workstation.conf src/content/en/blog/2019/06/02/nixos-stateless-workstation.htmlbody
-src/content/en/blog/2020/08/10/guix-srht.feedentry: src/content/en/blog/2020/08/10/guix-srht.conf src/content/en/blog/2020/08/10/guix-srht.htmlbody
-src/content/en/blog/2020/08/31/database-i-wish-i-had.feedentry: src/content/en/blog/2020/08/31/database-i-wish-i-had.conf src/content/en/blog/2020/08/31/database-i-wish-i-had.htmlbody
-src/content/en/blog/2020/10/05/cargo2nix.feedentry: src/content/en/blog/2020/10/05/cargo2nix.conf src/content/en/blog/2020/10/05/cargo2nix.htmlbody
-src/content/en/blog/2020/10/05/swift2nix.feedentry: src/content/en/blog/2020/10/05/swift2nix.conf src/content/en/blog/2020/10/05/swift2nix.htmlbody
-src/content/en/blog/2020/10/19/feature-flags.feedentry: src/content/en/blog/2020/10/19/feature-flags.conf src/content/en/blog/2020/10/19/feature-flags.htmlbody
-src/content/en/blog/2020/10/20/wrong-interviewing.feedentry: src/content/en/blog/2020/10/20/wrong-interviewing.conf src/content/en/blog/2020/10/20/wrong-interviewing.htmlbody
-src/content/en/blog/2020/11/07/diy-bugs.feedentry: src/content/en/blog/2020/11/07/diy-bugs.conf src/content/en/blog/2020/11/07/diy-bugs.htmlbody
-src/content/en/blog/2020/11/08/paradigm-shift-review.feedentry: src/content/en/blog/2020/11/08/paradigm-shift-review.conf src/content/en/blog/2020/11/08/paradigm-shift-review.htmlbody
-src/content/en/blog/2020/11/12/database-parsers-trees.feedentry: src/content/en/blog/2020/11/12/database-parsers-trees.conf src/content/en/blog/2020/11/12/database-parsers-trees.htmlbody
-src/content/en/blog/2020/11/14/local-first-review.feedentry: src/content/en/blog/2020/11/14/local-first-review.conf src/content/en/blog/2020/11/14/local-first-review.htmlbody
-src/content/en/blog/2021/01/26/remembering-ann.feedentry: src/content/en/blog/2021/01/26/remembering-ann.conf src/content/en/blog/2021/01/26/remembering-ann.htmlbody
-src/content/en/blog/2021/02/17/fallible.feedentry: src/content/en/blog/2021/02/17/fallible.conf src/content/en/blog/2021/02/17/fallible.htmlbody
-src/content/en/blog/2021/04/29/relational-review.feedentry: src/content/en/blog/2021/04/29/relational-review.conf src/content/en/blog/2021/04/29/relational-review.htmlbody
-src/content/en/pastebin/2016/04/05/rpn.feedentry: src/content/en/pastebin/2016/04/05/rpn.conf src/content/en/pastebin/2016/04/05/rpn.htmlbody
-src/content/en/pastebin/2018/07/11/nix-pinning.feedentry: src/content/en/pastebin/2018/07/11/nix-pinning.conf src/content/en/pastebin/2018/07/11/nix-pinning.htmlbody
-src/content/en/pastebin/2018/07/13/guix-nixos-systemd.feedentry: src/content/en/pastebin/2018/07/13/guix-nixos-systemd.conf src/content/en/pastebin/2018/07/13/guix-nixos-systemd.htmlbody
-src/content/en/pastebin/2018/07/13/guixbuilder-nixos.feedentry: src/content/en/pastebin/2018/07/13/guixbuilder-nixos.conf src/content/en/pastebin/2018/07/13/guixbuilder-nixos.htmlbody
-src/content/en/pastebin/2018/07/13/guixbuilder.feedentry: src/content/en/pastebin/2018/07/13/guixbuilder.conf src/content/en/pastebin/2018/07/13/guixbuilder.htmlbody
-src/content/en/pastebin/2018/07/13/nix-strpad.feedentry: src/content/en/pastebin/2018/07/13/nix-strpad.conf src/content/en/pastebin/2018/07/13/nix-strpad.htmlbody
-src/content/en/pastebin/2018/07/25/nix-exps.feedentry: src/content/en/pastebin/2018/07/25/nix-exps.conf src/content/en/pastebin/2018/07/25/nix-exps.htmlbody
-src/content/en/pastebin/2018/07/25/nix-showdrv.feedentry: src/content/en/pastebin/2018/07/25/nix-showdrv.conf src/content/en/pastebin/2018/07/25/nix-showdrv.htmlbody
-src/content/en/pastebin/2019/06/08/inconsistent-hash.feedentry: src/content/en/pastebin/2019/06/08/inconsistent-hash.conf src/content/en/pastebin/2019/06/08/inconsistent-hash.htmlbody
-src/content/en/pastebin/2019/12/29/raku-tuple-type.feedentry: src/content/en/pastebin/2019/12/29/raku-tuple-type.conf src/content/en/pastebin/2019/12/29/raku-tuple-type.htmlbody
-src/content/en/pastebin/2020/01/04/guix-import-failure.feedentry: src/content/en/pastebin/2020/01/04/guix-import-failure.conf src/content/en/pastebin/2020/01/04/guix-import-failure.htmlbody
-src/content/en/pastebin/2020/02/14/guix-shebang.feedentry: src/content/en/pastebin/2020/02/14/guix-shebang.conf src/content/en/pastebin/2020/02/14/guix-shebang.htmlbody
-src/content/en/pastebin/2020/11/27/guix-build-local.feedentry: src/content/en/pastebin/2020/11/27/guix-build-local.conf src/content/en/pastebin/2020/11/27/guix-build-local.htmlbody
-src/content/en/pastebin/2020/12/15/guix-pack-fail.feedentry: src/content/en/pastebin/2020/12/15/guix-pack-fail.conf src/content/en/pastebin/2020/12/15/guix-pack-fail.htmlbody
-src/content/en/pastebin/2021/04/03/naive-slugify-js.feedentry: src/content/en/pastebin/2021/04/03/naive-slugify-js.conf src/content/en/pastebin/2021/04/03/naive-slugify-js.htmlbody
-src/content/en/pastebin/2021/06/08/reading-session-pt1.feedentry: src/content/en/pastebin/2021/06/08/reading-session-pt1.conf src/content/en/pastebin/2021/06/08/reading-session-pt1.htmlbody
-src/content/en/pastebin/2021/06/22/curl-wget.feedentry: src/content/en/pastebin/2021/06/22/curl-wget.conf src/content/en/pastebin/2021/06/22/curl-wget.htmlbody
-src/content/en/pastebin/2021/08/11/h1-spacing.feedentry: src/content/en/pastebin/2021/08/11/h1-spacing.conf src/content/en/pastebin/2021/08/11/h1-spacing.htmlbody
-src/content/en/pastebin/2021/09/02/sicp-3-19.feedentry: src/content/en/pastebin/2021/09/02/sicp-3-19.conf src/content/en/pastebin/2021/09/02/sicp-3-19.htmlbody
-src/content/en/pastebin/2021/09/03/sicp-persistent-queue.feedentry: src/content/en/pastebin/2021/09/03/sicp-persistent-queue.conf src/content/en/pastebin/2021/09/03/sicp-persistent-queue.htmlbody
-src/content/en/pastebin/2022/07/14/git-cleanup.feedentry: src/content/en/pastebin/2022/07/14/git-cleanup.conf src/content/en/pastebin/2022/07/14/git-cleanup.htmlbody
-src/content/en/pastebin/2023/07/22/funcallable-amop.feedentry: src/content/en/pastebin/2023/07/22/funcallable-amop.conf src/content/en/pastebin/2023/07/22/funcallable-amop.htmlbody
-src/content/en/podcast/2020/12/19/test-entry.feedentry: src/content/en/podcast/2020/12/19/test-entry.conf src/content/en/podcast/2020/12/19/test-entry.htmlbody
-src/content/en/screencast/2021/02/07/autoqemu.feedentry: src/content/en/screencast/2021/02/07/autoqemu.conf src/content/en/screencast/2021/02/07/autoqemu.htmlbody
-src/content/en/til/2020/08/12/filename-timestamp.feedentry: src/content/en/til/2020/08/12/filename-timestamp.conf src/content/en/til/2020/08/12/filename-timestamp.htmlbody
-src/content/en/til/2020/08/13/code-jekyll.feedentry: src/content/en/til/2020/08/13/code-jekyll.conf src/content/en/til/2020/08/13/code-jekyll.htmlbody
-src/content/en/til/2020/08/14/browse-git.feedentry: src/content/en/til/2020/08/14/browse-git.conf src/content/en/til/2020/08/14/browse-git.htmlbody
-src/content/en/til/2020/08/16/git-search.feedentry: src/content/en/til/2020/08/16/git-search.conf src/content/en/til/2020/08/16/git-search.htmlbody
-src/content/en/til/2020/08/28/grep-online.feedentry: src/content/en/til/2020/08/28/grep-online.conf src/content/en/til/2020/08/28/grep-online.htmlbody
-src/content/en/til/2020/09/04/cli-email-fun-profit.feedentry: src/content/en/til/2020/09/04/cli-email-fun-profit.conf src/content/en/til/2020/09/04/cli-email-fun-profit.htmlbody
-src/content/en/til/2020/09/05/oldschool-pr.feedentry: src/content/en/til/2020/09/05/oldschool-pr.conf src/content/en/til/2020/09/05/oldschool-pr.htmlbody
-src/content/en/til/2020/10/11/search-git-history.feedentry: src/content/en/til/2020/10/11/search-git-history.conf src/content/en/til/2020/10/11/search-git-history.htmlbody
-src/content/en/til/2020/11/08/find-broken-symlink.feedentry: src/content/en/til/2020/11/08/find-broken-symlink.conf src/content/en/til/2020/11/08/find-broken-symlink.htmlbody
-src/content/en/til/2020/11/12/diy-nix-bash-ci.feedentry: src/content/en/til/2020/11/12/diy-nix-bash-ci.conf src/content/en/til/2020/11/12/diy-nix-bash-ci.htmlbody
-src/content/en/til/2020/11/12/git-bisect-automation.feedentry: src/content/en/til/2020/11/12/git-bisect-automation.conf src/content/en/til/2020/11/12/git-bisect-automation.htmlbody
-src/content/en/til/2020/11/12/useful-bashvars.feedentry: src/content/en/til/2020/11/12/useful-bashvars.conf src/content/en/til/2020/11/12/useful-bashvars.htmlbody
-src/content/en/til/2020/11/14/gpodder-media.feedentry: src/content/en/til/2020/11/14/gpodder-media.conf src/content/en/til/2020/11/14/gpodder-media.htmlbody
-src/content/en/til/2020/11/30/git-notes-ci.feedentry: src/content/en/til/2020/11/30/git-notes-ci.conf src/content/en/til/2020/11/30/git-notes-ci.htmlbody
-src/content/en/til/2020/12/15/shellcheck-repo.feedentry: src/content/en/til/2020/12/15/shellcheck-repo.conf src/content/en/til/2020/12/15/shellcheck-repo.htmlbody
-src/content/en/til/2020/12/29/svg.feedentry: src/content/en/til/2020/12/29/svg.conf src/content/en/til/2020/12/29/svg.htmlbody
-src/content/en/til/2021/01/12/curl-awk-emails.feedentry: src/content/en/til/2021/01/12/curl-awk-emails.conf src/content/en/til/2021/01/12/curl-awk-emails.htmlbody
-src/content/en/til/2021/01/17/posix-shebang.feedentry: src/content/en/til/2021/01/17/posix-shebang.conf src/content/en/til/2021/01/17/posix-shebang.htmlbody
-src/content/en/til/2021/04/24/cl-generic-precedence.feedentry: src/content/en/til/2021/04/24/cl-generic-precedence.conf src/content/en/til/2021/04/24/cl-generic-precedence.htmlbody
-src/content/en/til/2021/04/24/clojure-autocurry.feedentry: src/content/en/til/2021/04/24/clojure-autocurry.conf src/content/en/til/2021/04/24/clojure-autocurry.htmlbody
-src/content/en/til/2021/04/24/scm-nif.feedentry: src/content/en/til/2021/04/24/scm-nif.conf src/content/en/til/2021/04/24/scm-nif.htmlbody
-src/content/en/til/2021/07/23/git-tls-gpg.feedentry: src/content/en/til/2021/07/23/git-tls-gpg.conf src/content/en/til/2021/07/23/git-tls-gpg.htmlbody
-src/content/en/til/2021/08/11/js-bigint-reviver.feedentry: src/content/en/til/2021/08/11/js-bigint-reviver.conf src/content/en/til/2021/08/11/js-bigint-reviver.htmlbody
-src/content/pt/hea/2020/08/12/arquivo-datado.feedentry: src/content/pt/hea/2020/08/12/arquivo-datado.conf src/content/pt/hea/2020/08/12/arquivo-datado.htmlbody
-src/content/en/blog/2018/07/17/guix-nixos.sortdata: src/content/en/blog/2018/07/17/guix-nixos.conf
-src/content/en/blog/2018/08/01/npm-ci-reproducibility.sortdata: src/content/en/blog/2018/08/01/npm-ci-reproducibility.conf
-src/content/en/blog/2018/12/21/ytdl-subs.sortdata: src/content/en/blog/2018/12/21/ytdl-subs.conf
-src/content/en/blog/2019/06/02/nixos-stateless-workstation.sortdata: src/content/en/blog/2019/06/02/nixos-stateless-workstation.conf
-src/content/en/blog/2020/08/10/guix-srht.sortdata: src/content/en/blog/2020/08/10/guix-srht.conf
-src/content/en/blog/2020/08/31/database-i-wish-i-had.sortdata: src/content/en/blog/2020/08/31/database-i-wish-i-had.conf
-src/content/en/blog/2020/10/05/cargo2nix.sortdata: src/content/en/blog/2020/10/05/cargo2nix.conf
-src/content/en/blog/2020/10/05/swift2nix.sortdata: src/content/en/blog/2020/10/05/swift2nix.conf
-src/content/en/blog/2020/10/19/feature-flags.sortdata: src/content/en/blog/2020/10/19/feature-flags.conf
-src/content/en/blog/2020/10/20/wrong-interviewing.sortdata: src/content/en/blog/2020/10/20/wrong-interviewing.conf
-src/content/en/blog/2020/11/07/diy-bugs.sortdata: src/content/en/blog/2020/11/07/diy-bugs.conf
-src/content/en/blog/2020/11/08/paradigm-shift-review.sortdata: src/content/en/blog/2020/11/08/paradigm-shift-review.conf
-src/content/en/blog/2020/11/12/database-parsers-trees.sortdata: src/content/en/blog/2020/11/12/database-parsers-trees.conf
-src/content/en/blog/2020/11/14/local-first-review.sortdata: src/content/en/blog/2020/11/14/local-first-review.conf
-src/content/en/blog/2021/01/26/remembering-ann.sortdata: src/content/en/blog/2021/01/26/remembering-ann.conf
-src/content/en/blog/2021/02/17/fallible.sortdata: src/content/en/blog/2021/02/17/fallible.conf
-src/content/en/blog/2021/04/29/relational-review.sortdata: src/content/en/blog/2021/04/29/relational-review.conf
-src/content/en/pastebin/2016/04/05/rpn.sortdata: src/content/en/pastebin/2016/04/05/rpn.conf
-src/content/en/pastebin/2018/07/11/nix-pinning.sortdata: src/content/en/pastebin/2018/07/11/nix-pinning.conf
-src/content/en/pastebin/2018/07/13/guix-nixos-systemd.sortdata: src/content/en/pastebin/2018/07/13/guix-nixos-systemd.conf
-src/content/en/pastebin/2018/07/13/guixbuilder-nixos.sortdata: src/content/en/pastebin/2018/07/13/guixbuilder-nixos.conf
-src/content/en/pastebin/2018/07/13/guixbuilder.sortdata: src/content/en/pastebin/2018/07/13/guixbuilder.conf
-src/content/en/pastebin/2018/07/13/nix-strpad.sortdata: src/content/en/pastebin/2018/07/13/nix-strpad.conf
-src/content/en/pastebin/2018/07/25/nix-exps.sortdata: src/content/en/pastebin/2018/07/25/nix-exps.conf
-src/content/en/pastebin/2018/07/25/nix-showdrv.sortdata: src/content/en/pastebin/2018/07/25/nix-showdrv.conf
-src/content/en/pastebin/2019/06/08/inconsistent-hash.sortdata: src/content/en/pastebin/2019/06/08/inconsistent-hash.conf
-src/content/en/pastebin/2019/12/29/raku-tuple-type.sortdata: src/content/en/pastebin/2019/12/29/raku-tuple-type.conf
-src/content/en/pastebin/2020/01/04/guix-import-failure.sortdata: src/content/en/pastebin/2020/01/04/guix-import-failure.conf
-src/content/en/pastebin/2020/02/14/guix-shebang.sortdata: src/content/en/pastebin/2020/02/14/guix-shebang.conf
-src/content/en/pastebin/2020/11/27/guix-build-local.sortdata: src/content/en/pastebin/2020/11/27/guix-build-local.conf
-src/content/en/pastebin/2020/12/15/guix-pack-fail.sortdata: src/content/en/pastebin/2020/12/15/guix-pack-fail.conf
-src/content/en/pastebin/2021/04/03/naive-slugify-js.sortdata: src/content/en/pastebin/2021/04/03/naive-slugify-js.conf
-src/content/en/pastebin/2021/06/08/reading-session-pt1.sortdata: src/content/en/pastebin/2021/06/08/reading-session-pt1.conf
-src/content/en/pastebin/2021/06/22/curl-wget.sortdata: src/content/en/pastebin/2021/06/22/curl-wget.conf
-src/content/en/pastebin/2021/08/11/h1-spacing.sortdata: src/content/en/pastebin/2021/08/11/h1-spacing.conf
-src/content/en/pastebin/2021/09/02/sicp-3-19.sortdata: src/content/en/pastebin/2021/09/02/sicp-3-19.conf
-src/content/en/pastebin/2021/09/03/sicp-persistent-queue.sortdata: src/content/en/pastebin/2021/09/03/sicp-persistent-queue.conf
-src/content/en/pastebin/2022/07/14/git-cleanup.sortdata: src/content/en/pastebin/2022/07/14/git-cleanup.conf
-src/content/en/pastebin/2023/07/22/funcallable-amop.sortdata: src/content/en/pastebin/2023/07/22/funcallable-amop.conf
-src/content/en/podcast/2020/12/19/test-entry.sortdata: src/content/en/podcast/2020/12/19/test-entry.conf
-src/content/en/screencast/2021/02/07/autoqemu.sortdata: src/content/en/screencast/2021/02/07/autoqemu.conf
-src/content/en/til/2020/08/12/filename-timestamp.sortdata: src/content/en/til/2020/08/12/filename-timestamp.conf
-src/content/en/til/2020/08/13/code-jekyll.sortdata: src/content/en/til/2020/08/13/code-jekyll.conf
-src/content/en/til/2020/08/14/browse-git.sortdata: src/content/en/til/2020/08/14/browse-git.conf
-src/content/en/til/2020/08/16/git-search.sortdata: src/content/en/til/2020/08/16/git-search.conf
-src/content/en/til/2020/08/28/grep-online.sortdata: src/content/en/til/2020/08/28/grep-online.conf
-src/content/en/til/2020/09/04/cli-email-fun-profit.sortdata: src/content/en/til/2020/09/04/cli-email-fun-profit.conf
-src/content/en/til/2020/09/05/oldschool-pr.sortdata: src/content/en/til/2020/09/05/oldschool-pr.conf
-src/content/en/til/2020/10/11/search-git-history.sortdata: src/content/en/til/2020/10/11/search-git-history.conf
-src/content/en/til/2020/11/08/find-broken-symlink.sortdata: src/content/en/til/2020/11/08/find-broken-symlink.conf
-src/content/en/til/2020/11/12/diy-nix-bash-ci.sortdata: src/content/en/til/2020/11/12/diy-nix-bash-ci.conf
-src/content/en/til/2020/11/12/git-bisect-automation.sortdata: src/content/en/til/2020/11/12/git-bisect-automation.conf
-src/content/en/til/2020/11/12/useful-bashvars.sortdata: src/content/en/til/2020/11/12/useful-bashvars.conf
-src/content/en/til/2020/11/14/gpodder-media.sortdata: src/content/en/til/2020/11/14/gpodder-media.conf
-src/content/en/til/2020/11/30/git-notes-ci.sortdata: src/content/en/til/2020/11/30/git-notes-ci.conf
-src/content/en/til/2020/12/15/shellcheck-repo.sortdata: src/content/en/til/2020/12/15/shellcheck-repo.conf
-src/content/en/til/2020/12/29/svg.sortdata: src/content/en/til/2020/12/29/svg.conf
-src/content/en/til/2021/01/12/curl-awk-emails.sortdata: src/content/en/til/2021/01/12/curl-awk-emails.conf
-src/content/en/til/2021/01/17/posix-shebang.sortdata: src/content/en/til/2021/01/17/posix-shebang.conf
-src/content/en/til/2021/04/24/cl-generic-precedence.sortdata: src/content/en/til/2021/04/24/cl-generic-precedence.conf
-src/content/en/til/2021/04/24/clojure-autocurry.sortdata: src/content/en/til/2021/04/24/clojure-autocurry.conf
-src/content/en/til/2021/04/24/scm-nif.sortdata: src/content/en/til/2021/04/24/scm-nif.conf
-src/content/en/til/2021/07/23/git-tls-gpg.sortdata: src/content/en/til/2021/07/23/git-tls-gpg.conf
-src/content/en/til/2021/08/11/js-bigint-reviver.sortdata: src/content/en/til/2021/08/11/js-bigint-reviver.conf
-src/content/pt/hea/2020/08/12/arquivo-datado.sortdata: src/content/pt/hea/2020/08/12/arquivo-datado.conf
-src/content/en/blog/2018/07/17/guix-nixos.categorydata: src/content/en/blog/2018/07/17/guix-nixos.conf
-src/content/en/blog/2018/08/01/npm-ci-reproducibility.categorydata: src/content/en/blog/2018/08/01/npm-ci-reproducibility.conf
-src/content/en/blog/2018/12/21/ytdl-subs.categorydata: src/content/en/blog/2018/12/21/ytdl-subs.conf
-src/content/en/blog/2019/06/02/nixos-stateless-workstation.categorydata: src/content/en/blog/2019/06/02/nixos-stateless-workstation.conf
-src/content/en/blog/2020/08/10/guix-srht.categorydata: src/content/en/blog/2020/08/10/guix-srht.conf
-src/content/en/blog/2020/08/31/database-i-wish-i-had.categorydata: src/content/en/blog/2020/08/31/database-i-wish-i-had.conf
-src/content/en/blog/2020/10/05/cargo2nix.categorydata: src/content/en/blog/2020/10/05/cargo2nix.conf
-src/content/en/blog/2020/10/05/swift2nix.categorydata: src/content/en/blog/2020/10/05/swift2nix.conf
-src/content/en/blog/2020/10/19/feature-flags.categorydata: src/content/en/blog/2020/10/19/feature-flags.conf
-src/content/en/blog/2020/10/20/wrong-interviewing.categorydata: src/content/en/blog/2020/10/20/wrong-interviewing.conf
-src/content/en/blog/2020/11/07/diy-bugs.categorydata: src/content/en/blog/2020/11/07/diy-bugs.conf
-src/content/en/blog/2020/11/08/paradigm-shift-review.categorydata: src/content/en/blog/2020/11/08/paradigm-shift-review.conf
-src/content/en/blog/2020/11/12/database-parsers-trees.categorydata: src/content/en/blog/2020/11/12/database-parsers-trees.conf
-src/content/en/blog/2020/11/14/local-first-review.categorydata: src/content/en/blog/2020/11/14/local-first-review.conf
-src/content/en/blog/2021/01/26/remembering-ann.categorydata: src/content/en/blog/2021/01/26/remembering-ann.conf
-src/content/en/blog/2021/02/17/fallible.categorydata: src/content/en/blog/2021/02/17/fallible.conf
-src/content/en/blog/2021/04/29/relational-review.categorydata: src/content/en/blog/2021/04/29/relational-review.conf
-src/content/en/pastebin/2016/04/05/rpn.categorydata: src/content/en/pastebin/2016/04/05/rpn.conf
-src/content/en/pastebin/2018/07/11/nix-pinning.categorydata: src/content/en/pastebin/2018/07/11/nix-pinning.conf
-src/content/en/pastebin/2018/07/13/guix-nixos-systemd.categorydata: src/content/en/pastebin/2018/07/13/guix-nixos-systemd.conf
-src/content/en/pastebin/2018/07/13/guixbuilder-nixos.categorydata: src/content/en/pastebin/2018/07/13/guixbuilder-nixos.conf
-src/content/en/pastebin/2018/07/13/guixbuilder.categorydata: src/content/en/pastebin/2018/07/13/guixbuilder.conf
-src/content/en/pastebin/2018/07/13/nix-strpad.categorydata: src/content/en/pastebin/2018/07/13/nix-strpad.conf
-src/content/en/pastebin/2018/07/25/nix-exps.categorydata: src/content/en/pastebin/2018/07/25/nix-exps.conf
-src/content/en/pastebin/2018/07/25/nix-showdrv.categorydata: src/content/en/pastebin/2018/07/25/nix-showdrv.conf
-src/content/en/pastebin/2019/06/08/inconsistent-hash.categorydata: src/content/en/pastebin/2019/06/08/inconsistent-hash.conf
-src/content/en/pastebin/2019/12/29/raku-tuple-type.categorydata: src/content/en/pastebin/2019/12/29/raku-tuple-type.conf
-src/content/en/pastebin/2020/01/04/guix-import-failure.categorydata: src/content/en/pastebin/2020/01/04/guix-import-failure.conf
-src/content/en/pastebin/2020/02/14/guix-shebang.categorydata: src/content/en/pastebin/2020/02/14/guix-shebang.conf
-src/content/en/pastebin/2020/11/27/guix-build-local.categorydata: src/content/en/pastebin/2020/11/27/guix-build-local.conf
-src/content/en/pastebin/2020/12/15/guix-pack-fail.categorydata: src/content/en/pastebin/2020/12/15/guix-pack-fail.conf
-src/content/en/pastebin/2021/04/03/naive-slugify-js.categorydata: src/content/en/pastebin/2021/04/03/naive-slugify-js.conf
-src/content/en/pastebin/2021/06/08/reading-session-pt1.categorydata: src/content/en/pastebin/2021/06/08/reading-session-pt1.conf
-src/content/en/pastebin/2021/06/22/curl-wget.categorydata: src/content/en/pastebin/2021/06/22/curl-wget.conf
-src/content/en/pastebin/2021/08/11/h1-spacing.categorydata: src/content/en/pastebin/2021/08/11/h1-spacing.conf
-src/content/en/pastebin/2021/09/02/sicp-3-19.categorydata: src/content/en/pastebin/2021/09/02/sicp-3-19.conf
-src/content/en/pastebin/2021/09/03/sicp-persistent-queue.categorydata: src/content/en/pastebin/2021/09/03/sicp-persistent-queue.conf
-src/content/en/pastebin/2022/07/14/git-cleanup.categorydata: src/content/en/pastebin/2022/07/14/git-cleanup.conf
-src/content/en/pastebin/2023/07/22/funcallable-amop.categorydata: src/content/en/pastebin/2023/07/22/funcallable-amop.conf
-src/content/en/podcast/2020/12/19/test-entry.categorydata: src/content/en/podcast/2020/12/19/test-entry.conf
-src/content/en/screencast/2021/02/07/autoqemu.categorydata: src/content/en/screencast/2021/02/07/autoqemu.conf
-src/content/en/til/2020/08/12/filename-timestamp.categorydata: src/content/en/til/2020/08/12/filename-timestamp.conf
-src/content/en/til/2020/08/13/code-jekyll.categorydata: src/content/en/til/2020/08/13/code-jekyll.conf
-src/content/en/til/2020/08/14/browse-git.categorydata: src/content/en/til/2020/08/14/browse-git.conf
-src/content/en/til/2020/08/16/git-search.categorydata: src/content/en/til/2020/08/16/git-search.conf
-src/content/en/til/2020/08/28/grep-online.categorydata: src/content/en/til/2020/08/28/grep-online.conf
-src/content/en/til/2020/09/04/cli-email-fun-profit.categorydata: src/content/en/til/2020/09/04/cli-email-fun-profit.conf
-src/content/en/til/2020/09/05/oldschool-pr.categorydata: src/content/en/til/2020/09/05/oldschool-pr.conf
-src/content/en/til/2020/10/11/search-git-history.categorydata: src/content/en/til/2020/10/11/search-git-history.conf
-src/content/en/til/2020/11/08/find-broken-symlink.categorydata: src/content/en/til/2020/11/08/find-broken-symlink.conf
-src/content/en/til/2020/11/12/diy-nix-bash-ci.categorydata: src/content/en/til/2020/11/12/diy-nix-bash-ci.conf
-src/content/en/til/2020/11/12/git-bisect-automation.categorydata: src/content/en/til/2020/11/12/git-bisect-automation.conf
-src/content/en/til/2020/11/12/useful-bashvars.categorydata: src/content/en/til/2020/11/12/useful-bashvars.conf
-src/content/en/til/2020/11/14/gpodder-media.categorydata: src/content/en/til/2020/11/14/gpodder-media.conf
-src/content/en/til/2020/11/30/git-notes-ci.categorydata: src/content/en/til/2020/11/30/git-notes-ci.conf
-src/content/en/til/2020/12/15/shellcheck-repo.categorydata: src/content/en/til/2020/12/15/shellcheck-repo.conf
-src/content/en/til/2020/12/29/svg.categorydata: src/content/en/til/2020/12/29/svg.conf
-src/content/en/til/2021/01/12/curl-awk-emails.categorydata: src/content/en/til/2021/01/12/curl-awk-emails.conf
-src/content/en/til/2021/01/17/posix-shebang.categorydata: src/content/en/til/2021/01/17/posix-shebang.conf
-src/content/en/til/2021/04/24/cl-generic-precedence.categorydata: src/content/en/til/2021/04/24/cl-generic-precedence.conf
-src/content/en/til/2021/04/24/clojure-autocurry.categorydata: src/content/en/til/2021/04/24/clojure-autocurry.conf
-src/content/en/til/2021/04/24/scm-nif.categorydata: src/content/en/til/2021/04/24/scm-nif.conf
-src/content/en/til/2021/07/23/git-tls-gpg.categorydata: src/content/en/til/2021/07/23/git-tls-gpg.conf
-src/content/en/til/2021/08/11/js-bigint-reviver.categorydata: src/content/en/til/2021/08/11/js-bigint-reviver.conf
-src/content/pt/hea/2020/08/12/arquivo-datado.categorydata: src/content/pt/hea/2020/08/12/arquivo-datado.conf
+src/content/en/blog/2018/07/17/guix-nixos.feedentry: src/content/en/blog/2018/07/17/guix-nixos.json src/content/en/blog/2018/07/17/guix-nixos.htmlbody
+src/content/en/blog/2018/08/01/npm-ci-reproducibility.feedentry: src/content/en/blog/2018/08/01/npm-ci-reproducibility.json src/content/en/blog/2018/08/01/npm-ci-reproducibility.htmlbody
+src/content/en/blog/2018/12/21/ytdl-subs.feedentry: src/content/en/blog/2018/12/21/ytdl-subs.json src/content/en/blog/2018/12/21/ytdl-subs.htmlbody
+src/content/en/blog/2019/06/02/nixos-stateless-workstation.feedentry: src/content/en/blog/2019/06/02/nixos-stateless-workstation.json src/content/en/blog/2019/06/02/nixos-stateless-workstation.htmlbody
+src/content/en/blog/2020/08/10/guix-srht.feedentry: src/content/en/blog/2020/08/10/guix-srht.json src/content/en/blog/2020/08/10/guix-srht.htmlbody
+src/content/en/blog/2020/08/31/database-i-wish-i-had.feedentry: src/content/en/blog/2020/08/31/database-i-wish-i-had.json src/content/en/blog/2020/08/31/database-i-wish-i-had.htmlbody
+src/content/en/blog/2020/10/05/cargo2nix.feedentry: src/content/en/blog/2020/10/05/cargo2nix.json src/content/en/blog/2020/10/05/cargo2nix.htmlbody
+src/content/en/blog/2020/10/05/swift2nix.feedentry: src/content/en/blog/2020/10/05/swift2nix.json src/content/en/blog/2020/10/05/swift2nix.htmlbody
+src/content/en/blog/2020/10/19/feature-flags.feedentry: src/content/en/blog/2020/10/19/feature-flags.json src/content/en/blog/2020/10/19/feature-flags.htmlbody
+src/content/en/blog/2020/10/20/wrong-interviewing.feedentry: src/content/en/blog/2020/10/20/wrong-interviewing.json src/content/en/blog/2020/10/20/wrong-interviewing.htmlbody
+src/content/en/blog/2020/11/07/diy-bugs.feedentry: src/content/en/blog/2020/11/07/diy-bugs.json src/content/en/blog/2020/11/07/diy-bugs.htmlbody
+src/content/en/blog/2020/11/08/paradigm-shift-review.feedentry: src/content/en/blog/2020/11/08/paradigm-shift-review.json src/content/en/blog/2020/11/08/paradigm-shift-review.htmlbody
+src/content/en/blog/2020/11/12/database-parsers-trees.feedentry: src/content/en/blog/2020/11/12/database-parsers-trees.json src/content/en/blog/2020/11/12/database-parsers-trees.htmlbody
+src/content/en/blog/2020/11/14/local-first-review.feedentry: src/content/en/blog/2020/11/14/local-first-review.json src/content/en/blog/2020/11/14/local-first-review.htmlbody
+src/content/en/blog/2021/01/26/remembering-ann.feedentry: src/content/en/blog/2021/01/26/remembering-ann.json src/content/en/blog/2021/01/26/remembering-ann.htmlbody
+src/content/en/blog/2021/02/17/fallible.feedentry: src/content/en/blog/2021/02/17/fallible.json src/content/en/blog/2021/02/17/fallible.htmlbody
+src/content/en/blog/2021/04/29/relational-review.feedentry: src/content/en/blog/2021/04/29/relational-review.json src/content/en/blog/2021/04/29/relational-review.htmlbody
+src/content/en/pastebin/2016/04/05/rpn.feedentry: src/content/en/pastebin/2016/04/05/rpn.json src/content/en/pastebin/2016/04/05/rpn.htmlbody
+src/content/en/pastebin/2018/07/11/nix-pinning.feedentry: src/content/en/pastebin/2018/07/11/nix-pinning.json src/content/en/pastebin/2018/07/11/nix-pinning.htmlbody
+src/content/en/pastebin/2018/07/13/guix-nixos-systemd.feedentry: src/content/en/pastebin/2018/07/13/guix-nixos-systemd.json src/content/en/pastebin/2018/07/13/guix-nixos-systemd.htmlbody
+src/content/en/pastebin/2018/07/13/guixbuilder-nixos.feedentry: src/content/en/pastebin/2018/07/13/guixbuilder-nixos.json src/content/en/pastebin/2018/07/13/guixbuilder-nixos.htmlbody
+src/content/en/pastebin/2018/07/13/guixbuilder.feedentry: src/content/en/pastebin/2018/07/13/guixbuilder.json src/content/en/pastebin/2018/07/13/guixbuilder.htmlbody
+src/content/en/pastebin/2018/07/13/nix-strpad.feedentry: src/content/en/pastebin/2018/07/13/nix-strpad.json src/content/en/pastebin/2018/07/13/nix-strpad.htmlbody
+src/content/en/pastebin/2018/07/25/nix-exps.feedentry: src/content/en/pastebin/2018/07/25/nix-exps.json src/content/en/pastebin/2018/07/25/nix-exps.htmlbody
+src/content/en/pastebin/2018/07/25/nix-showdrv.feedentry: src/content/en/pastebin/2018/07/25/nix-showdrv.json src/content/en/pastebin/2018/07/25/nix-showdrv.htmlbody
+src/content/en/pastebin/2019/06/08/inconsistent-hash.feedentry: src/content/en/pastebin/2019/06/08/inconsistent-hash.json src/content/en/pastebin/2019/06/08/inconsistent-hash.htmlbody
+src/content/en/pastebin/2019/12/29/raku-tuple-type.feedentry: src/content/en/pastebin/2019/12/29/raku-tuple-type.json src/content/en/pastebin/2019/12/29/raku-tuple-type.htmlbody
+src/content/en/pastebin/2020/01/04/guix-import-failure.feedentry: src/content/en/pastebin/2020/01/04/guix-import-failure.json src/content/en/pastebin/2020/01/04/guix-import-failure.htmlbody
+src/content/en/pastebin/2020/02/14/guix-shebang.feedentry: src/content/en/pastebin/2020/02/14/guix-shebang.json src/content/en/pastebin/2020/02/14/guix-shebang.htmlbody
+src/content/en/pastebin/2020/11/27/guix-build-local.feedentry: src/content/en/pastebin/2020/11/27/guix-build-local.json src/content/en/pastebin/2020/11/27/guix-build-local.htmlbody
+src/content/en/pastebin/2020/12/15/guix-pack-fail.feedentry: src/content/en/pastebin/2020/12/15/guix-pack-fail.json src/content/en/pastebin/2020/12/15/guix-pack-fail.htmlbody
+src/content/en/pastebin/2021/04/03/naive-slugify-js.feedentry: src/content/en/pastebin/2021/04/03/naive-slugify-js.json src/content/en/pastebin/2021/04/03/naive-slugify-js.htmlbody
+src/content/en/pastebin/2021/06/08/reading-session-pt1.feedentry: src/content/en/pastebin/2021/06/08/reading-session-pt1.json src/content/en/pastebin/2021/06/08/reading-session-pt1.htmlbody
+src/content/en/pastebin/2021/06/22/curl-wget.feedentry: src/content/en/pastebin/2021/06/22/curl-wget.json src/content/en/pastebin/2021/06/22/curl-wget.htmlbody
+src/content/en/pastebin/2021/08/11/h1-spacing.feedentry: src/content/en/pastebin/2021/08/11/h1-spacing.json src/content/en/pastebin/2021/08/11/h1-spacing.htmlbody
+src/content/en/pastebin/2021/09/02/sicp-3-19.feedentry: src/content/en/pastebin/2021/09/02/sicp-3-19.json src/content/en/pastebin/2021/09/02/sicp-3-19.htmlbody
+src/content/en/pastebin/2021/09/03/sicp-persistent-queue.feedentry: src/content/en/pastebin/2021/09/03/sicp-persistent-queue.json src/content/en/pastebin/2021/09/03/sicp-persistent-queue.htmlbody
+src/content/en/pastebin/2022/07/14/git-cleanup.feedentry: src/content/en/pastebin/2022/07/14/git-cleanup.json src/content/en/pastebin/2022/07/14/git-cleanup.htmlbody
+src/content/en/pastebin/2023/07/22/funcallable-amop.feedentry: src/content/en/pastebin/2023/07/22/funcallable-amop.json src/content/en/pastebin/2023/07/22/funcallable-amop.htmlbody
+src/content/en/podcast/2020/12/19/test-entry.feedentry: src/content/en/podcast/2020/12/19/test-entry.json src/content/en/podcast/2020/12/19/test-entry.htmlbody
+src/content/en/screencast/2021/02/07/autoqemu.feedentry: src/content/en/screencast/2021/02/07/autoqemu.json src/content/en/screencast/2021/02/07/autoqemu.htmlbody
+src/content/en/slide/2020/10/19/feature-flags.feedentry: src/content/en/slide/2020/10/19/feature-flags.json src/content/en/slide/2020/10/19/feature-flags.htmlbody
+src/content/en/slide/2020/11/14/local-first-hype.feedentry: src/content/en/slide/2020/11/14/local-first-hype.json src/content/en/slide/2020/11/14/local-first-hype.htmlbody
+src/content/en/til/2020/08/12/filename-timestamp.feedentry: src/content/en/til/2020/08/12/filename-timestamp.json src/content/en/til/2020/08/12/filename-timestamp.htmlbody
+src/content/en/til/2020/08/13/code-jekyll.feedentry: src/content/en/til/2020/08/13/code-jekyll.json src/content/en/til/2020/08/13/code-jekyll.htmlbody
+src/content/en/til/2020/08/14/browse-git.feedentry: src/content/en/til/2020/08/14/browse-git.json src/content/en/til/2020/08/14/browse-git.htmlbody
+src/content/en/til/2020/08/16/git-search.feedentry: src/content/en/til/2020/08/16/git-search.json src/content/en/til/2020/08/16/git-search.htmlbody
+src/content/en/til/2020/08/28/grep-online.feedentry: src/content/en/til/2020/08/28/grep-online.json src/content/en/til/2020/08/28/grep-online.htmlbody
+src/content/en/til/2020/09/04/cli-email-fun-profit.feedentry: src/content/en/til/2020/09/04/cli-email-fun-profit.json src/content/en/til/2020/09/04/cli-email-fun-profit.htmlbody
+src/content/en/til/2020/09/05/oldschool-pr.feedentry: src/content/en/til/2020/09/05/oldschool-pr.json src/content/en/til/2020/09/05/oldschool-pr.htmlbody
+src/content/en/til/2020/10/11/search-git-history.feedentry: src/content/en/til/2020/10/11/search-git-history.json src/content/en/til/2020/10/11/search-git-history.htmlbody
+src/content/en/til/2020/11/08/find-broken-symlink.feedentry: src/content/en/til/2020/11/08/find-broken-symlink.json src/content/en/til/2020/11/08/find-broken-symlink.htmlbody
+src/content/en/til/2020/11/12/diy-nix-bash-ci.feedentry: src/content/en/til/2020/11/12/diy-nix-bash-ci.json src/content/en/til/2020/11/12/diy-nix-bash-ci.htmlbody
+src/content/en/til/2020/11/12/git-bisect-automation.feedentry: src/content/en/til/2020/11/12/git-bisect-automation.json src/content/en/til/2020/11/12/git-bisect-automation.htmlbody
+src/content/en/til/2020/11/12/useful-bashvars.feedentry: src/content/en/til/2020/11/12/useful-bashvars.json src/content/en/til/2020/11/12/useful-bashvars.htmlbody
+src/content/en/til/2020/11/14/gpodder-media.feedentry: src/content/en/til/2020/11/14/gpodder-media.json src/content/en/til/2020/11/14/gpodder-media.htmlbody
+src/content/en/til/2020/11/30/git-notes-ci.feedentry: src/content/en/til/2020/11/30/git-notes-ci.json src/content/en/til/2020/11/30/git-notes-ci.htmlbody
+src/content/en/til/2020/12/15/shellcheck-repo.feedentry: src/content/en/til/2020/12/15/shellcheck-repo.json src/content/en/til/2020/12/15/shellcheck-repo.htmlbody
+src/content/en/til/2020/12/29/svg.feedentry: src/content/en/til/2020/12/29/svg.json src/content/en/til/2020/12/29/svg.htmlbody
+src/content/en/til/2021/01/12/curl-awk-emails.feedentry: src/content/en/til/2021/01/12/curl-awk-emails.json src/content/en/til/2021/01/12/curl-awk-emails.htmlbody
+src/content/en/til/2021/01/17/posix-shebang.feedentry: src/content/en/til/2021/01/17/posix-shebang.json src/content/en/til/2021/01/17/posix-shebang.htmlbody
+src/content/en/til/2021/04/24/cl-generic-precedence.feedentry: src/content/en/til/2021/04/24/cl-generic-precedence.json src/content/en/til/2021/04/24/cl-generic-precedence.htmlbody
+src/content/en/til/2021/04/24/clojure-autocurry.feedentry: src/content/en/til/2021/04/24/clojure-autocurry.json src/content/en/til/2021/04/24/clojure-autocurry.htmlbody
+src/content/en/til/2021/04/24/scm-nif.feedentry: src/content/en/til/2021/04/24/scm-nif.json src/content/en/til/2021/04/24/scm-nif.htmlbody
+src/content/en/til/2021/07/23/git-tls-gpg.feedentry: src/content/en/til/2021/07/23/git-tls-gpg.json src/content/en/til/2021/07/23/git-tls-gpg.htmlbody
+src/content/en/til/2021/08/11/js-bigint-reviver.feedentry: src/content/en/til/2021/08/11/js-bigint-reviver.json src/content/en/til/2021/08/11/js-bigint-reviver.htmlbody
+src/content/pt/hea/2020/08/12/arquivo-datado.feedentry: src/content/pt/hea/2020/08/12/arquivo-datado.json src/content/pt/hea/2020/08/12/arquivo-datado.htmlbody
+src/content/en/blog/2018/07/17/guix-nixos.sortdata: src/content/en/blog/2018/07/17/guix-nixos.json
+src/content/en/blog/2018/08/01/npm-ci-reproducibility.sortdata: src/content/en/blog/2018/08/01/npm-ci-reproducibility.json
+src/content/en/blog/2018/12/21/ytdl-subs.sortdata: src/content/en/blog/2018/12/21/ytdl-subs.json
+src/content/en/blog/2019/06/02/nixos-stateless-workstation.sortdata: src/content/en/blog/2019/06/02/nixos-stateless-workstation.json
+src/content/en/blog/2020/08/10/guix-srht.sortdata: src/content/en/blog/2020/08/10/guix-srht.json
+src/content/en/blog/2020/08/31/database-i-wish-i-had.sortdata: src/content/en/blog/2020/08/31/database-i-wish-i-had.json
+src/content/en/blog/2020/10/05/cargo2nix.sortdata: src/content/en/blog/2020/10/05/cargo2nix.json
+src/content/en/blog/2020/10/05/swift2nix.sortdata: src/content/en/blog/2020/10/05/swift2nix.json
+src/content/en/blog/2020/10/19/feature-flags.sortdata: src/content/en/blog/2020/10/19/feature-flags.json
+src/content/en/blog/2020/10/20/wrong-interviewing.sortdata: src/content/en/blog/2020/10/20/wrong-interviewing.json
+src/content/en/blog/2020/11/07/diy-bugs.sortdata: src/content/en/blog/2020/11/07/diy-bugs.json
+src/content/en/blog/2020/11/08/paradigm-shift-review.sortdata: src/content/en/blog/2020/11/08/paradigm-shift-review.json
+src/content/en/blog/2020/11/12/database-parsers-trees.sortdata: src/content/en/blog/2020/11/12/database-parsers-trees.json
+src/content/en/blog/2020/11/14/local-first-review.sortdata: src/content/en/blog/2020/11/14/local-first-review.json
+src/content/en/blog/2021/01/26/remembering-ann.sortdata: src/content/en/blog/2021/01/26/remembering-ann.json
+src/content/en/blog/2021/02/17/fallible.sortdata: src/content/en/blog/2021/02/17/fallible.json
+src/content/en/blog/2021/04/29/relational-review.sortdata: src/content/en/blog/2021/04/29/relational-review.json
+src/content/en/pastebin/2016/04/05/rpn.sortdata: src/content/en/pastebin/2016/04/05/rpn.json
+src/content/en/pastebin/2018/07/11/nix-pinning.sortdata: src/content/en/pastebin/2018/07/11/nix-pinning.json
+src/content/en/pastebin/2018/07/13/guix-nixos-systemd.sortdata: src/content/en/pastebin/2018/07/13/guix-nixos-systemd.json
+src/content/en/pastebin/2018/07/13/guixbuilder-nixos.sortdata: src/content/en/pastebin/2018/07/13/guixbuilder-nixos.json
+src/content/en/pastebin/2018/07/13/guixbuilder.sortdata: src/content/en/pastebin/2018/07/13/guixbuilder.json
+src/content/en/pastebin/2018/07/13/nix-strpad.sortdata: src/content/en/pastebin/2018/07/13/nix-strpad.json
+src/content/en/pastebin/2018/07/25/nix-exps.sortdata: src/content/en/pastebin/2018/07/25/nix-exps.json
+src/content/en/pastebin/2018/07/25/nix-showdrv.sortdata: src/content/en/pastebin/2018/07/25/nix-showdrv.json
+src/content/en/pastebin/2019/06/08/inconsistent-hash.sortdata: src/content/en/pastebin/2019/06/08/inconsistent-hash.json
+src/content/en/pastebin/2019/12/29/raku-tuple-type.sortdata: src/content/en/pastebin/2019/12/29/raku-tuple-type.json
+src/content/en/pastebin/2020/01/04/guix-import-failure.sortdata: src/content/en/pastebin/2020/01/04/guix-import-failure.json
+src/content/en/pastebin/2020/02/14/guix-shebang.sortdata: src/content/en/pastebin/2020/02/14/guix-shebang.json
+src/content/en/pastebin/2020/11/27/guix-build-local.sortdata: src/content/en/pastebin/2020/11/27/guix-build-local.json
+src/content/en/pastebin/2020/12/15/guix-pack-fail.sortdata: src/content/en/pastebin/2020/12/15/guix-pack-fail.json
+src/content/en/pastebin/2021/04/03/naive-slugify-js.sortdata: src/content/en/pastebin/2021/04/03/naive-slugify-js.json
+src/content/en/pastebin/2021/06/08/reading-session-pt1.sortdata: src/content/en/pastebin/2021/06/08/reading-session-pt1.json
+src/content/en/pastebin/2021/06/22/curl-wget.sortdata: src/content/en/pastebin/2021/06/22/curl-wget.json
+src/content/en/pastebin/2021/08/11/h1-spacing.sortdata: src/content/en/pastebin/2021/08/11/h1-spacing.json
+src/content/en/pastebin/2021/09/02/sicp-3-19.sortdata: src/content/en/pastebin/2021/09/02/sicp-3-19.json
+src/content/en/pastebin/2021/09/03/sicp-persistent-queue.sortdata: src/content/en/pastebin/2021/09/03/sicp-persistent-queue.json
+src/content/en/pastebin/2022/07/14/git-cleanup.sortdata: src/content/en/pastebin/2022/07/14/git-cleanup.json
+src/content/en/pastebin/2023/07/22/funcallable-amop.sortdata: src/content/en/pastebin/2023/07/22/funcallable-amop.json
+src/content/en/podcast/2020/12/19/test-entry.sortdata: src/content/en/podcast/2020/12/19/test-entry.json
+src/content/en/screencast/2021/02/07/autoqemu.sortdata: src/content/en/screencast/2021/02/07/autoqemu.json
+src/content/en/slide/2020/10/19/feature-flags.sortdata: src/content/en/slide/2020/10/19/feature-flags.json
+src/content/en/slide/2020/11/14/local-first-hype.sortdata: src/content/en/slide/2020/11/14/local-first-hype.json
+src/content/en/til/2020/08/12/filename-timestamp.sortdata: src/content/en/til/2020/08/12/filename-timestamp.json
+src/content/en/til/2020/08/13/code-jekyll.sortdata: src/content/en/til/2020/08/13/code-jekyll.json
+src/content/en/til/2020/08/14/browse-git.sortdata: src/content/en/til/2020/08/14/browse-git.json
+src/content/en/til/2020/08/16/git-search.sortdata: src/content/en/til/2020/08/16/git-search.json
+src/content/en/til/2020/08/28/grep-online.sortdata: src/content/en/til/2020/08/28/grep-online.json
+src/content/en/til/2020/09/04/cli-email-fun-profit.sortdata: src/content/en/til/2020/09/04/cli-email-fun-profit.json
+src/content/en/til/2020/09/05/oldschool-pr.sortdata: src/content/en/til/2020/09/05/oldschool-pr.json
+src/content/en/til/2020/10/11/search-git-history.sortdata: src/content/en/til/2020/10/11/search-git-history.json
+src/content/en/til/2020/11/08/find-broken-symlink.sortdata: src/content/en/til/2020/11/08/find-broken-symlink.json
+src/content/en/til/2020/11/12/diy-nix-bash-ci.sortdata: src/content/en/til/2020/11/12/diy-nix-bash-ci.json
+src/content/en/til/2020/11/12/git-bisect-automation.sortdata: src/content/en/til/2020/11/12/git-bisect-automation.json
+src/content/en/til/2020/11/12/useful-bashvars.sortdata: src/content/en/til/2020/11/12/useful-bashvars.json
+src/content/en/til/2020/11/14/gpodder-media.sortdata: src/content/en/til/2020/11/14/gpodder-media.json
+src/content/en/til/2020/11/30/git-notes-ci.sortdata: src/content/en/til/2020/11/30/git-notes-ci.json
+src/content/en/til/2020/12/15/shellcheck-repo.sortdata: src/content/en/til/2020/12/15/shellcheck-repo.json
+src/content/en/til/2020/12/29/svg.sortdata: src/content/en/til/2020/12/29/svg.json
+src/content/en/til/2021/01/12/curl-awk-emails.sortdata: src/content/en/til/2021/01/12/curl-awk-emails.json
+src/content/en/til/2021/01/17/posix-shebang.sortdata: src/content/en/til/2021/01/17/posix-shebang.json
+src/content/en/til/2021/04/24/cl-generic-precedence.sortdata: src/content/en/til/2021/04/24/cl-generic-precedence.json
+src/content/en/til/2021/04/24/clojure-autocurry.sortdata: src/content/en/til/2021/04/24/clojure-autocurry.json
+src/content/en/til/2021/04/24/scm-nif.sortdata: src/content/en/til/2021/04/24/scm-nif.json
+src/content/en/til/2021/07/23/git-tls-gpg.sortdata: src/content/en/til/2021/07/23/git-tls-gpg.json
+src/content/en/til/2021/08/11/js-bigint-reviver.sortdata: src/content/en/til/2021/08/11/js-bigint-reviver.json
+src/content/pt/hea/2020/08/12/arquivo-datado.sortdata: src/content/pt/hea/2020/08/12/arquivo-datado.json
+src/content/en/blog/2018/07/17/guix-nixos.categorydata: src/content/en/blog/2018/07/17/guix-nixos.json
+src/content/en/blog/2018/08/01/npm-ci-reproducibility.categorydata: src/content/en/blog/2018/08/01/npm-ci-reproducibility.json
+src/content/en/blog/2018/12/21/ytdl-subs.categorydata: src/content/en/blog/2018/12/21/ytdl-subs.json
+src/content/en/blog/2019/06/02/nixos-stateless-workstation.categorydata: src/content/en/blog/2019/06/02/nixos-stateless-workstation.json
+src/content/en/blog/2020/08/10/guix-srht.categorydata: src/content/en/blog/2020/08/10/guix-srht.json
+src/content/en/blog/2020/08/31/database-i-wish-i-had.categorydata: src/content/en/blog/2020/08/31/database-i-wish-i-had.json
+src/content/en/blog/2020/10/05/cargo2nix.categorydata: src/content/en/blog/2020/10/05/cargo2nix.json
+src/content/en/blog/2020/10/05/swift2nix.categorydata: src/content/en/blog/2020/10/05/swift2nix.json
+src/content/en/blog/2020/10/19/feature-flags.categorydata: src/content/en/blog/2020/10/19/feature-flags.json
+src/content/en/blog/2020/10/20/wrong-interviewing.categorydata: src/content/en/blog/2020/10/20/wrong-interviewing.json
+src/content/en/blog/2020/11/07/diy-bugs.categorydata: src/content/en/blog/2020/11/07/diy-bugs.json
+src/content/en/blog/2020/11/08/paradigm-shift-review.categorydata: src/content/en/blog/2020/11/08/paradigm-shift-review.json
+src/content/en/blog/2020/11/12/database-parsers-trees.categorydata: src/content/en/blog/2020/11/12/database-parsers-trees.json
+src/content/en/blog/2020/11/14/local-first-review.categorydata: src/content/en/blog/2020/11/14/local-first-review.json
+src/content/en/blog/2021/01/26/remembering-ann.categorydata: src/content/en/blog/2021/01/26/remembering-ann.json
+src/content/en/blog/2021/02/17/fallible.categorydata: src/content/en/blog/2021/02/17/fallible.json
+src/content/en/blog/2021/04/29/relational-review.categorydata: src/content/en/blog/2021/04/29/relational-review.json
+src/content/en/pastebin/2016/04/05/rpn.categorydata: src/content/en/pastebin/2016/04/05/rpn.json
+src/content/en/pastebin/2018/07/11/nix-pinning.categorydata: src/content/en/pastebin/2018/07/11/nix-pinning.json
+src/content/en/pastebin/2018/07/13/guix-nixos-systemd.categorydata: src/content/en/pastebin/2018/07/13/guix-nixos-systemd.json
+src/content/en/pastebin/2018/07/13/guixbuilder-nixos.categorydata: src/content/en/pastebin/2018/07/13/guixbuilder-nixos.json
+src/content/en/pastebin/2018/07/13/guixbuilder.categorydata: src/content/en/pastebin/2018/07/13/guixbuilder.json
+src/content/en/pastebin/2018/07/13/nix-strpad.categorydata: src/content/en/pastebin/2018/07/13/nix-strpad.json
+src/content/en/pastebin/2018/07/25/nix-exps.categorydata: src/content/en/pastebin/2018/07/25/nix-exps.json
+src/content/en/pastebin/2018/07/25/nix-showdrv.categorydata: src/content/en/pastebin/2018/07/25/nix-showdrv.json
+src/content/en/pastebin/2019/06/08/inconsistent-hash.categorydata: src/content/en/pastebin/2019/06/08/inconsistent-hash.json
+src/content/en/pastebin/2019/12/29/raku-tuple-type.categorydata: src/content/en/pastebin/2019/12/29/raku-tuple-type.json
+src/content/en/pastebin/2020/01/04/guix-import-failure.categorydata: src/content/en/pastebin/2020/01/04/guix-import-failure.json
+src/content/en/pastebin/2020/02/14/guix-shebang.categorydata: src/content/en/pastebin/2020/02/14/guix-shebang.json
+src/content/en/pastebin/2020/11/27/guix-build-local.categorydata: src/content/en/pastebin/2020/11/27/guix-build-local.json
+src/content/en/pastebin/2020/12/15/guix-pack-fail.categorydata: src/content/en/pastebin/2020/12/15/guix-pack-fail.json
+src/content/en/pastebin/2021/04/03/naive-slugify-js.categorydata: src/content/en/pastebin/2021/04/03/naive-slugify-js.json
+src/content/en/pastebin/2021/06/08/reading-session-pt1.categorydata: src/content/en/pastebin/2021/06/08/reading-session-pt1.json
+src/content/en/pastebin/2021/06/22/curl-wget.categorydata: src/content/en/pastebin/2021/06/22/curl-wget.json
+src/content/en/pastebin/2021/08/11/h1-spacing.categorydata: src/content/en/pastebin/2021/08/11/h1-spacing.json
+src/content/en/pastebin/2021/09/02/sicp-3-19.categorydata: src/content/en/pastebin/2021/09/02/sicp-3-19.json
+src/content/en/pastebin/2021/09/03/sicp-persistent-queue.categorydata: src/content/en/pastebin/2021/09/03/sicp-persistent-queue.json
+src/content/en/pastebin/2022/07/14/git-cleanup.categorydata: src/content/en/pastebin/2022/07/14/git-cleanup.json
+src/content/en/pastebin/2023/07/22/funcallable-amop.categorydata: src/content/en/pastebin/2023/07/22/funcallable-amop.json
+src/content/en/podcast/2020/12/19/test-entry.categorydata: src/content/en/podcast/2020/12/19/test-entry.json
+src/content/en/screencast/2021/02/07/autoqemu.categorydata: src/content/en/screencast/2021/02/07/autoqemu.json
+src/content/en/slide/2020/10/19/feature-flags.categorydata: src/content/en/slide/2020/10/19/feature-flags.json
+src/content/en/slide/2020/11/14/local-first-hype.categorydata: src/content/en/slide/2020/11/14/local-first-hype.json
+src/content/en/til/2020/08/12/filename-timestamp.categorydata: src/content/en/til/2020/08/12/filename-timestamp.json
+src/content/en/til/2020/08/13/code-jekyll.categorydata: src/content/en/til/2020/08/13/code-jekyll.json
+src/content/en/til/2020/08/14/browse-git.categorydata: src/content/en/til/2020/08/14/browse-git.json
+src/content/en/til/2020/08/16/git-search.categorydata: src/content/en/til/2020/08/16/git-search.json
+src/content/en/til/2020/08/28/grep-online.categorydata: src/content/en/til/2020/08/28/grep-online.json
+src/content/en/til/2020/09/04/cli-email-fun-profit.categorydata: src/content/en/til/2020/09/04/cli-email-fun-profit.json
+src/content/en/til/2020/09/05/oldschool-pr.categorydata: src/content/en/til/2020/09/05/oldschool-pr.json
+src/content/en/til/2020/10/11/search-git-history.categorydata: src/content/en/til/2020/10/11/search-git-history.json
+src/content/en/til/2020/11/08/find-broken-symlink.categorydata: src/content/en/til/2020/11/08/find-broken-symlink.json
+src/content/en/til/2020/11/12/diy-nix-bash-ci.categorydata: src/content/en/til/2020/11/12/diy-nix-bash-ci.json
+src/content/en/til/2020/11/12/git-bisect-automation.categorydata: src/content/en/til/2020/11/12/git-bisect-automation.json
+src/content/en/til/2020/11/12/useful-bashvars.categorydata: src/content/en/til/2020/11/12/useful-bashvars.json
+src/content/en/til/2020/11/14/gpodder-media.categorydata: src/content/en/til/2020/11/14/gpodder-media.json
+src/content/en/til/2020/11/30/git-notes-ci.categorydata: src/content/en/til/2020/11/30/git-notes-ci.json
+src/content/en/til/2020/12/15/shellcheck-repo.categorydata: src/content/en/til/2020/12/15/shellcheck-repo.json
+src/content/en/til/2020/12/29/svg.categorydata: src/content/en/til/2020/12/29/svg.json
+src/content/en/til/2021/01/12/curl-awk-emails.categorydata: src/content/en/til/2021/01/12/curl-awk-emails.json
+src/content/en/til/2021/01/17/posix-shebang.categorydata: src/content/en/til/2021/01/17/posix-shebang.json
+src/content/en/til/2021/04/24/cl-generic-precedence.categorydata: src/content/en/til/2021/04/24/cl-generic-precedence.json
+src/content/en/til/2021/04/24/clojure-autocurry.categorydata: src/content/en/til/2021/04/24/clojure-autocurry.json
+src/content/en/til/2021/04/24/scm-nif.categorydata: src/content/en/til/2021/04/24/scm-nif.json
+src/content/en/til/2021/07/23/git-tls-gpg.categorydata: src/content/en/til/2021/07/23/git-tls-gpg.json
+src/content/en/til/2021/08/11/js-bigint-reviver.categorydata: src/content/en/til/2021/08/11/js-bigint-reviver.json
+src/content/pt/hea/2020/08/12/arquivo-datado.categorydata: src/content/pt/hea/2020/08/12/arquivo-datado.json
-src/content/en/blog/index.htmlheader src/content/en/blog/index.htmlfooter: src/content/en/blog/index.conf
-src/content/en/pastebin/index.htmlheader src/content/en/pastebin/index.htmlfooter: src/content/en/pastebin/index.conf
-src/content/en/podcast/index.htmlheader src/content/en/podcast/index.htmlfooter: src/content/en/podcast/index.conf
-src/content/en/screencast/index.htmlheader src/content/en/screencast/index.htmlfooter: src/content/en/screencast/index.conf
-src/content/en/til/index.htmlheader src/content/en/til/index.htmlfooter: src/content/en/til/index.conf
-src/content/pt/hea/index.htmlheader src/content/pt/hea/index.htmlfooter: src/content/pt/hea/index.conf
-src/content/en/blog/categories.htmlheader src/content/en/blog/categories.htmlfooter: src/content/en/blog/categories.conf
-src/content/en/pastebin/categories.htmlheader src/content/en/pastebin/categories.htmlfooter: src/content/en/pastebin/categories.conf
-src/content/en/podcast/categories.htmlheader src/content/en/podcast/categories.htmlfooter: src/content/en/podcast/categories.conf
-src/content/en/screencast/categories.htmlheader src/content/en/screencast/categories.htmlfooter: src/content/en/screencast/categories.conf
-src/content/en/til/categories.htmlheader src/content/en/til/categories.htmlfooter: src/content/en/til/categories.conf
-src/content/pt/hea/categorias.htmlheader src/content/pt/hea/categorias.htmlfooter: src/content/pt/hea/categorias.conf
-src/content/en/blog/index.htmllisting: src/content/en/blog/index.conf
-src/content/en/pastebin/index.htmllisting: src/content/en/pastebin/index.conf
-src/content/en/podcast/index.htmllisting: src/content/en/podcast/index.conf
-src/content/en/screencast/index.htmllisting: src/content/en/screencast/index.conf
-src/content/en/til/index.htmllisting: src/content/en/til/index.conf
-src/content/pt/hea/index.htmllisting: src/content/pt/hea/index.conf
-src/content/en/blog/categories.htmllisting: src/content/en/blog/categories.conf
-src/content/en/pastebin/categories.htmllisting: src/content/en/pastebin/categories.conf
-src/content/en/podcast/categories.htmllisting: src/content/en/podcast/categories.conf
-src/content/en/screencast/categories.htmllisting: src/content/en/screencast/categories.conf
-src/content/en/til/categories.htmllisting: src/content/en/til/categories.conf
-src/content/pt/hea/categorias.htmllisting: src/content/pt/hea/categorias.conf
+src/content/de/blog/index.htmlheader src/content/de/blog/index.htmlfooter: src/content/de/blog/index.json
+src/content/de/folien/index.htmlheader src/content/de/folien/index.htmlfooter: src/content/de/folien/index.json
+src/content/de/hhg/index.htmlheader src/content/de/hhg/index.htmlfooter: src/content/de/hhg/index.json
+src/content/de/pastebin/index.htmlheader src/content/de/pastebin/index.htmlfooter: src/content/de/pastebin/index.json
+src/content/de/podcast/index.htmlheader src/content/de/podcast/index.htmlfooter: src/content/de/podcast/index.json
+src/content/de/screencast/index.htmlheader src/content/de/screencast/index.htmlfooter: src/content/de/screencast/index.json
+src/content/en/blog/index.htmlheader src/content/en/blog/index.htmlfooter: src/content/en/blog/index.json
+src/content/en/pastebin/index.htmlheader src/content/en/pastebin/index.htmlfooter: src/content/en/pastebin/index.json
+src/content/en/podcast/index.htmlheader src/content/en/podcast/index.htmlfooter: src/content/en/podcast/index.json
+src/content/en/screencast/index.htmlheader src/content/en/screencast/index.htmlfooter: src/content/en/screencast/index.json
+src/content/en/slide/index.htmlheader src/content/en/slide/index.htmlfooter: src/content/en/slide/index.json
+src/content/en/til/index.htmlheader src/content/en/til/index.htmlfooter: src/content/en/til/index.json
+src/content/eo/blog/index.htmlheader src/content/eo/blog/index.htmlfooter: src/content/eo/blog/index.json
+src/content/eo/hml/index.htmlheader src/content/eo/hml/index.htmlfooter: src/content/eo/hml/index.json
+src/content/eo/lumbildoj/index.htmlheader src/content/eo/lumbildoj/index.htmlfooter: src/content/eo/lumbildoj/index.json
+src/content/eo/pastebin/index.htmlheader src/content/eo/pastebin/index.htmlfooter: src/content/eo/pastebin/index.json
+src/content/eo/podkasto/index.htmlheader src/content/eo/podkasto/index.htmlfooter: src/content/eo/podkasto/index.json
+src/content/eo/screencast/index.htmlheader src/content/eo/screencast/index.htmlfooter: src/content/eo/screencast/index.json
+src/content/es/blog/index.htmlheader src/content/es/blog/index.htmlfooter: src/content/es/blog/index.json
+src/content/es/diapositivas/index.htmlheader src/content/es/diapositivas/index.htmlfooter: src/content/es/diapositivas/index.json
+src/content/es/haa/index.htmlheader src/content/es/haa/index.htmlfooter: src/content/es/haa/index.json
+src/content/es/pastebin/index.htmlheader src/content/es/pastebin/index.htmlfooter: src/content/es/pastebin/index.json
+src/content/es/podcast/index.htmlheader src/content/es/podcast/index.htmlfooter: src/content/es/podcast/index.json
+src/content/es/screencast/index.htmlheader src/content/es/screencast/index.htmlfooter: src/content/es/screencast/index.json
+src/content/fr/aja/index.htmlheader src/content/fr/aja/index.htmlfooter: src/content/fr/aja/index.json
+src/content/fr/blog/index.htmlheader src/content/fr/blog/index.htmlfooter: src/content/fr/blog/index.json
+src/content/fr/diapositives/index.htmlheader src/content/fr/diapositives/index.htmlfooter: src/content/fr/diapositives/index.json
+src/content/fr/pastebin/index.htmlheader src/content/fr/pastebin/index.htmlfooter: src/content/fr/pastebin/index.json
+src/content/fr/podcast/index.htmlheader src/content/fr/podcast/index.htmlfooter: src/content/fr/podcast/index.json
+src/content/fr/screencast/index.htmlheader src/content/fr/screencast/index.htmlfooter: src/content/fr/screencast/index.json
+src/content/pt/blog/index.htmlheader src/content/pt/blog/index.htmlfooter: src/content/pt/blog/index.json
+src/content/pt/eslaides/index.htmlheader src/content/pt/eslaides/index.htmlfooter: src/content/pt/eslaides/index.json
+src/content/pt/hea/index.htmlheader src/content/pt/hea/index.htmlfooter: src/content/pt/hea/index.json
+src/content/pt/pastebin/index.htmlheader src/content/pt/pastebin/index.htmlfooter: src/content/pt/pastebin/index.json
+src/content/pt/podcast/index.htmlheader src/content/pt/podcast/index.htmlfooter: src/content/pt/podcast/index.json
+src/content/pt/screencast/index.htmlheader src/content/pt/screencast/index.htmlfooter: src/content/pt/screencast/index.json
+src/content/de/blog/kategorien.htmlheader src/content/de/blog/kategorien.htmlfooter: src/content/de/blog/kategorien.json
+src/content/de/folien/kategorien.htmlheader src/content/de/folien/kategorien.htmlfooter: src/content/de/folien/kategorien.json
+src/content/de/hhg/kategorien.htmlheader src/content/de/hhg/kategorien.htmlfooter: src/content/de/hhg/kategorien.json
+src/content/de/pastebin/kategorien.htmlheader src/content/de/pastebin/kategorien.htmlfooter: src/content/de/pastebin/kategorien.json
+src/content/de/podcast/kategorien.htmlheader src/content/de/podcast/kategorien.htmlfooter: src/content/de/podcast/kategorien.json
+src/content/de/screencast/kategorien.htmlheader src/content/de/screencast/kategorien.htmlfooter: src/content/de/screencast/kategorien.json
+src/content/en/blog/categories.htmlheader src/content/en/blog/categories.htmlfooter: src/content/en/blog/categories.json
+src/content/en/pastebin/categories.htmlheader src/content/en/pastebin/categories.htmlfooter: src/content/en/pastebin/categories.json
+src/content/en/podcast/categories.htmlheader src/content/en/podcast/categories.htmlfooter: src/content/en/podcast/categories.json
+src/content/en/screencast/categories.htmlheader src/content/en/screencast/categories.htmlfooter: src/content/en/screencast/categories.json
+src/content/en/slide/categories.htmlheader src/content/en/slide/categories.htmlfooter: src/content/en/slide/categories.json
+src/content/en/til/categories.htmlheader src/content/en/til/categories.htmlfooter: src/content/en/til/categories.json
+src/content/eo/blog/kategorioj.htmlheader src/content/eo/blog/kategorioj.htmlfooter: src/content/eo/blog/kategorioj.json
+src/content/eo/hml/kategorioj.htmlheader src/content/eo/hml/kategorioj.htmlfooter: src/content/eo/hml/kategorioj.json
+src/content/eo/lumbildoj/kategorioj.htmlheader src/content/eo/lumbildoj/kategorioj.htmlfooter: src/content/eo/lumbildoj/kategorioj.json
+src/content/eo/pastebin/kategorioj.htmlheader src/content/eo/pastebin/kategorioj.htmlfooter: src/content/eo/pastebin/kategorioj.json
+src/content/eo/podkasto/kategorioj.htmlheader src/content/eo/podkasto/kategorioj.htmlfooter: src/content/eo/podkasto/kategorioj.json
+src/content/eo/screencast/kategorioj.htmlheader src/content/eo/screencast/kategorioj.htmlfooter: src/content/eo/screencast/kategorioj.json
+src/content/es/blog/categorias.htmlheader src/content/es/blog/categorias.htmlfooter: src/content/es/blog/categorias.json
+src/content/es/diapositivas/categorias.htmlheader src/content/es/diapositivas/categorias.htmlfooter: src/content/es/diapositivas/categorias.json
+src/content/es/haa/categorias.htmlheader src/content/es/haa/categorias.htmlfooter: src/content/es/haa/categorias.json
+src/content/es/pastebin/categorias.htmlheader src/content/es/pastebin/categorias.htmlfooter: src/content/es/pastebin/categorias.json
+src/content/es/podcast/categorias.htmlheader src/content/es/podcast/categorias.htmlfooter: src/content/es/podcast/categorias.json
+src/content/es/screencast/categorias.htmlheader src/content/es/screencast/categorias.htmlfooter: src/content/es/screencast/categorias.json
+src/content/fr/aja/categories.htmlheader src/content/fr/aja/categories.htmlfooter: src/content/fr/aja/categories.json
+src/content/fr/blog/categories.htmlheader src/content/fr/blog/categories.htmlfooter: src/content/fr/blog/categories.json
+src/content/fr/diapositives/categories.htmlheader src/content/fr/diapositives/categories.htmlfooter: src/content/fr/diapositives/categories.json
+src/content/fr/pastebin/categories.htmlheader src/content/fr/pastebin/categories.htmlfooter: src/content/fr/pastebin/categories.json
+src/content/fr/podcast/categories.htmlheader src/content/fr/podcast/categories.htmlfooter: src/content/fr/podcast/categories.json
+src/content/fr/screencast/categories.htmlheader src/content/fr/screencast/categories.htmlfooter: src/content/fr/screencast/categories.json
+src/content/pt/blog/categorias.htmlheader src/content/pt/blog/categorias.htmlfooter: src/content/pt/blog/categorias.json
+src/content/pt/eslaides/categorias.htmlheader src/content/pt/eslaides/categorias.htmlfooter: src/content/pt/eslaides/categorias.json
+src/content/pt/hea/categorias.htmlheader src/content/pt/hea/categorias.htmlfooter: src/content/pt/hea/categorias.json
+src/content/pt/pastebin/categorias.htmlheader src/content/pt/pastebin/categorias.htmlfooter: src/content/pt/pastebin/categorias.json
+src/content/pt/podcast/categorias.htmlheader src/content/pt/podcast/categorias.htmlfooter: src/content/pt/podcast/categorias.json
+src/content/pt/screencast/categorias.htmlheader src/content/pt/screencast/categorias.htmlfooter: src/content/pt/screencast/categorias.json
+src/content/de/index.htmlheader src/content/de/index.htmlfooter: src/content/de/index.json
+src/content/en/index.htmlheader src/content/en/index.htmlfooter: src/content/en/index.json
+src/content/eo/index.htmlheader src/content/eo/index.htmlfooter: src/content/eo/index.json
+src/content/es/index.htmlheader src/content/es/index.htmlfooter: src/content/es/index.json
+src/content/fr/index.htmlheader src/content/fr/index.htmlfooter: src/content/fr/index.json
+src/content/pt/index.htmlheader src/content/pt/index.htmlfooter: src/content/pt/index.json
+src/content/de/blog/index.htmllisting: src/content/de/blog/index.json
+src/content/de/folien/index.htmllisting: src/content/de/folien/index.json
+src/content/de/hhg/index.htmllisting: src/content/de/hhg/index.json
+src/content/de/pastebin/index.htmllisting: src/content/de/pastebin/index.json
+src/content/de/podcast/index.htmllisting: src/content/de/podcast/index.json
+src/content/de/screencast/index.htmllisting: src/content/de/screencast/index.json
+src/content/en/blog/index.htmllisting: src/content/en/blog/index.json
+src/content/en/pastebin/index.htmllisting: src/content/en/pastebin/index.json
+src/content/en/podcast/index.htmllisting: src/content/en/podcast/index.json
+src/content/en/screencast/index.htmllisting: src/content/en/screencast/index.json
+src/content/en/slide/index.htmllisting: src/content/en/slide/index.json
+src/content/en/til/index.htmllisting: src/content/en/til/index.json
+src/content/eo/blog/index.htmllisting: src/content/eo/blog/index.json
+src/content/eo/hml/index.htmllisting: src/content/eo/hml/index.json
+src/content/eo/lumbildoj/index.htmllisting: src/content/eo/lumbildoj/index.json
+src/content/eo/pastebin/index.htmllisting: src/content/eo/pastebin/index.json
+src/content/eo/podkasto/index.htmllisting: src/content/eo/podkasto/index.json
+src/content/eo/screencast/index.htmllisting: src/content/eo/screencast/index.json
+src/content/es/blog/index.htmllisting: src/content/es/blog/index.json
+src/content/es/diapositivas/index.htmllisting: src/content/es/diapositivas/index.json
+src/content/es/haa/index.htmllisting: src/content/es/haa/index.json
+src/content/es/pastebin/index.htmllisting: src/content/es/pastebin/index.json
+src/content/es/podcast/index.htmllisting: src/content/es/podcast/index.json
+src/content/es/screencast/index.htmllisting: src/content/es/screencast/index.json
+src/content/fr/aja/index.htmllisting: src/content/fr/aja/index.json
+src/content/fr/blog/index.htmllisting: src/content/fr/blog/index.json
+src/content/fr/diapositives/index.htmllisting: src/content/fr/diapositives/index.json
+src/content/fr/pastebin/index.htmllisting: src/content/fr/pastebin/index.json
+src/content/fr/podcast/index.htmllisting: src/content/fr/podcast/index.json
+src/content/fr/screencast/index.htmllisting: src/content/fr/screencast/index.json
+src/content/pt/blog/index.htmllisting: src/content/pt/blog/index.json
+src/content/pt/eslaides/index.htmllisting: src/content/pt/eslaides/index.json
+src/content/pt/hea/index.htmllisting: src/content/pt/hea/index.json
+src/content/pt/pastebin/index.htmllisting: src/content/pt/pastebin/index.json
+src/content/pt/podcast/index.htmllisting: src/content/pt/podcast/index.json
+src/content/pt/screencast/index.htmllisting: src/content/pt/screencast/index.json
+src/content/de/blog/kategorien.htmllisting: src/content/de/blog/kategorien.json
+src/content/de/folien/kategorien.htmllisting: src/content/de/folien/kategorien.json
+src/content/de/hhg/kategorien.htmllisting: src/content/de/hhg/kategorien.json
+src/content/de/pastebin/kategorien.htmllisting: src/content/de/pastebin/kategorien.json
+src/content/de/podcast/kategorien.htmllisting: src/content/de/podcast/kategorien.json
+src/content/de/screencast/kategorien.htmllisting: src/content/de/screencast/kategorien.json
+src/content/en/blog/categories.htmllisting: src/content/en/blog/categories.json
+src/content/en/pastebin/categories.htmllisting: src/content/en/pastebin/categories.json
+src/content/en/podcast/categories.htmllisting: src/content/en/podcast/categories.json
+src/content/en/screencast/categories.htmllisting: src/content/en/screencast/categories.json
+src/content/en/slide/categories.htmllisting: src/content/en/slide/categories.json
+src/content/en/til/categories.htmllisting: src/content/en/til/categories.json
+src/content/eo/blog/kategorioj.htmllisting: src/content/eo/blog/kategorioj.json
+src/content/eo/hml/kategorioj.htmllisting: src/content/eo/hml/kategorioj.json
+src/content/eo/lumbildoj/kategorioj.htmllisting: src/content/eo/lumbildoj/kategorioj.json
+src/content/eo/pastebin/kategorioj.htmllisting: src/content/eo/pastebin/kategorioj.json
+src/content/eo/podkasto/kategorioj.htmllisting: src/content/eo/podkasto/kategorioj.json
+src/content/eo/screencast/kategorioj.htmllisting: src/content/eo/screencast/kategorioj.json
+src/content/es/blog/categorias.htmllisting: src/content/es/blog/categorias.json
+src/content/es/diapositivas/categorias.htmllisting: src/content/es/diapositivas/categorias.json
+src/content/es/haa/categorias.htmllisting: src/content/es/haa/categorias.json
+src/content/es/pastebin/categorias.htmllisting: src/content/es/pastebin/categorias.json
+src/content/es/podcast/categorias.htmllisting: src/content/es/podcast/categorias.json
+src/content/es/screencast/categorias.htmllisting: src/content/es/screencast/categorias.json
+src/content/fr/aja/categories.htmllisting: src/content/fr/aja/categories.json
+src/content/fr/blog/categories.htmllisting: src/content/fr/blog/categories.json
+src/content/fr/diapositives/categories.htmllisting: src/content/fr/diapositives/categories.json
+src/content/fr/pastebin/categories.htmllisting: src/content/fr/pastebin/categories.json
+src/content/fr/podcast/categories.htmllisting: src/content/fr/podcast/categories.json
+src/content/fr/screencast/categories.htmllisting: src/content/fr/screencast/categories.json
+src/content/pt/blog/categorias.htmllisting: src/content/pt/blog/categorias.json
+src/content/pt/eslaides/categorias.htmllisting: src/content/pt/eslaides/categorias.json
+src/content/pt/hea/categorias.htmllisting: src/content/pt/hea/categorias.json
+src/content/pt/pastebin/categorias.htmllisting: src/content/pt/pastebin/categorias.json
+src/content/pt/podcast/categorias.htmllisting: src/content/pt/podcast/categorias.json
+src/content/pt/screencast/categorias.htmllisting: src/content/pt/screencast/categorias.json
+src/content/de/index.htmllisting: src/content/de/index.json
+src/content/en/index.htmllisting: src/content/en/index.json
+src/content/eo/index.htmllisting: src/content/eo/index.json
+src/content/es/index.htmllisting: src/content/es/index.json
+src/content/fr/index.htmllisting: src/content/fr/index.json
+src/content/pt/index.htmllisting: src/content/pt/index.json
+src/content/de/blog/index.html: src/content/de/blog/index.htmlheader src/content/de/blog/index.htmlfooter
+src/content/de/folien/index.html: src/content/de/folien/index.htmlheader src/content/de/folien/index.htmlfooter
+src/content/de/hhg/index.html: src/content/de/hhg/index.htmlheader src/content/de/hhg/index.htmlfooter
+src/content/de/pastebin/index.html: src/content/de/pastebin/index.htmlheader src/content/de/pastebin/index.htmlfooter
+src/content/de/podcast/index.html: src/content/de/podcast/index.htmlheader src/content/de/podcast/index.htmlfooter
+src/content/de/screencast/index.html: src/content/de/screencast/index.htmlheader src/content/de/screencast/index.htmlfooter
src/content/en/blog/index.html: src/content/en/blog/index.htmlheader src/content/en/blog/index.htmlfooter
src/content/en/pastebin/index.html: src/content/en/pastebin/index.htmlheader src/content/en/pastebin/index.htmlfooter
src/content/en/podcast/index.html: src/content/en/podcast/index.htmlheader src/content/en/podcast/index.htmlfooter
src/content/en/screencast/index.html: src/content/en/screencast/index.htmlheader src/content/en/screencast/index.htmlfooter
+src/content/en/slide/index.html: src/content/en/slide/index.htmlheader src/content/en/slide/index.htmlfooter
src/content/en/til/index.html: src/content/en/til/index.htmlheader src/content/en/til/index.htmlfooter
+src/content/eo/blog/index.html: src/content/eo/blog/index.htmlheader src/content/eo/blog/index.htmlfooter
+src/content/eo/hml/index.html: src/content/eo/hml/index.htmlheader src/content/eo/hml/index.htmlfooter
+src/content/eo/lumbildoj/index.html: src/content/eo/lumbildoj/index.htmlheader src/content/eo/lumbildoj/index.htmlfooter
+src/content/eo/pastebin/index.html: src/content/eo/pastebin/index.htmlheader src/content/eo/pastebin/index.htmlfooter
+src/content/eo/podkasto/index.html: src/content/eo/podkasto/index.htmlheader src/content/eo/podkasto/index.htmlfooter
+src/content/eo/screencast/index.html: src/content/eo/screencast/index.htmlheader src/content/eo/screencast/index.htmlfooter
+src/content/es/blog/index.html: src/content/es/blog/index.htmlheader src/content/es/blog/index.htmlfooter
+src/content/es/diapositivas/index.html: src/content/es/diapositivas/index.htmlheader src/content/es/diapositivas/index.htmlfooter
+src/content/es/haa/index.html: src/content/es/haa/index.htmlheader src/content/es/haa/index.htmlfooter
+src/content/es/pastebin/index.html: src/content/es/pastebin/index.htmlheader src/content/es/pastebin/index.htmlfooter
+src/content/es/podcast/index.html: src/content/es/podcast/index.htmlheader src/content/es/podcast/index.htmlfooter
+src/content/es/screencast/index.html: src/content/es/screencast/index.htmlheader src/content/es/screencast/index.htmlfooter
+src/content/fr/aja/index.html: src/content/fr/aja/index.htmlheader src/content/fr/aja/index.htmlfooter
+src/content/fr/blog/index.html: src/content/fr/blog/index.htmlheader src/content/fr/blog/index.htmlfooter
+src/content/fr/diapositives/index.html: src/content/fr/diapositives/index.htmlheader src/content/fr/diapositives/index.htmlfooter
+src/content/fr/pastebin/index.html: src/content/fr/pastebin/index.htmlheader src/content/fr/pastebin/index.htmlfooter
+src/content/fr/podcast/index.html: src/content/fr/podcast/index.htmlheader src/content/fr/podcast/index.htmlfooter
+src/content/fr/screencast/index.html: src/content/fr/screencast/index.htmlheader src/content/fr/screencast/index.htmlfooter
+src/content/pt/blog/index.html: src/content/pt/blog/index.htmlheader src/content/pt/blog/index.htmlfooter
+src/content/pt/eslaides/index.html: src/content/pt/eslaides/index.htmlheader src/content/pt/eslaides/index.htmlfooter
src/content/pt/hea/index.html: src/content/pt/hea/index.htmlheader src/content/pt/hea/index.htmlfooter
+src/content/pt/pastebin/index.html: src/content/pt/pastebin/index.htmlheader src/content/pt/pastebin/index.htmlfooter
+src/content/pt/podcast/index.html: src/content/pt/podcast/index.htmlheader src/content/pt/podcast/index.htmlfooter
+src/content/pt/screencast/index.html: src/content/pt/screencast/index.htmlheader src/content/pt/screencast/index.htmlfooter
+src/content/de/blog/kategorien.html: src/content/de/blog/kategorien.htmlheader src/content/de/blog/kategorien.htmlfooter
+src/content/de/folien/kategorien.html: src/content/de/folien/kategorien.htmlheader src/content/de/folien/kategorien.htmlfooter
+src/content/de/hhg/kategorien.html: src/content/de/hhg/kategorien.htmlheader src/content/de/hhg/kategorien.htmlfooter
+src/content/de/pastebin/kategorien.html: src/content/de/pastebin/kategorien.htmlheader src/content/de/pastebin/kategorien.htmlfooter
+src/content/de/podcast/kategorien.html: src/content/de/podcast/kategorien.htmlheader src/content/de/podcast/kategorien.htmlfooter
+src/content/de/screencast/kategorien.html: src/content/de/screencast/kategorien.htmlheader src/content/de/screencast/kategorien.htmlfooter
src/content/en/blog/categories.html: src/content/en/blog/categories.htmlheader src/content/en/blog/categories.htmlfooter
src/content/en/pastebin/categories.html: src/content/en/pastebin/categories.htmlheader src/content/en/pastebin/categories.htmlfooter
src/content/en/podcast/categories.html: src/content/en/podcast/categories.htmlheader src/content/en/podcast/categories.htmlfooter
src/content/en/screencast/categories.html: src/content/en/screencast/categories.htmlheader src/content/en/screencast/categories.htmlfooter
+src/content/en/slide/categories.html: src/content/en/slide/categories.htmlheader src/content/en/slide/categories.htmlfooter
src/content/en/til/categories.html: src/content/en/til/categories.htmlheader src/content/en/til/categories.htmlfooter
+src/content/eo/blog/kategorioj.html: src/content/eo/blog/kategorioj.htmlheader src/content/eo/blog/kategorioj.htmlfooter
+src/content/eo/hml/kategorioj.html: src/content/eo/hml/kategorioj.htmlheader src/content/eo/hml/kategorioj.htmlfooter
+src/content/eo/lumbildoj/kategorioj.html: src/content/eo/lumbildoj/kategorioj.htmlheader src/content/eo/lumbildoj/kategorioj.htmlfooter
+src/content/eo/pastebin/kategorioj.html: src/content/eo/pastebin/kategorioj.htmlheader src/content/eo/pastebin/kategorioj.htmlfooter
+src/content/eo/podkasto/kategorioj.html: src/content/eo/podkasto/kategorioj.htmlheader src/content/eo/podkasto/kategorioj.htmlfooter
+src/content/eo/screencast/kategorioj.html: src/content/eo/screencast/kategorioj.htmlheader src/content/eo/screencast/kategorioj.htmlfooter
+src/content/es/blog/categorias.html: src/content/es/blog/categorias.htmlheader src/content/es/blog/categorias.htmlfooter
+src/content/es/diapositivas/categorias.html: src/content/es/diapositivas/categorias.htmlheader src/content/es/diapositivas/categorias.htmlfooter
+src/content/es/haa/categorias.html: src/content/es/haa/categorias.htmlheader src/content/es/haa/categorias.htmlfooter
+src/content/es/pastebin/categorias.html: src/content/es/pastebin/categorias.htmlheader src/content/es/pastebin/categorias.htmlfooter
+src/content/es/podcast/categorias.html: src/content/es/podcast/categorias.htmlheader src/content/es/podcast/categorias.htmlfooter
+src/content/es/screencast/categorias.html: src/content/es/screencast/categorias.htmlheader src/content/es/screencast/categorias.htmlfooter
+src/content/fr/aja/categories.html: src/content/fr/aja/categories.htmlheader src/content/fr/aja/categories.htmlfooter
+src/content/fr/blog/categories.html: src/content/fr/blog/categories.htmlheader src/content/fr/blog/categories.htmlfooter
+src/content/fr/diapositives/categories.html: src/content/fr/diapositives/categories.htmlheader src/content/fr/diapositives/categories.htmlfooter
+src/content/fr/pastebin/categories.html: src/content/fr/pastebin/categories.htmlheader src/content/fr/pastebin/categories.htmlfooter
+src/content/fr/podcast/categories.html: src/content/fr/podcast/categories.htmlheader src/content/fr/podcast/categories.htmlfooter
+src/content/fr/screencast/categories.html: src/content/fr/screencast/categories.htmlheader src/content/fr/screencast/categories.htmlfooter
+src/content/pt/blog/categorias.html: src/content/pt/blog/categorias.htmlheader src/content/pt/blog/categorias.htmlfooter
+src/content/pt/eslaides/categorias.html: src/content/pt/eslaides/categorias.htmlheader src/content/pt/eslaides/categorias.htmlfooter
src/content/pt/hea/categorias.html: src/content/pt/hea/categorias.htmlheader src/content/pt/hea/categorias.htmlfooter
+src/content/pt/pastebin/categorias.html: src/content/pt/pastebin/categorias.htmlheader src/content/pt/pastebin/categorias.htmlfooter
+src/content/pt/podcast/categorias.html: src/content/pt/podcast/categorias.htmlheader src/content/pt/podcast/categorias.htmlfooter
+src/content/pt/screencast/categorias.html: src/content/pt/screencast/categorias.htmlheader src/content/pt/screencast/categorias.htmlfooter
+src/content/de/index.html: src/content/de/index.htmlheader src/content/de/index.htmlfooter
+src/content/en/index.html: src/content/en/index.htmlheader src/content/en/index.htmlfooter
+src/content/eo/index.html: src/content/eo/index.htmlheader src/content/eo/index.htmlfooter
+src/content/es/index.html: src/content/es/index.htmlheader src/content/es/index.htmlfooter
+src/content/fr/index.html: src/content/fr/index.htmlheader src/content/fr/index.htmlfooter
+src/content/pt/index.html: src/content/pt/index.htmlheader src/content/pt/index.htmlfooter
+src/content/de/blog/index.html: src/content/de/blog/index.htmllisting src/content/de/blog/index.htmlbody
+src/content/de/folien/index.html: src/content/de/folien/index.htmllisting src/content/de/folien/index.htmlbody
+src/content/de/hhg/index.html: src/content/de/hhg/index.htmllisting src/content/de/hhg/index.htmlbody
+src/content/de/pastebin/index.html: src/content/de/pastebin/index.htmllisting src/content/de/pastebin/index.htmlbody
+src/content/de/podcast/index.html: src/content/de/podcast/index.htmllisting src/content/de/podcast/index.htmlbody
+src/content/de/screencast/index.html: src/content/de/screencast/index.htmllisting src/content/de/screencast/index.htmlbody
src/content/en/blog/index.html: src/content/en/blog/index.htmllisting src/content/en/blog/index.htmlbody
src/content/en/pastebin/index.html: src/content/en/pastebin/index.htmllisting src/content/en/pastebin/index.htmlbody
src/content/en/podcast/index.html: src/content/en/podcast/index.htmllisting src/content/en/podcast/index.htmlbody
src/content/en/screencast/index.html: src/content/en/screencast/index.htmllisting src/content/en/screencast/index.htmlbody
+src/content/en/slide/index.html: src/content/en/slide/index.htmllisting src/content/en/slide/index.htmlbody
src/content/en/til/index.html: src/content/en/til/index.htmllisting src/content/en/til/index.htmlbody
+src/content/eo/blog/index.html: src/content/eo/blog/index.htmllisting src/content/eo/blog/index.htmlbody
+src/content/eo/hml/index.html: src/content/eo/hml/index.htmllisting src/content/eo/hml/index.htmlbody
+src/content/eo/lumbildoj/index.html: src/content/eo/lumbildoj/index.htmllisting src/content/eo/lumbildoj/index.htmlbody
+src/content/eo/pastebin/index.html: src/content/eo/pastebin/index.htmllisting src/content/eo/pastebin/index.htmlbody
+src/content/eo/podkasto/index.html: src/content/eo/podkasto/index.htmllisting src/content/eo/podkasto/index.htmlbody
+src/content/eo/screencast/index.html: src/content/eo/screencast/index.htmllisting src/content/eo/screencast/index.htmlbody
+src/content/es/blog/index.html: src/content/es/blog/index.htmllisting src/content/es/blog/index.htmlbody
+src/content/es/diapositivas/index.html: src/content/es/diapositivas/index.htmllisting src/content/es/diapositivas/index.htmlbody
+src/content/es/haa/index.html: src/content/es/haa/index.htmllisting src/content/es/haa/index.htmlbody
+src/content/es/pastebin/index.html: src/content/es/pastebin/index.htmllisting src/content/es/pastebin/index.htmlbody
+src/content/es/podcast/index.html: src/content/es/podcast/index.htmllisting src/content/es/podcast/index.htmlbody
+src/content/es/screencast/index.html: src/content/es/screencast/index.htmllisting src/content/es/screencast/index.htmlbody
+src/content/fr/aja/index.html: src/content/fr/aja/index.htmllisting src/content/fr/aja/index.htmlbody
+src/content/fr/blog/index.html: src/content/fr/blog/index.htmllisting src/content/fr/blog/index.htmlbody
+src/content/fr/diapositives/index.html: src/content/fr/diapositives/index.htmllisting src/content/fr/diapositives/index.htmlbody
+src/content/fr/pastebin/index.html: src/content/fr/pastebin/index.htmllisting src/content/fr/pastebin/index.htmlbody
+src/content/fr/podcast/index.html: src/content/fr/podcast/index.htmllisting src/content/fr/podcast/index.htmlbody
+src/content/fr/screencast/index.html: src/content/fr/screencast/index.htmllisting src/content/fr/screencast/index.htmlbody
+src/content/pt/blog/index.html: src/content/pt/blog/index.htmllisting src/content/pt/blog/index.htmlbody
+src/content/pt/eslaides/index.html: src/content/pt/eslaides/index.htmllisting src/content/pt/eslaides/index.htmlbody
src/content/pt/hea/index.html: src/content/pt/hea/index.htmllisting src/content/pt/hea/index.htmlbody
+src/content/pt/pastebin/index.html: src/content/pt/pastebin/index.htmllisting src/content/pt/pastebin/index.htmlbody
+src/content/pt/podcast/index.html: src/content/pt/podcast/index.htmllisting src/content/pt/podcast/index.htmlbody
+src/content/pt/screencast/index.html: src/content/pt/screencast/index.htmllisting src/content/pt/screencast/index.htmlbody
+src/content/de/blog/kategorien.html: src/content/de/blog/kategorien.htmllisting src/content/de/blog/kategorien.htmlbody
+src/content/de/folien/kategorien.html: src/content/de/folien/kategorien.htmllisting src/content/de/folien/kategorien.htmlbody
+src/content/de/hhg/kategorien.html: src/content/de/hhg/kategorien.htmllisting src/content/de/hhg/kategorien.htmlbody
+src/content/de/pastebin/kategorien.html: src/content/de/pastebin/kategorien.htmllisting src/content/de/pastebin/kategorien.htmlbody
+src/content/de/podcast/kategorien.html: src/content/de/podcast/kategorien.htmllisting src/content/de/podcast/kategorien.htmlbody
+src/content/de/screencast/kategorien.html: src/content/de/screencast/kategorien.htmllisting src/content/de/screencast/kategorien.htmlbody
src/content/en/blog/categories.html: src/content/en/blog/categories.htmllisting src/content/en/blog/categories.htmlbody
src/content/en/pastebin/categories.html: src/content/en/pastebin/categories.htmllisting src/content/en/pastebin/categories.htmlbody
src/content/en/podcast/categories.html: src/content/en/podcast/categories.htmllisting src/content/en/podcast/categories.htmlbody
src/content/en/screencast/categories.html: src/content/en/screencast/categories.htmllisting src/content/en/screencast/categories.htmlbody
+src/content/en/slide/categories.html: src/content/en/slide/categories.htmllisting src/content/en/slide/categories.htmlbody
src/content/en/til/categories.html: src/content/en/til/categories.htmllisting src/content/en/til/categories.htmlbody
+src/content/eo/blog/kategorioj.html: src/content/eo/blog/kategorioj.htmllisting src/content/eo/blog/kategorioj.htmlbody
+src/content/eo/hml/kategorioj.html: src/content/eo/hml/kategorioj.htmllisting src/content/eo/hml/kategorioj.htmlbody
+src/content/eo/lumbildoj/kategorioj.html: src/content/eo/lumbildoj/kategorioj.htmllisting src/content/eo/lumbildoj/kategorioj.htmlbody
+src/content/eo/pastebin/kategorioj.html: src/content/eo/pastebin/kategorioj.htmllisting src/content/eo/pastebin/kategorioj.htmlbody
+src/content/eo/podkasto/kategorioj.html: src/content/eo/podkasto/kategorioj.htmllisting src/content/eo/podkasto/kategorioj.htmlbody
+src/content/eo/screencast/kategorioj.html: src/content/eo/screencast/kategorioj.htmllisting src/content/eo/screencast/kategorioj.htmlbody
+src/content/es/blog/categorias.html: src/content/es/blog/categorias.htmllisting src/content/es/blog/categorias.htmlbody
+src/content/es/diapositivas/categorias.html: src/content/es/diapositivas/categorias.htmllisting src/content/es/diapositivas/categorias.htmlbody
+src/content/es/haa/categorias.html: src/content/es/haa/categorias.htmllisting src/content/es/haa/categorias.htmlbody
+src/content/es/pastebin/categorias.html: src/content/es/pastebin/categorias.htmllisting src/content/es/pastebin/categorias.htmlbody
+src/content/es/podcast/categorias.html: src/content/es/podcast/categorias.htmllisting src/content/es/podcast/categorias.htmlbody
+src/content/es/screencast/categorias.html: src/content/es/screencast/categorias.htmllisting src/content/es/screencast/categorias.htmlbody
+src/content/fr/aja/categories.html: src/content/fr/aja/categories.htmllisting src/content/fr/aja/categories.htmlbody
+src/content/fr/blog/categories.html: src/content/fr/blog/categories.htmllisting src/content/fr/blog/categories.htmlbody
+src/content/fr/diapositives/categories.html: src/content/fr/diapositives/categories.htmllisting src/content/fr/diapositives/categories.htmlbody
+src/content/fr/pastebin/categories.html: src/content/fr/pastebin/categories.htmllisting src/content/fr/pastebin/categories.htmlbody
+src/content/fr/podcast/categories.html: src/content/fr/podcast/categories.htmllisting src/content/fr/podcast/categories.htmlbody
+src/content/fr/screencast/categories.html: src/content/fr/screencast/categories.htmllisting src/content/fr/screencast/categories.htmlbody
+src/content/pt/blog/categorias.html: src/content/pt/blog/categorias.htmllisting src/content/pt/blog/categorias.htmlbody
+src/content/pt/eslaides/categorias.html: src/content/pt/eslaides/categorias.htmllisting src/content/pt/eslaides/categorias.htmlbody
src/content/pt/hea/categorias.html: src/content/pt/hea/categorias.htmllisting src/content/pt/hea/categorias.htmlbody
+src/content/pt/pastebin/categorias.html: src/content/pt/pastebin/categorias.htmllisting src/content/pt/pastebin/categorias.htmlbody
+src/content/pt/podcast/categorias.html: src/content/pt/podcast/categorias.htmllisting src/content/pt/podcast/categorias.htmlbody
+src/content/pt/screencast/categorias.html: src/content/pt/screencast/categorias.htmllisting src/content/pt/screencast/categorias.htmlbody
+src/content/de/index.html: src/content/de/index.htmllisting src/content/de/index.htmlbody
+src/content/en/index.html: src/content/en/index.htmllisting src/content/en/index.htmlbody
+src/content/eo/index.html: src/content/eo/index.htmllisting src/content/eo/index.htmlbody
+src/content/es/index.html: src/content/es/index.htmllisting src/content/es/index.htmlbody
+src/content/fr/index.html: src/content/fr/index.htmllisting src/content/fr/index.htmlbody
+src/content/pt/index.html: src/content/pt/index.htmllisting src/content/pt/index.htmlbody
src/content/en/podcast/2020/12/19/test-entry.flac.torrent: src/content/en/podcast/2020/12/19/test-entry.flac
src/content/en/podcast/2020/12/19/test-entry.ogg.torrent: src/content/en/podcast/2020/12/19/test-entry.ogg
src/content/en/screencast/2021/02/07/autoqemu.webm.torrent: src/content/en/screencast/2021/02/07/autoqemu.webm
-src/content/en/slide/2020/10/19/feature-flags.ps: src/content/en/slide/2020/10/19/feature-flags.adoc
-src/content/en/slide/2020/11/14/local-first-hype.ps: src/content/en/slide/2020/11/14/local-first-hype.adoc
+src/content/en/slide/2020/10/19/feature-flags.ps: src/content/en/slide/2020/10/19/feature-flags.eslaides
+src/content/en/slide/2020/11/14/local-first-hype.ps: src/content/en/slide/2020/11/14/local-first-hype.eslaides
src/content/en/slide/2020/10/19/feature-flags.pdf: src/content/en/slide/2020/10/19/feature-flags.ps
src/content/en/slide/2020/11/14/local-first-hype.pdf: src/content/en/slide/2020/11/14/local-first-hype.ps
+articles.de.blog.adoc = \
+
+articles.de.blog.sortdata = $(articles.de.blog.adoc:.adoc=.sortdata)
+articles.de.blog.indexentry = $(articles.de.blog.adoc:.adoc=.indexentry)
+articles.de.blog.feedentry = $(articles.de.blog.adoc:.adoc=.feedentry)
+articles.de.blog.categorydata = $(articles.de.blog.adoc:.adoc=.categorydata)
+articles.de.blog.homeentry = $(articles.de.blog.adoc:.adoc=.homeentry)
+src/content/de/blog/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.de.blog.sortdata) > $@
+
+src/content/de/blog/feed.xml: src/content/de/blog/sortdata.txt
+src/content/de/blog/feed.xml: $(articles.de.blog.sortdata)
+src/content/de/blog/index.htmllisting: src/content/de/blog/sortdata.txt
+src/content/de/blog/index.htmllisting: $(articles.de.blog.sortdata)
+src/content/de/blog/kategorien.htmllisting: src/content/de/blog/sortdata.txt
+src/content/de/blog/kategorien.htmllisting: $(articles.de.blog.sortdata)
+src/content/de/blog/kategorien.txt: src/content/de/blog/sortdata.txt
+src/content/de/blog/kategorien.txt: $(articles.de.blog.sortdata)
+src/content/de/blog/index.htmllisting src/content/de/blog/kategorien.htmllisting: $(articles.de.blog.indexentry)
+src/content/de/blog/kategorien.txt: $(articles.de.blog.categorydata)
+src/content/de/blog/kategorien.txt src/content/de/blog/feed.xml: $(articles.de.blog.feedentry)
+src/content/de/blog/kategorien.htmllisting src/content/de/blog/kategorien.xml: src/content/de/blog/kategorien.txt
+
+
+articles.de.folien.adoc = \
+
+articles.de.folien.sortdata = $(articles.de.folien.adoc:.adoc=.sortdata)
+articles.de.folien.indexentry = $(articles.de.folien.adoc:.adoc=.indexentry)
+articles.de.folien.feedentry = $(articles.de.folien.adoc:.adoc=.feedentry)
+articles.de.folien.categorydata = $(articles.de.folien.adoc:.adoc=.categorydata)
+articles.de.folien.homeentry = $(articles.de.folien.adoc:.adoc=.homeentry)
+src/content/de/folien/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.de.folien.sortdata) > $@
+
+src/content/de/folien/feed.xml: src/content/de/folien/sortdata.txt
+src/content/de/folien/feed.xml: $(articles.de.folien.sortdata)
+src/content/de/folien/index.htmllisting: src/content/de/folien/sortdata.txt
+src/content/de/folien/index.htmllisting: $(articles.de.folien.sortdata)
+src/content/de/folien/kategorien.htmllisting: src/content/de/folien/sortdata.txt
+src/content/de/folien/kategorien.htmllisting: $(articles.de.folien.sortdata)
+src/content/de/folien/kategorien.txt: src/content/de/folien/sortdata.txt
+src/content/de/folien/kategorien.txt: $(articles.de.folien.sortdata)
+src/content/de/folien/index.htmllisting src/content/de/folien/kategorien.htmllisting: $(articles.de.folien.indexentry)
+src/content/de/folien/kategorien.txt: $(articles.de.folien.categorydata)
+src/content/de/folien/kategorien.txt src/content/de/folien/feed.xml: $(articles.de.folien.feedentry)
+src/content/de/folien/kategorien.htmllisting src/content/de/folien/kategorien.xml: src/content/de/folien/kategorien.txt
+
+
+articles.de.hhg.adoc = \
+
+articles.de.hhg.sortdata = $(articles.de.hhg.adoc:.adoc=.sortdata)
+articles.de.hhg.indexentry = $(articles.de.hhg.adoc:.adoc=.indexentry)
+articles.de.hhg.feedentry = $(articles.de.hhg.adoc:.adoc=.feedentry)
+articles.de.hhg.categorydata = $(articles.de.hhg.adoc:.adoc=.categorydata)
+articles.de.hhg.homeentry = $(articles.de.hhg.adoc:.adoc=.homeentry)
+src/content/de/hhg/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.de.hhg.sortdata) > $@
+
+src/content/de/hhg/feed.xml: src/content/de/hhg/sortdata.txt
+src/content/de/hhg/feed.xml: $(articles.de.hhg.sortdata)
+src/content/de/hhg/index.htmllisting: src/content/de/hhg/sortdata.txt
+src/content/de/hhg/index.htmllisting: $(articles.de.hhg.sortdata)
+src/content/de/hhg/kategorien.htmllisting: src/content/de/hhg/sortdata.txt
+src/content/de/hhg/kategorien.htmllisting: $(articles.de.hhg.sortdata)
+src/content/de/hhg/kategorien.txt: src/content/de/hhg/sortdata.txt
+src/content/de/hhg/kategorien.txt: $(articles.de.hhg.sortdata)
+src/content/de/hhg/index.htmllisting src/content/de/hhg/kategorien.htmllisting: $(articles.de.hhg.indexentry)
+src/content/de/hhg/kategorien.txt: $(articles.de.hhg.categorydata)
+src/content/de/hhg/kategorien.txt src/content/de/hhg/feed.xml: $(articles.de.hhg.feedentry)
+src/content/de/hhg/kategorien.htmllisting src/content/de/hhg/kategorien.xml: src/content/de/hhg/kategorien.txt
+
+
+articles.de.pastebin.adoc = \
+
+articles.de.pastebin.sortdata = $(articles.de.pastebin.adoc:.adoc=.sortdata)
+articles.de.pastebin.indexentry = $(articles.de.pastebin.adoc:.adoc=.indexentry)
+articles.de.pastebin.feedentry = $(articles.de.pastebin.adoc:.adoc=.feedentry)
+articles.de.pastebin.categorydata = $(articles.de.pastebin.adoc:.adoc=.categorydata)
+articles.de.pastebin.homeentry = $(articles.de.pastebin.adoc:.adoc=.homeentry)
+src/content/de/pastebin/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.de.pastebin.sortdata) > $@
+
+src/content/de/pastebin/feed.xml: src/content/de/pastebin/sortdata.txt
+src/content/de/pastebin/feed.xml: $(articles.de.pastebin.sortdata)
+src/content/de/pastebin/index.htmllisting: src/content/de/pastebin/sortdata.txt
+src/content/de/pastebin/index.htmllisting: $(articles.de.pastebin.sortdata)
+src/content/de/pastebin/kategorien.htmllisting: src/content/de/pastebin/sortdata.txt
+src/content/de/pastebin/kategorien.htmllisting: $(articles.de.pastebin.sortdata)
+src/content/de/pastebin/kategorien.txt: src/content/de/pastebin/sortdata.txt
+src/content/de/pastebin/kategorien.txt: $(articles.de.pastebin.sortdata)
+src/content/de/pastebin/index.htmllisting src/content/de/pastebin/kategorien.htmllisting: $(articles.de.pastebin.indexentry)
+src/content/de/pastebin/kategorien.txt: $(articles.de.pastebin.categorydata)
+src/content/de/pastebin/kategorien.txt src/content/de/pastebin/feed.xml: $(articles.de.pastebin.feedentry)
+src/content/de/pastebin/kategorien.htmllisting src/content/de/pastebin/kategorien.xml: src/content/de/pastebin/kategorien.txt
+
+
+articles.de.podcast.adoc = \
+
+articles.de.podcast.sortdata = $(articles.de.podcast.adoc:.adoc=.sortdata)
+articles.de.podcast.indexentry = $(articles.de.podcast.adoc:.adoc=.indexentry)
+articles.de.podcast.feedentry = $(articles.de.podcast.adoc:.adoc=.feedentry)
+articles.de.podcast.categorydata = $(articles.de.podcast.adoc:.adoc=.categorydata)
+articles.de.podcast.homeentry = $(articles.de.podcast.adoc:.adoc=.homeentry)
+src/content/de/podcast/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.de.podcast.sortdata) > $@
+
+src/content/de/podcast/feed.xml: src/content/de/podcast/sortdata.txt
+src/content/de/podcast/feed.xml: $(articles.de.podcast.sortdata)
+src/content/de/podcast/index.htmllisting: src/content/de/podcast/sortdata.txt
+src/content/de/podcast/index.htmllisting: $(articles.de.podcast.sortdata)
+src/content/de/podcast/kategorien.htmllisting: src/content/de/podcast/sortdata.txt
+src/content/de/podcast/kategorien.htmllisting: $(articles.de.podcast.sortdata)
+src/content/de/podcast/kategorien.txt: src/content/de/podcast/sortdata.txt
+src/content/de/podcast/kategorien.txt: $(articles.de.podcast.sortdata)
+src/content/de/podcast/index.htmllisting src/content/de/podcast/kategorien.htmllisting: $(articles.de.podcast.indexentry)
+src/content/de/podcast/kategorien.txt: $(articles.de.podcast.categorydata)
+src/content/de/podcast/kategorien.txt src/content/de/podcast/feed.xml: $(articles.de.podcast.feedentry)
+src/content/de/podcast/kategorien.htmllisting src/content/de/podcast/kategorien.xml: src/content/de/podcast/kategorien.txt
+
+
+articles.de.screencast.adoc = \
+
+articles.de.screencast.sortdata = $(articles.de.screencast.adoc:.adoc=.sortdata)
+articles.de.screencast.indexentry = $(articles.de.screencast.adoc:.adoc=.indexentry)
+articles.de.screencast.feedentry = $(articles.de.screencast.adoc:.adoc=.feedentry)
+articles.de.screencast.categorydata = $(articles.de.screencast.adoc:.adoc=.categorydata)
+articles.de.screencast.homeentry = $(articles.de.screencast.adoc:.adoc=.homeentry)
+src/content/de/screencast/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.de.screencast.sortdata) > $@
+
+src/content/de/screencast/feed.xml: src/content/de/screencast/sortdata.txt
+src/content/de/screencast/feed.xml: $(articles.de.screencast.sortdata)
+src/content/de/screencast/index.htmllisting: src/content/de/screencast/sortdata.txt
+src/content/de/screencast/index.htmllisting: $(articles.de.screencast.sortdata)
+src/content/de/screencast/kategorien.htmllisting: src/content/de/screencast/sortdata.txt
+src/content/de/screencast/kategorien.htmllisting: $(articles.de.screencast.sortdata)
+src/content/de/screencast/kategorien.txt: src/content/de/screencast/sortdata.txt
+src/content/de/screencast/kategorien.txt: $(articles.de.screencast.sortdata)
+src/content/de/screencast/index.htmllisting src/content/de/screencast/kategorien.htmllisting: $(articles.de.screencast.indexentry)
+src/content/de/screencast/kategorien.txt: $(articles.de.screencast.categorydata)
+src/content/de/screencast/kategorien.txt src/content/de/screencast/feed.xml: $(articles.de.screencast.feedentry)
+src/content/de/screencast/kategorien.htmllisting src/content/de/screencast/kategorien.xml: src/content/de/screencast/kategorien.txt
+
+
articles.en.blog.adoc = \
src/content/en/blog/2018/07/17/guix-nixos.adoc \
src/content/en/blog/2018/08/01/npm-ci-reproducibility.adoc \
@@ -996,6 +1753,7 @@ articles.en.blog.sortdata = $(articles.en.blog.adoc:.adoc=.sortdata)
articles.en.blog.indexentry = $(articles.en.blog.adoc:.adoc=.indexentry)
articles.en.blog.feedentry = $(articles.en.blog.adoc:.adoc=.feedentry)
articles.en.blog.categorydata = $(articles.en.blog.adoc:.adoc=.categorydata)
+articles.en.blog.homeentry = $(articles.en.blog.adoc:.adoc=.homeentry)
src/content/en/blog/sortdata.txt: deps.mk
printf '%s\n' $(articles.en.blog.sortdata) > $@
@@ -1011,7 +1769,6 @@ src/content/en/blog/index.htmllisting src/content/en/blog/categories.htmllisting
src/content/en/blog/categories.txt: $(articles.en.blog.categorydata)
src/content/en/blog/categories.txt src/content/en/blog/feed.xml: $(articles.en.blog.feedentry)
src/content/en/blog/categories.htmllisting src/content/en/blog/categories.xml: src/content/en/blog/categories.txt
-src/content/en/blog/categories.xml.gz: src/content/en/blog/categories.xml
articles.en.pastebin.adoc = \
@@ -1042,6 +1799,7 @@ articles.en.pastebin.sortdata = $(articles.en.pastebin.adoc:.adoc=.sortdata)
articles.en.pastebin.indexentry = $(articles.en.pastebin.adoc:.adoc=.indexentry)
articles.en.pastebin.feedentry = $(articles.en.pastebin.adoc:.adoc=.feedentry)
articles.en.pastebin.categorydata = $(articles.en.pastebin.adoc:.adoc=.categorydata)
+articles.en.pastebin.homeentry = $(articles.en.pastebin.adoc:.adoc=.homeentry)
src/content/en/pastebin/sortdata.txt: deps.mk
printf '%s\n' $(articles.en.pastebin.sortdata) > $@
@@ -1057,7 +1815,6 @@ src/content/en/pastebin/index.htmllisting src/content/en/pastebin/categories.htm
src/content/en/pastebin/categories.txt: $(articles.en.pastebin.categorydata)
src/content/en/pastebin/categories.txt src/content/en/pastebin/feed.xml: $(articles.en.pastebin.feedentry)
src/content/en/pastebin/categories.htmllisting src/content/en/pastebin/categories.xml: src/content/en/pastebin/categories.txt
-src/content/en/pastebin/categories.xml.gz: src/content/en/pastebin/categories.xml
articles.en.podcast.adoc = \
@@ -1067,6 +1824,7 @@ articles.en.podcast.sortdata = $(articles.en.podcast.adoc:.adoc=.sortdata)
articles.en.podcast.indexentry = $(articles.en.podcast.adoc:.adoc=.indexentry)
articles.en.podcast.feedentry = $(articles.en.podcast.adoc:.adoc=.feedentry)
articles.en.podcast.categorydata = $(articles.en.podcast.adoc:.adoc=.categorydata)
+articles.en.podcast.homeentry = $(articles.en.podcast.adoc:.adoc=.homeentry)
src/content/en/podcast/sortdata.txt: deps.mk
printf '%s\n' $(articles.en.podcast.sortdata) > $@
@@ -1082,7 +1840,6 @@ src/content/en/podcast/index.htmllisting src/content/en/podcast/categories.htmll
src/content/en/podcast/categories.txt: $(articles.en.podcast.categorydata)
src/content/en/podcast/categories.txt src/content/en/podcast/feed.xml: $(articles.en.podcast.feedentry)
src/content/en/podcast/categories.htmllisting src/content/en/podcast/categories.xml: src/content/en/podcast/categories.txt
-src/content/en/podcast/categories.xml.gz: src/content/en/podcast/categories.xml
articles.en.screencast.adoc = \
@@ -1092,6 +1849,7 @@ articles.en.screencast.sortdata = $(articles.en.screencast.adoc:.adoc=.sortd
articles.en.screencast.indexentry = $(articles.en.screencast.adoc:.adoc=.indexentry)
articles.en.screencast.feedentry = $(articles.en.screencast.adoc:.adoc=.feedentry)
articles.en.screencast.categorydata = $(articles.en.screencast.adoc:.adoc=.categorydata)
+articles.en.screencast.homeentry = $(articles.en.screencast.adoc:.adoc=.homeentry)
src/content/en/screencast/sortdata.txt: deps.mk
printf '%s\n' $(articles.en.screencast.sortdata) > $@
@@ -1107,7 +1865,32 @@ src/content/en/screencast/index.htmllisting src/content/en/screencast/categories
src/content/en/screencast/categories.txt: $(articles.en.screencast.categorydata)
src/content/en/screencast/categories.txt src/content/en/screencast/feed.xml: $(articles.en.screencast.feedentry)
src/content/en/screencast/categories.htmllisting src/content/en/screencast/categories.xml: src/content/en/screencast/categories.txt
-src/content/en/screencast/categories.xml.gz: src/content/en/screencast/categories.xml
+
+
+articles.en.slide.adoc = \
+ src/content/en/slide/2020/10/19/feature-flags.adoc \
+ src/content/en/slide/2020/11/14/local-first-hype.adoc \
+
+articles.en.slide.sortdata = $(articles.en.slide.adoc:.adoc=.sortdata)
+articles.en.slide.indexentry = $(articles.en.slide.adoc:.adoc=.indexentry)
+articles.en.slide.feedentry = $(articles.en.slide.adoc:.adoc=.feedentry)
+articles.en.slide.categorydata = $(articles.en.slide.adoc:.adoc=.categorydata)
+articles.en.slide.homeentry = $(articles.en.slide.adoc:.adoc=.homeentry)
+src/content/en/slide/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.en.slide.sortdata) > $@
+
+src/content/en/slide/feed.xml: src/content/en/slide/sortdata.txt
+src/content/en/slide/feed.xml: $(articles.en.slide.sortdata)
+src/content/en/slide/index.htmllisting: src/content/en/slide/sortdata.txt
+src/content/en/slide/index.htmllisting: $(articles.en.slide.sortdata)
+src/content/en/slide/categories.htmllisting: src/content/en/slide/sortdata.txt
+src/content/en/slide/categories.htmllisting: $(articles.en.slide.sortdata)
+src/content/en/slide/categories.txt: src/content/en/slide/sortdata.txt
+src/content/en/slide/categories.txt: $(articles.en.slide.sortdata)
+src/content/en/slide/index.htmllisting src/content/en/slide/categories.htmllisting: $(articles.en.slide.indexentry)
+src/content/en/slide/categories.txt: $(articles.en.slide.categorydata)
+src/content/en/slide/categories.txt src/content/en/slide/feed.xml: $(articles.en.slide.feedentry)
+src/content/en/slide/categories.htmllisting src/content/en/slide/categories.xml: src/content/en/slide/categories.txt
articles.en.til.adoc = \
@@ -1139,6 +1922,7 @@ articles.en.til.sortdata = $(articles.en.til.adoc:.adoc=.sortdata)
articles.en.til.indexentry = $(articles.en.til.adoc:.adoc=.indexentry)
articles.en.til.feedentry = $(articles.en.til.adoc:.adoc=.feedentry)
articles.en.til.categorydata = $(articles.en.til.adoc:.adoc=.categorydata)
+articles.en.til.homeentry = $(articles.en.til.adoc:.adoc=.homeentry)
src/content/en/til/sortdata.txt: deps.mk
printf '%s\n' $(articles.en.til.sortdata) > $@
@@ -1154,7 +1938,486 @@ src/content/en/til/index.htmllisting src/content/en/til/categories.htmllisting:
src/content/en/til/categories.txt: $(articles.en.til.categorydata)
src/content/en/til/categories.txt src/content/en/til/feed.xml: $(articles.en.til.feedentry)
src/content/en/til/categories.htmllisting src/content/en/til/categories.xml: src/content/en/til/categories.txt
-src/content/en/til/categories.xml.gz: src/content/en/til/categories.xml
+
+
+articles.eo.blog.adoc = \
+
+articles.eo.blog.sortdata = $(articles.eo.blog.adoc:.adoc=.sortdata)
+articles.eo.blog.indexentry = $(articles.eo.blog.adoc:.adoc=.indexentry)
+articles.eo.blog.feedentry = $(articles.eo.blog.adoc:.adoc=.feedentry)
+articles.eo.blog.categorydata = $(articles.eo.blog.adoc:.adoc=.categorydata)
+articles.eo.blog.homeentry = $(articles.eo.blog.adoc:.adoc=.homeentry)
+src/content/eo/blog/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.eo.blog.sortdata) > $@
+
+src/content/eo/blog/feed.xml: src/content/eo/blog/sortdata.txt
+src/content/eo/blog/feed.xml: $(articles.eo.blog.sortdata)
+src/content/eo/blog/index.htmllisting: src/content/eo/blog/sortdata.txt
+src/content/eo/blog/index.htmllisting: $(articles.eo.blog.sortdata)
+src/content/eo/blog/kategorioj.htmllisting: src/content/eo/blog/sortdata.txt
+src/content/eo/blog/kategorioj.htmllisting: $(articles.eo.blog.sortdata)
+src/content/eo/blog/kategorioj.txt: src/content/eo/blog/sortdata.txt
+src/content/eo/blog/kategorioj.txt: $(articles.eo.blog.sortdata)
+src/content/eo/blog/index.htmllisting src/content/eo/blog/kategorioj.htmllisting: $(articles.eo.blog.indexentry)
+src/content/eo/blog/kategorioj.txt: $(articles.eo.blog.categorydata)
+src/content/eo/blog/kategorioj.txt src/content/eo/blog/feed.xml: $(articles.eo.blog.feedentry)
+src/content/eo/blog/kategorioj.htmllisting src/content/eo/blog/kategorioj.xml: src/content/eo/blog/kategorioj.txt
+
+
+articles.eo.hml.adoc = \
+
+articles.eo.hml.sortdata = $(articles.eo.hml.adoc:.adoc=.sortdata)
+articles.eo.hml.indexentry = $(articles.eo.hml.adoc:.adoc=.indexentry)
+articles.eo.hml.feedentry = $(articles.eo.hml.adoc:.adoc=.feedentry)
+articles.eo.hml.categorydata = $(articles.eo.hml.adoc:.adoc=.categorydata)
+articles.eo.hml.homeentry = $(articles.eo.hml.adoc:.adoc=.homeentry)
+src/content/eo/hml/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.eo.hml.sortdata) > $@
+
+src/content/eo/hml/feed.xml: src/content/eo/hml/sortdata.txt
+src/content/eo/hml/feed.xml: $(articles.eo.hml.sortdata)
+src/content/eo/hml/index.htmllisting: src/content/eo/hml/sortdata.txt
+src/content/eo/hml/index.htmllisting: $(articles.eo.hml.sortdata)
+src/content/eo/hml/kategorioj.htmllisting: src/content/eo/hml/sortdata.txt
+src/content/eo/hml/kategorioj.htmllisting: $(articles.eo.hml.sortdata)
+src/content/eo/hml/kategorioj.txt: src/content/eo/hml/sortdata.txt
+src/content/eo/hml/kategorioj.txt: $(articles.eo.hml.sortdata)
+src/content/eo/hml/index.htmllisting src/content/eo/hml/kategorioj.htmllisting: $(articles.eo.hml.indexentry)
+src/content/eo/hml/kategorioj.txt: $(articles.eo.hml.categorydata)
+src/content/eo/hml/kategorioj.txt src/content/eo/hml/feed.xml: $(articles.eo.hml.feedentry)
+src/content/eo/hml/kategorioj.htmllisting src/content/eo/hml/kategorioj.xml: src/content/eo/hml/kategorioj.txt
+
+
+articles.eo.lumbildoj.adoc = \
+
+articles.eo.lumbildoj.sortdata = $(articles.eo.lumbildoj.adoc:.adoc=.sortdata)
+articles.eo.lumbildoj.indexentry = $(articles.eo.lumbildoj.adoc:.adoc=.indexentry)
+articles.eo.lumbildoj.feedentry = $(articles.eo.lumbildoj.adoc:.adoc=.feedentry)
+articles.eo.lumbildoj.categorydata = $(articles.eo.lumbildoj.adoc:.adoc=.categorydata)
+articles.eo.lumbildoj.homeentry = $(articles.eo.lumbildoj.adoc:.adoc=.homeentry)
+src/content/eo/lumbildoj/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.eo.lumbildoj.sortdata) > $@
+
+src/content/eo/lumbildoj/feed.xml: src/content/eo/lumbildoj/sortdata.txt
+src/content/eo/lumbildoj/feed.xml: $(articles.eo.lumbildoj.sortdata)
+src/content/eo/lumbildoj/index.htmllisting: src/content/eo/lumbildoj/sortdata.txt
+src/content/eo/lumbildoj/index.htmllisting: $(articles.eo.lumbildoj.sortdata)
+src/content/eo/lumbildoj/kategorioj.htmllisting: src/content/eo/lumbildoj/sortdata.txt
+src/content/eo/lumbildoj/kategorioj.htmllisting: $(articles.eo.lumbildoj.sortdata)
+src/content/eo/lumbildoj/kategorioj.txt: src/content/eo/lumbildoj/sortdata.txt
+src/content/eo/lumbildoj/kategorioj.txt: $(articles.eo.lumbildoj.sortdata)
+src/content/eo/lumbildoj/index.htmllisting src/content/eo/lumbildoj/kategorioj.htmllisting: $(articles.eo.lumbildoj.indexentry)
+src/content/eo/lumbildoj/kategorioj.txt: $(articles.eo.lumbildoj.categorydata)
+src/content/eo/lumbildoj/kategorioj.txt src/content/eo/lumbildoj/feed.xml: $(articles.eo.lumbildoj.feedentry)
+src/content/eo/lumbildoj/kategorioj.htmllisting src/content/eo/lumbildoj/kategorioj.xml: src/content/eo/lumbildoj/kategorioj.txt
+
+
+articles.eo.pastebin.adoc = \
+
+articles.eo.pastebin.sortdata = $(articles.eo.pastebin.adoc:.adoc=.sortdata)
+articles.eo.pastebin.indexentry = $(articles.eo.pastebin.adoc:.adoc=.indexentry)
+articles.eo.pastebin.feedentry = $(articles.eo.pastebin.adoc:.adoc=.feedentry)
+articles.eo.pastebin.categorydata = $(articles.eo.pastebin.adoc:.adoc=.categorydata)
+articles.eo.pastebin.homeentry = $(articles.eo.pastebin.adoc:.adoc=.homeentry)
+src/content/eo/pastebin/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.eo.pastebin.sortdata) > $@
+
+src/content/eo/pastebin/feed.xml: src/content/eo/pastebin/sortdata.txt
+src/content/eo/pastebin/feed.xml: $(articles.eo.pastebin.sortdata)
+src/content/eo/pastebin/index.htmllisting: src/content/eo/pastebin/sortdata.txt
+src/content/eo/pastebin/index.htmllisting: $(articles.eo.pastebin.sortdata)
+src/content/eo/pastebin/kategorioj.htmllisting: src/content/eo/pastebin/sortdata.txt
+src/content/eo/pastebin/kategorioj.htmllisting: $(articles.eo.pastebin.sortdata)
+src/content/eo/pastebin/kategorioj.txt: src/content/eo/pastebin/sortdata.txt
+src/content/eo/pastebin/kategorioj.txt: $(articles.eo.pastebin.sortdata)
+src/content/eo/pastebin/index.htmllisting src/content/eo/pastebin/kategorioj.htmllisting: $(articles.eo.pastebin.indexentry)
+src/content/eo/pastebin/kategorioj.txt: $(articles.eo.pastebin.categorydata)
+src/content/eo/pastebin/kategorioj.txt src/content/eo/pastebin/feed.xml: $(articles.eo.pastebin.feedentry)
+src/content/eo/pastebin/kategorioj.htmllisting src/content/eo/pastebin/kategorioj.xml: src/content/eo/pastebin/kategorioj.txt
+
+
+articles.eo.podkasto.adoc = \
+
+articles.eo.podkasto.sortdata = $(articles.eo.podkasto.adoc:.adoc=.sortdata)
+articles.eo.podkasto.indexentry = $(articles.eo.podkasto.adoc:.adoc=.indexentry)
+articles.eo.podkasto.feedentry = $(articles.eo.podkasto.adoc:.adoc=.feedentry)
+articles.eo.podkasto.categorydata = $(articles.eo.podkasto.adoc:.adoc=.categorydata)
+articles.eo.podkasto.homeentry = $(articles.eo.podkasto.adoc:.adoc=.homeentry)
+src/content/eo/podkasto/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.eo.podkasto.sortdata) > $@
+
+src/content/eo/podkasto/feed.xml: src/content/eo/podkasto/sortdata.txt
+src/content/eo/podkasto/feed.xml: $(articles.eo.podkasto.sortdata)
+src/content/eo/podkasto/index.htmllisting: src/content/eo/podkasto/sortdata.txt
+src/content/eo/podkasto/index.htmllisting: $(articles.eo.podkasto.sortdata)
+src/content/eo/podkasto/kategorioj.htmllisting: src/content/eo/podkasto/sortdata.txt
+src/content/eo/podkasto/kategorioj.htmllisting: $(articles.eo.podkasto.sortdata)
+src/content/eo/podkasto/kategorioj.txt: src/content/eo/podkasto/sortdata.txt
+src/content/eo/podkasto/kategorioj.txt: $(articles.eo.podkasto.sortdata)
+src/content/eo/podkasto/index.htmllisting src/content/eo/podkasto/kategorioj.htmllisting: $(articles.eo.podkasto.indexentry)
+src/content/eo/podkasto/kategorioj.txt: $(articles.eo.podkasto.categorydata)
+src/content/eo/podkasto/kategorioj.txt src/content/eo/podkasto/feed.xml: $(articles.eo.podkasto.feedentry)
+src/content/eo/podkasto/kategorioj.htmllisting src/content/eo/podkasto/kategorioj.xml: src/content/eo/podkasto/kategorioj.txt
+
+
+articles.eo.screencast.adoc = \
+
+articles.eo.screencast.sortdata = $(articles.eo.screencast.adoc:.adoc=.sortdata)
+articles.eo.screencast.indexentry = $(articles.eo.screencast.adoc:.adoc=.indexentry)
+articles.eo.screencast.feedentry = $(articles.eo.screencast.adoc:.adoc=.feedentry)
+articles.eo.screencast.categorydata = $(articles.eo.screencast.adoc:.adoc=.categorydata)
+articles.eo.screencast.homeentry = $(articles.eo.screencast.adoc:.adoc=.homeentry)
+src/content/eo/screencast/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.eo.screencast.sortdata) > $@
+
+src/content/eo/screencast/feed.xml: src/content/eo/screencast/sortdata.txt
+src/content/eo/screencast/feed.xml: $(articles.eo.screencast.sortdata)
+src/content/eo/screencast/index.htmllisting: src/content/eo/screencast/sortdata.txt
+src/content/eo/screencast/index.htmllisting: $(articles.eo.screencast.sortdata)
+src/content/eo/screencast/kategorioj.htmllisting: src/content/eo/screencast/sortdata.txt
+src/content/eo/screencast/kategorioj.htmllisting: $(articles.eo.screencast.sortdata)
+src/content/eo/screencast/kategorioj.txt: src/content/eo/screencast/sortdata.txt
+src/content/eo/screencast/kategorioj.txt: $(articles.eo.screencast.sortdata)
+src/content/eo/screencast/index.htmllisting src/content/eo/screencast/kategorioj.htmllisting: $(articles.eo.screencast.indexentry)
+src/content/eo/screencast/kategorioj.txt: $(articles.eo.screencast.categorydata)
+src/content/eo/screencast/kategorioj.txt src/content/eo/screencast/feed.xml: $(articles.eo.screencast.feedentry)
+src/content/eo/screencast/kategorioj.htmllisting src/content/eo/screencast/kategorioj.xml: src/content/eo/screencast/kategorioj.txt
+
+
+articles.es.blog.adoc = \
+
+articles.es.blog.sortdata = $(articles.es.blog.adoc:.adoc=.sortdata)
+articles.es.blog.indexentry = $(articles.es.blog.adoc:.adoc=.indexentry)
+articles.es.blog.feedentry = $(articles.es.blog.adoc:.adoc=.feedentry)
+articles.es.blog.categorydata = $(articles.es.blog.adoc:.adoc=.categorydata)
+articles.es.blog.homeentry = $(articles.es.blog.adoc:.adoc=.homeentry)
+src/content/es/blog/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.es.blog.sortdata) > $@
+
+src/content/es/blog/feed.xml: src/content/es/blog/sortdata.txt
+src/content/es/blog/feed.xml: $(articles.es.blog.sortdata)
+src/content/es/blog/index.htmllisting: src/content/es/blog/sortdata.txt
+src/content/es/blog/index.htmllisting: $(articles.es.blog.sortdata)
+src/content/es/blog/categorias.htmllisting: src/content/es/blog/sortdata.txt
+src/content/es/blog/categorias.htmllisting: $(articles.es.blog.sortdata)
+src/content/es/blog/categorias.txt: src/content/es/blog/sortdata.txt
+src/content/es/blog/categorias.txt: $(articles.es.blog.sortdata)
+src/content/es/blog/index.htmllisting src/content/es/blog/categorias.htmllisting: $(articles.es.blog.indexentry)
+src/content/es/blog/categorias.txt: $(articles.es.blog.categorydata)
+src/content/es/blog/categorias.txt src/content/es/blog/feed.xml: $(articles.es.blog.feedentry)
+src/content/es/blog/categorias.htmllisting src/content/es/blog/categorias.xml: src/content/es/blog/categorias.txt
+
+
+articles.es.diapositivas.adoc = \
+
+articles.es.diapositivas.sortdata = $(articles.es.diapositivas.adoc:.adoc=.sortdata)
+articles.es.diapositivas.indexentry = $(articles.es.diapositivas.adoc:.adoc=.indexentry)
+articles.es.diapositivas.feedentry = $(articles.es.diapositivas.adoc:.adoc=.feedentry)
+articles.es.diapositivas.categorydata = $(articles.es.diapositivas.adoc:.adoc=.categorydata)
+articles.es.diapositivas.homeentry = $(articles.es.diapositivas.adoc:.adoc=.homeentry)
+src/content/es/diapositivas/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.es.diapositivas.sortdata) > $@
+
+src/content/es/diapositivas/feed.xml: src/content/es/diapositivas/sortdata.txt
+src/content/es/diapositivas/feed.xml: $(articles.es.diapositivas.sortdata)
+src/content/es/diapositivas/index.htmllisting: src/content/es/diapositivas/sortdata.txt
+src/content/es/diapositivas/index.htmllisting: $(articles.es.diapositivas.sortdata)
+src/content/es/diapositivas/categorias.htmllisting: src/content/es/diapositivas/sortdata.txt
+src/content/es/diapositivas/categorias.htmllisting: $(articles.es.diapositivas.sortdata)
+src/content/es/diapositivas/categorias.txt: src/content/es/diapositivas/sortdata.txt
+src/content/es/diapositivas/categorias.txt: $(articles.es.diapositivas.sortdata)
+src/content/es/diapositivas/index.htmllisting src/content/es/diapositivas/categorias.htmllisting: $(articles.es.diapositivas.indexentry)
+src/content/es/diapositivas/categorias.txt: $(articles.es.diapositivas.categorydata)
+src/content/es/diapositivas/categorias.txt src/content/es/diapositivas/feed.xml: $(articles.es.diapositivas.feedentry)
+src/content/es/diapositivas/categorias.htmllisting src/content/es/diapositivas/categorias.xml: src/content/es/diapositivas/categorias.txt
+
+
+articles.es.haa.adoc = \
+
+articles.es.haa.sortdata = $(articles.es.haa.adoc:.adoc=.sortdata)
+articles.es.haa.indexentry = $(articles.es.haa.adoc:.adoc=.indexentry)
+articles.es.haa.feedentry = $(articles.es.haa.adoc:.adoc=.feedentry)
+articles.es.haa.categorydata = $(articles.es.haa.adoc:.adoc=.categorydata)
+articles.es.haa.homeentry = $(articles.es.haa.adoc:.adoc=.homeentry)
+src/content/es/haa/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.es.haa.sortdata) > $@
+
+src/content/es/haa/feed.xml: src/content/es/haa/sortdata.txt
+src/content/es/haa/feed.xml: $(articles.es.haa.sortdata)
+src/content/es/haa/index.htmllisting: src/content/es/haa/sortdata.txt
+src/content/es/haa/index.htmllisting: $(articles.es.haa.sortdata)
+src/content/es/haa/categorias.htmllisting: src/content/es/haa/sortdata.txt
+src/content/es/haa/categorias.htmllisting: $(articles.es.haa.sortdata)
+src/content/es/haa/categorias.txt: src/content/es/haa/sortdata.txt
+src/content/es/haa/categorias.txt: $(articles.es.haa.sortdata)
+src/content/es/haa/index.htmllisting src/content/es/haa/categorias.htmllisting: $(articles.es.haa.indexentry)
+src/content/es/haa/categorias.txt: $(articles.es.haa.categorydata)
+src/content/es/haa/categorias.txt src/content/es/haa/feed.xml: $(articles.es.haa.feedentry)
+src/content/es/haa/categorias.htmllisting src/content/es/haa/categorias.xml: src/content/es/haa/categorias.txt
+
+
+articles.es.pastebin.adoc = \
+
+articles.es.pastebin.sortdata = $(articles.es.pastebin.adoc:.adoc=.sortdata)
+articles.es.pastebin.indexentry = $(articles.es.pastebin.adoc:.adoc=.indexentry)
+articles.es.pastebin.feedentry = $(articles.es.pastebin.adoc:.adoc=.feedentry)
+articles.es.pastebin.categorydata = $(articles.es.pastebin.adoc:.adoc=.categorydata)
+articles.es.pastebin.homeentry = $(articles.es.pastebin.adoc:.adoc=.homeentry)
+src/content/es/pastebin/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.es.pastebin.sortdata) > $@
+
+src/content/es/pastebin/feed.xml: src/content/es/pastebin/sortdata.txt
+src/content/es/pastebin/feed.xml: $(articles.es.pastebin.sortdata)
+src/content/es/pastebin/index.htmllisting: src/content/es/pastebin/sortdata.txt
+src/content/es/pastebin/index.htmllisting: $(articles.es.pastebin.sortdata)
+src/content/es/pastebin/categorias.htmllisting: src/content/es/pastebin/sortdata.txt
+src/content/es/pastebin/categorias.htmllisting: $(articles.es.pastebin.sortdata)
+src/content/es/pastebin/categorias.txt: src/content/es/pastebin/sortdata.txt
+src/content/es/pastebin/categorias.txt: $(articles.es.pastebin.sortdata)
+src/content/es/pastebin/index.htmllisting src/content/es/pastebin/categorias.htmllisting: $(articles.es.pastebin.indexentry)
+src/content/es/pastebin/categorias.txt: $(articles.es.pastebin.categorydata)
+src/content/es/pastebin/categorias.txt src/content/es/pastebin/feed.xml: $(articles.es.pastebin.feedentry)
+src/content/es/pastebin/categorias.htmllisting src/content/es/pastebin/categorias.xml: src/content/es/pastebin/categorias.txt
+
+
+articles.es.podcast.adoc = \
+
+articles.es.podcast.sortdata = $(articles.es.podcast.adoc:.adoc=.sortdata)
+articles.es.podcast.indexentry = $(articles.es.podcast.adoc:.adoc=.indexentry)
+articles.es.podcast.feedentry = $(articles.es.podcast.adoc:.adoc=.feedentry)
+articles.es.podcast.categorydata = $(articles.es.podcast.adoc:.adoc=.categorydata)
+articles.es.podcast.homeentry = $(articles.es.podcast.adoc:.adoc=.homeentry)
+src/content/es/podcast/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.es.podcast.sortdata) > $@
+
+src/content/es/podcast/feed.xml: src/content/es/podcast/sortdata.txt
+src/content/es/podcast/feed.xml: $(articles.es.podcast.sortdata)
+src/content/es/podcast/index.htmllisting: src/content/es/podcast/sortdata.txt
+src/content/es/podcast/index.htmllisting: $(articles.es.podcast.sortdata)
+src/content/es/podcast/categorias.htmllisting: src/content/es/podcast/sortdata.txt
+src/content/es/podcast/categorias.htmllisting: $(articles.es.podcast.sortdata)
+src/content/es/podcast/categorias.txt: src/content/es/podcast/sortdata.txt
+src/content/es/podcast/categorias.txt: $(articles.es.podcast.sortdata)
+src/content/es/podcast/index.htmllisting src/content/es/podcast/categorias.htmllisting: $(articles.es.podcast.indexentry)
+src/content/es/podcast/categorias.txt: $(articles.es.podcast.categorydata)
+src/content/es/podcast/categorias.txt src/content/es/podcast/feed.xml: $(articles.es.podcast.feedentry)
+src/content/es/podcast/categorias.htmllisting src/content/es/podcast/categorias.xml: src/content/es/podcast/categorias.txt
+
+
+articles.es.screencast.adoc = \
+
+articles.es.screencast.sortdata = $(articles.es.screencast.adoc:.adoc=.sortdata)
+articles.es.screencast.indexentry = $(articles.es.screencast.adoc:.adoc=.indexentry)
+articles.es.screencast.feedentry = $(articles.es.screencast.adoc:.adoc=.feedentry)
+articles.es.screencast.categorydata = $(articles.es.screencast.adoc:.adoc=.categorydata)
+articles.es.screencast.homeentry = $(articles.es.screencast.adoc:.adoc=.homeentry)
+src/content/es/screencast/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.es.screencast.sortdata) > $@
+
+src/content/es/screencast/feed.xml: src/content/es/screencast/sortdata.txt
+src/content/es/screencast/feed.xml: $(articles.es.screencast.sortdata)
+src/content/es/screencast/index.htmllisting: src/content/es/screencast/sortdata.txt
+src/content/es/screencast/index.htmllisting: $(articles.es.screencast.sortdata)
+src/content/es/screencast/categorias.htmllisting: src/content/es/screencast/sortdata.txt
+src/content/es/screencast/categorias.htmllisting: $(articles.es.screencast.sortdata)
+src/content/es/screencast/categorias.txt: src/content/es/screencast/sortdata.txt
+src/content/es/screencast/categorias.txt: $(articles.es.screencast.sortdata)
+src/content/es/screencast/index.htmllisting src/content/es/screencast/categorias.htmllisting: $(articles.es.screencast.indexentry)
+src/content/es/screencast/categorias.txt: $(articles.es.screencast.categorydata)
+src/content/es/screencast/categorias.txt src/content/es/screencast/feed.xml: $(articles.es.screencast.feedentry)
+src/content/es/screencast/categorias.htmllisting src/content/es/screencast/categorias.xml: src/content/es/screencast/categorias.txt
+
+
+articles.fr.aja.adoc = \
+
+articles.fr.aja.sortdata = $(articles.fr.aja.adoc:.adoc=.sortdata)
+articles.fr.aja.indexentry = $(articles.fr.aja.adoc:.adoc=.indexentry)
+articles.fr.aja.feedentry = $(articles.fr.aja.adoc:.adoc=.feedentry)
+articles.fr.aja.categorydata = $(articles.fr.aja.adoc:.adoc=.categorydata)
+articles.fr.aja.homeentry = $(articles.fr.aja.adoc:.adoc=.homeentry)
+src/content/fr/aja/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.fr.aja.sortdata) > $@
+
+src/content/fr/aja/feed.xml: src/content/fr/aja/sortdata.txt
+src/content/fr/aja/feed.xml: $(articles.fr.aja.sortdata)
+src/content/fr/aja/index.htmllisting: src/content/fr/aja/sortdata.txt
+src/content/fr/aja/index.htmllisting: $(articles.fr.aja.sortdata)
+src/content/fr/aja/categories.htmllisting: src/content/fr/aja/sortdata.txt
+src/content/fr/aja/categories.htmllisting: $(articles.fr.aja.sortdata)
+src/content/fr/aja/categories.txt: src/content/fr/aja/sortdata.txt
+src/content/fr/aja/categories.txt: $(articles.fr.aja.sortdata)
+src/content/fr/aja/index.htmllisting src/content/fr/aja/categories.htmllisting: $(articles.fr.aja.indexentry)
+src/content/fr/aja/categories.txt: $(articles.fr.aja.categorydata)
+src/content/fr/aja/categories.txt src/content/fr/aja/feed.xml: $(articles.fr.aja.feedentry)
+src/content/fr/aja/categories.htmllisting src/content/fr/aja/categories.xml: src/content/fr/aja/categories.txt
+
+
+articles.fr.blog.adoc = \
+
+articles.fr.blog.sortdata = $(articles.fr.blog.adoc:.adoc=.sortdata)
+articles.fr.blog.indexentry = $(articles.fr.blog.adoc:.adoc=.indexentry)
+articles.fr.blog.feedentry = $(articles.fr.blog.adoc:.adoc=.feedentry)
+articles.fr.blog.categorydata = $(articles.fr.blog.adoc:.adoc=.categorydata)
+articles.fr.blog.homeentry = $(articles.fr.blog.adoc:.adoc=.homeentry)
+src/content/fr/blog/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.fr.blog.sortdata) > $@
+
+src/content/fr/blog/feed.xml: src/content/fr/blog/sortdata.txt
+src/content/fr/blog/feed.xml: $(articles.fr.blog.sortdata)
+src/content/fr/blog/index.htmllisting: src/content/fr/blog/sortdata.txt
+src/content/fr/blog/index.htmllisting: $(articles.fr.blog.sortdata)
+src/content/fr/blog/categories.htmllisting: src/content/fr/blog/sortdata.txt
+src/content/fr/blog/categories.htmllisting: $(articles.fr.blog.sortdata)
+src/content/fr/blog/categories.txt: src/content/fr/blog/sortdata.txt
+src/content/fr/blog/categories.txt: $(articles.fr.blog.sortdata)
+src/content/fr/blog/index.htmllisting src/content/fr/blog/categories.htmllisting: $(articles.fr.blog.indexentry)
+src/content/fr/blog/categories.txt: $(articles.fr.blog.categorydata)
+src/content/fr/blog/categories.txt src/content/fr/blog/feed.xml: $(articles.fr.blog.feedentry)
+src/content/fr/blog/categories.htmllisting src/content/fr/blog/categories.xml: src/content/fr/blog/categories.txt
+
+
+articles.fr.diapositives.adoc = \
+
+articles.fr.diapositives.sortdata = $(articles.fr.diapositives.adoc:.adoc=.sortdata)
+articles.fr.diapositives.indexentry = $(articles.fr.diapositives.adoc:.adoc=.indexentry)
+articles.fr.diapositives.feedentry = $(articles.fr.diapositives.adoc:.adoc=.feedentry)
+articles.fr.diapositives.categorydata = $(articles.fr.diapositives.adoc:.adoc=.categorydata)
+articles.fr.diapositives.homeentry = $(articles.fr.diapositives.adoc:.adoc=.homeentry)
+src/content/fr/diapositives/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.fr.diapositives.sortdata) > $@
+
+src/content/fr/diapositives/feed.xml: src/content/fr/diapositives/sortdata.txt
+src/content/fr/diapositives/feed.xml: $(articles.fr.diapositives.sortdata)
+src/content/fr/diapositives/index.htmllisting: src/content/fr/diapositives/sortdata.txt
+src/content/fr/diapositives/index.htmllisting: $(articles.fr.diapositives.sortdata)
+src/content/fr/diapositives/categories.htmllisting: src/content/fr/diapositives/sortdata.txt
+src/content/fr/diapositives/categories.htmllisting: $(articles.fr.diapositives.sortdata)
+src/content/fr/diapositives/categories.txt: src/content/fr/diapositives/sortdata.txt
+src/content/fr/diapositives/categories.txt: $(articles.fr.diapositives.sortdata)
+src/content/fr/diapositives/index.htmllisting src/content/fr/diapositives/categories.htmllisting: $(articles.fr.diapositives.indexentry)
+src/content/fr/diapositives/categories.txt: $(articles.fr.diapositives.categorydata)
+src/content/fr/diapositives/categories.txt src/content/fr/diapositives/feed.xml: $(articles.fr.diapositives.feedentry)
+src/content/fr/diapositives/categories.htmllisting src/content/fr/diapositives/categories.xml: src/content/fr/diapositives/categories.txt
+
+
+articles.fr.pastebin.adoc = \
+
+articles.fr.pastebin.sortdata = $(articles.fr.pastebin.adoc:.adoc=.sortdata)
+articles.fr.pastebin.indexentry = $(articles.fr.pastebin.adoc:.adoc=.indexentry)
+articles.fr.pastebin.feedentry = $(articles.fr.pastebin.adoc:.adoc=.feedentry)
+articles.fr.pastebin.categorydata = $(articles.fr.pastebin.adoc:.adoc=.categorydata)
+articles.fr.pastebin.homeentry = $(articles.fr.pastebin.adoc:.adoc=.homeentry)
+src/content/fr/pastebin/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.fr.pastebin.sortdata) > $@
+
+src/content/fr/pastebin/feed.xml: src/content/fr/pastebin/sortdata.txt
+src/content/fr/pastebin/feed.xml: $(articles.fr.pastebin.sortdata)
+src/content/fr/pastebin/index.htmllisting: src/content/fr/pastebin/sortdata.txt
+src/content/fr/pastebin/index.htmllisting: $(articles.fr.pastebin.sortdata)
+src/content/fr/pastebin/categories.htmllisting: src/content/fr/pastebin/sortdata.txt
+src/content/fr/pastebin/categories.htmllisting: $(articles.fr.pastebin.sortdata)
+src/content/fr/pastebin/categories.txt: src/content/fr/pastebin/sortdata.txt
+src/content/fr/pastebin/categories.txt: $(articles.fr.pastebin.sortdata)
+src/content/fr/pastebin/index.htmllisting src/content/fr/pastebin/categories.htmllisting: $(articles.fr.pastebin.indexentry)
+src/content/fr/pastebin/categories.txt: $(articles.fr.pastebin.categorydata)
+src/content/fr/pastebin/categories.txt src/content/fr/pastebin/feed.xml: $(articles.fr.pastebin.feedentry)
+src/content/fr/pastebin/categories.htmllisting src/content/fr/pastebin/categories.xml: src/content/fr/pastebin/categories.txt
+
+
+articles.fr.podcast.adoc = \
+
+articles.fr.podcast.sortdata = $(articles.fr.podcast.adoc:.adoc=.sortdata)
+articles.fr.podcast.indexentry = $(articles.fr.podcast.adoc:.adoc=.indexentry)
+articles.fr.podcast.feedentry = $(articles.fr.podcast.adoc:.adoc=.feedentry)
+articles.fr.podcast.categorydata = $(articles.fr.podcast.adoc:.adoc=.categorydata)
+articles.fr.podcast.homeentry = $(articles.fr.podcast.adoc:.adoc=.homeentry)
+src/content/fr/podcast/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.fr.podcast.sortdata) > $@
+
+src/content/fr/podcast/feed.xml: src/content/fr/podcast/sortdata.txt
+src/content/fr/podcast/feed.xml: $(articles.fr.podcast.sortdata)
+src/content/fr/podcast/index.htmllisting: src/content/fr/podcast/sortdata.txt
+src/content/fr/podcast/index.htmllisting: $(articles.fr.podcast.sortdata)
+src/content/fr/podcast/categories.htmllisting: src/content/fr/podcast/sortdata.txt
+src/content/fr/podcast/categories.htmllisting: $(articles.fr.podcast.sortdata)
+src/content/fr/podcast/categories.txt: src/content/fr/podcast/sortdata.txt
+src/content/fr/podcast/categories.txt: $(articles.fr.podcast.sortdata)
+src/content/fr/podcast/index.htmllisting src/content/fr/podcast/categories.htmllisting: $(articles.fr.podcast.indexentry)
+src/content/fr/podcast/categories.txt: $(articles.fr.podcast.categorydata)
+src/content/fr/podcast/categories.txt src/content/fr/podcast/feed.xml: $(articles.fr.podcast.feedentry)
+src/content/fr/podcast/categories.htmllisting src/content/fr/podcast/categories.xml: src/content/fr/podcast/categories.txt
+
+
+articles.fr.screencast.adoc = \
+
+articles.fr.screencast.sortdata = $(articles.fr.screencast.adoc:.adoc=.sortdata)
+articles.fr.screencast.indexentry = $(articles.fr.screencast.adoc:.adoc=.indexentry)
+articles.fr.screencast.feedentry = $(articles.fr.screencast.adoc:.adoc=.feedentry)
+articles.fr.screencast.categorydata = $(articles.fr.screencast.adoc:.adoc=.categorydata)
+articles.fr.screencast.homeentry = $(articles.fr.screencast.adoc:.adoc=.homeentry)
+src/content/fr/screencast/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.fr.screencast.sortdata) > $@
+
+src/content/fr/screencast/feed.xml: src/content/fr/screencast/sortdata.txt
+src/content/fr/screencast/feed.xml: $(articles.fr.screencast.sortdata)
+src/content/fr/screencast/index.htmllisting: src/content/fr/screencast/sortdata.txt
+src/content/fr/screencast/index.htmllisting: $(articles.fr.screencast.sortdata)
+src/content/fr/screencast/categories.htmllisting: src/content/fr/screencast/sortdata.txt
+src/content/fr/screencast/categories.htmllisting: $(articles.fr.screencast.sortdata)
+src/content/fr/screencast/categories.txt: src/content/fr/screencast/sortdata.txt
+src/content/fr/screencast/categories.txt: $(articles.fr.screencast.sortdata)
+src/content/fr/screencast/index.htmllisting src/content/fr/screencast/categories.htmllisting: $(articles.fr.screencast.indexentry)
+src/content/fr/screencast/categories.txt: $(articles.fr.screencast.categorydata)
+src/content/fr/screencast/categories.txt src/content/fr/screencast/feed.xml: $(articles.fr.screencast.feedentry)
+src/content/fr/screencast/categories.htmllisting src/content/fr/screencast/categories.xml: src/content/fr/screencast/categories.txt
+
+
+articles.pt.blog.adoc = \
+
+articles.pt.blog.sortdata = $(articles.pt.blog.adoc:.adoc=.sortdata)
+articles.pt.blog.indexentry = $(articles.pt.blog.adoc:.adoc=.indexentry)
+articles.pt.blog.feedentry = $(articles.pt.blog.adoc:.adoc=.feedentry)
+articles.pt.blog.categorydata = $(articles.pt.blog.adoc:.adoc=.categorydata)
+articles.pt.blog.homeentry = $(articles.pt.blog.adoc:.adoc=.homeentry)
+src/content/pt/blog/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.pt.blog.sortdata) > $@
+
+src/content/pt/blog/feed.xml: src/content/pt/blog/sortdata.txt
+src/content/pt/blog/feed.xml: $(articles.pt.blog.sortdata)
+src/content/pt/blog/index.htmllisting: src/content/pt/blog/sortdata.txt
+src/content/pt/blog/index.htmllisting: $(articles.pt.blog.sortdata)
+src/content/pt/blog/categorias.htmllisting: src/content/pt/blog/sortdata.txt
+src/content/pt/blog/categorias.htmllisting: $(articles.pt.blog.sortdata)
+src/content/pt/blog/categorias.txt: src/content/pt/blog/sortdata.txt
+src/content/pt/blog/categorias.txt: $(articles.pt.blog.sortdata)
+src/content/pt/blog/index.htmllisting src/content/pt/blog/categorias.htmllisting: $(articles.pt.blog.indexentry)
+src/content/pt/blog/categorias.txt: $(articles.pt.blog.categorydata)
+src/content/pt/blog/categorias.txt src/content/pt/blog/feed.xml: $(articles.pt.blog.feedentry)
+src/content/pt/blog/categorias.htmllisting src/content/pt/blog/categorias.xml: src/content/pt/blog/categorias.txt
+
+
+articles.pt.eslaides.adoc = \
+
+articles.pt.eslaides.sortdata = $(articles.pt.eslaides.adoc:.adoc=.sortdata)
+articles.pt.eslaides.indexentry = $(articles.pt.eslaides.adoc:.adoc=.indexentry)
+articles.pt.eslaides.feedentry = $(articles.pt.eslaides.adoc:.adoc=.feedentry)
+articles.pt.eslaides.categorydata = $(articles.pt.eslaides.adoc:.adoc=.categorydata)
+articles.pt.eslaides.homeentry = $(articles.pt.eslaides.adoc:.adoc=.homeentry)
+src/content/pt/eslaides/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.pt.eslaides.sortdata) > $@
+
+src/content/pt/eslaides/feed.xml: src/content/pt/eslaides/sortdata.txt
+src/content/pt/eslaides/feed.xml: $(articles.pt.eslaides.sortdata)
+src/content/pt/eslaides/index.htmllisting: src/content/pt/eslaides/sortdata.txt
+src/content/pt/eslaides/index.htmllisting: $(articles.pt.eslaides.sortdata)
+src/content/pt/eslaides/categorias.htmllisting: src/content/pt/eslaides/sortdata.txt
+src/content/pt/eslaides/categorias.htmllisting: $(articles.pt.eslaides.sortdata)
+src/content/pt/eslaides/categorias.txt: src/content/pt/eslaides/sortdata.txt
+src/content/pt/eslaides/categorias.txt: $(articles.pt.eslaides.sortdata)
+src/content/pt/eslaides/index.htmllisting src/content/pt/eslaides/categorias.htmllisting: $(articles.pt.eslaides.indexentry)
+src/content/pt/eslaides/categorias.txt: $(articles.pt.eslaides.categorydata)
+src/content/pt/eslaides/categorias.txt src/content/pt/eslaides/feed.xml: $(articles.pt.eslaides.feedentry)
+src/content/pt/eslaides/categorias.htmllisting src/content/pt/eslaides/categorias.xml: src/content/pt/eslaides/categorias.txt
articles.pt.hea.adoc = \
@@ -1164,6 +2427,7 @@ articles.pt.hea.sortdata = $(articles.pt.hea.adoc:.adoc=.sortdata)
articles.pt.hea.indexentry = $(articles.pt.hea.adoc:.adoc=.indexentry)
articles.pt.hea.feedentry = $(articles.pt.hea.adoc:.adoc=.feedentry)
articles.pt.hea.categorydata = $(articles.pt.hea.adoc:.adoc=.categorydata)
+articles.pt.hea.homeentry = $(articles.pt.hea.adoc:.adoc=.homeentry)
src/content/pt/hea/sortdata.txt: deps.mk
printf '%s\n' $(articles.pt.hea.sortdata) > $@
@@ -1179,4 +2443,872 @@ src/content/pt/hea/index.htmllisting src/content/pt/hea/categorias.htmllisting:
src/content/pt/hea/categorias.txt: $(articles.pt.hea.categorydata)
src/content/pt/hea/categorias.txt src/content/pt/hea/feed.xml: $(articles.pt.hea.feedentry)
src/content/pt/hea/categorias.htmllisting src/content/pt/hea/categorias.xml: src/content/pt/hea/categorias.txt
-src/content/pt/hea/categorias.xml.gz: src/content/pt/hea/categorias.xml
+
+
+articles.pt.pastebin.adoc = \
+
+articles.pt.pastebin.sortdata = $(articles.pt.pastebin.adoc:.adoc=.sortdata)
+articles.pt.pastebin.indexentry = $(articles.pt.pastebin.adoc:.adoc=.indexentry)
+articles.pt.pastebin.feedentry = $(articles.pt.pastebin.adoc:.adoc=.feedentry)
+articles.pt.pastebin.categorydata = $(articles.pt.pastebin.adoc:.adoc=.categorydata)
+articles.pt.pastebin.homeentry = $(articles.pt.pastebin.adoc:.adoc=.homeentry)
+src/content/pt/pastebin/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.pt.pastebin.sortdata) > $@
+
+src/content/pt/pastebin/feed.xml: src/content/pt/pastebin/sortdata.txt
+src/content/pt/pastebin/feed.xml: $(articles.pt.pastebin.sortdata)
+src/content/pt/pastebin/index.htmllisting: src/content/pt/pastebin/sortdata.txt
+src/content/pt/pastebin/index.htmllisting: $(articles.pt.pastebin.sortdata)
+src/content/pt/pastebin/categorias.htmllisting: src/content/pt/pastebin/sortdata.txt
+src/content/pt/pastebin/categorias.htmllisting: $(articles.pt.pastebin.sortdata)
+src/content/pt/pastebin/categorias.txt: src/content/pt/pastebin/sortdata.txt
+src/content/pt/pastebin/categorias.txt: $(articles.pt.pastebin.sortdata)
+src/content/pt/pastebin/index.htmllisting src/content/pt/pastebin/categorias.htmllisting: $(articles.pt.pastebin.indexentry)
+src/content/pt/pastebin/categorias.txt: $(articles.pt.pastebin.categorydata)
+src/content/pt/pastebin/categorias.txt src/content/pt/pastebin/feed.xml: $(articles.pt.pastebin.feedentry)
+src/content/pt/pastebin/categorias.htmllisting src/content/pt/pastebin/categorias.xml: src/content/pt/pastebin/categorias.txt
+
+
+articles.pt.podcast.adoc = \
+
+articles.pt.podcast.sortdata = $(articles.pt.podcast.adoc:.adoc=.sortdata)
+articles.pt.podcast.indexentry = $(articles.pt.podcast.adoc:.adoc=.indexentry)
+articles.pt.podcast.feedentry = $(articles.pt.podcast.adoc:.adoc=.feedentry)
+articles.pt.podcast.categorydata = $(articles.pt.podcast.adoc:.adoc=.categorydata)
+articles.pt.podcast.homeentry = $(articles.pt.podcast.adoc:.adoc=.homeentry)
+src/content/pt/podcast/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.pt.podcast.sortdata) > $@
+
+src/content/pt/podcast/feed.xml: src/content/pt/podcast/sortdata.txt
+src/content/pt/podcast/feed.xml: $(articles.pt.podcast.sortdata)
+src/content/pt/podcast/index.htmllisting: src/content/pt/podcast/sortdata.txt
+src/content/pt/podcast/index.htmllisting: $(articles.pt.podcast.sortdata)
+src/content/pt/podcast/categorias.htmllisting: src/content/pt/podcast/sortdata.txt
+src/content/pt/podcast/categorias.htmllisting: $(articles.pt.podcast.sortdata)
+src/content/pt/podcast/categorias.txt: src/content/pt/podcast/sortdata.txt
+src/content/pt/podcast/categorias.txt: $(articles.pt.podcast.sortdata)
+src/content/pt/podcast/index.htmllisting src/content/pt/podcast/categorias.htmllisting: $(articles.pt.podcast.indexentry)
+src/content/pt/podcast/categorias.txt: $(articles.pt.podcast.categorydata)
+src/content/pt/podcast/categorias.txt src/content/pt/podcast/feed.xml: $(articles.pt.podcast.feedentry)
+src/content/pt/podcast/categorias.htmllisting src/content/pt/podcast/categorias.xml: src/content/pt/podcast/categorias.txt
+
+
+articles.pt.screencast.adoc = \
+
+articles.pt.screencast.sortdata = $(articles.pt.screencast.adoc:.adoc=.sortdata)
+articles.pt.screencast.indexentry = $(articles.pt.screencast.adoc:.adoc=.indexentry)
+articles.pt.screencast.feedentry = $(articles.pt.screencast.adoc:.adoc=.feedentry)
+articles.pt.screencast.categorydata = $(articles.pt.screencast.adoc:.adoc=.categorydata)
+articles.pt.screencast.homeentry = $(articles.pt.screencast.adoc:.adoc=.homeentry)
+src/content/pt/screencast/sortdata.txt: deps.mk
+ printf '%s\n' $(articles.pt.screencast.sortdata) > $@
+
+src/content/pt/screencast/feed.xml: src/content/pt/screencast/sortdata.txt
+src/content/pt/screencast/feed.xml: $(articles.pt.screencast.sortdata)
+src/content/pt/screencast/index.htmllisting: src/content/pt/screencast/sortdata.txt
+src/content/pt/screencast/index.htmllisting: $(articles.pt.screencast.sortdata)
+src/content/pt/screencast/categorias.htmllisting: src/content/pt/screencast/sortdata.txt
+src/content/pt/screencast/categorias.htmllisting: $(articles.pt.screencast.sortdata)
+src/content/pt/screencast/categorias.txt: src/content/pt/screencast/sortdata.txt
+src/content/pt/screencast/categorias.txt: $(articles.pt.screencast.sortdata)
+src/content/pt/screencast/index.htmllisting src/content/pt/screencast/categorias.htmllisting: $(articles.pt.screencast.indexentry)
+src/content/pt/screencast/categorias.txt: $(articles.pt.screencast.categorydata)
+src/content/pt/screencast/categorias.txt src/content/pt/screencast/feed.xml: $(articles.pt.screencast.feedentry)
+src/content/pt/screencast/categorias.htmllisting src/content/pt/screencast/categorias.xml: src/content/pt/screencast/categorias.txt
+
+src/content/de/index.htmllisting: src/content/de/blog/sortdata.txt $(articles.de.blog.homeentry) src/content/de/folien/sortdata.txt $(articles.de.folien.homeentry) src/content/de/hhg/sortdata.txt $(articles.de.hhg.homeentry) src/content/de/pastebin/sortdata.txt $(articles.de.pastebin.homeentry) src/content/de/podcast/sortdata.txt $(articles.de.podcast.homeentry) src/content/de/screencast/sortdata.txt $(articles.de.screencast.homeentry)
+
+src/content/en/index.htmllisting: src/content/en/blog/sortdata.txt $(articles.en.blog.homeentry) src/content/en/pastebin/sortdata.txt $(articles.en.pastebin.homeentry) src/content/en/podcast/sortdata.txt $(articles.en.podcast.homeentry) src/content/en/screencast/sortdata.txt $(articles.en.screencast.homeentry) src/content/en/slide/sortdata.txt $(articles.en.slide.homeentry) src/content/en/til/sortdata.txt $(articles.en.til.homeentry)
+
+src/content/eo/index.htmllisting: src/content/eo/blog/sortdata.txt $(articles.eo.blog.homeentry) src/content/eo/hml/sortdata.txt $(articles.eo.hml.homeentry) src/content/eo/lumbildoj/sortdata.txt $(articles.eo.lumbildoj.homeentry) src/content/eo/pastebin/sortdata.txt $(articles.eo.pastebin.homeentry) src/content/eo/podkasto/sortdata.txt $(articles.eo.podkasto.homeentry) src/content/eo/screencast/sortdata.txt $(articles.eo.screencast.homeentry)
+
+src/content/es/index.htmllisting: src/content/es/blog/sortdata.txt $(articles.es.blog.homeentry) src/content/es/diapositivas/sortdata.txt $(articles.es.diapositivas.homeentry) src/content/es/haa/sortdata.txt $(articles.es.haa.homeentry) src/content/es/pastebin/sortdata.txt $(articles.es.pastebin.homeentry) src/content/es/podcast/sortdata.txt $(articles.es.podcast.homeentry) src/content/es/screencast/sortdata.txt $(articles.es.screencast.homeentry)
+
+src/content/fr/index.htmllisting: src/content/fr/aja/sortdata.txt $(articles.fr.aja.homeentry) src/content/fr/blog/sortdata.txt $(articles.fr.blog.homeentry) src/content/fr/diapositives/sortdata.txt $(articles.fr.diapositives.homeentry) src/content/fr/pastebin/sortdata.txt $(articles.fr.pastebin.homeentry) src/content/fr/podcast/sortdata.txt $(articles.fr.podcast.homeentry) src/content/fr/screencast/sortdata.txt $(articles.fr.screencast.homeentry)
+
+src/content/pt/index.htmllisting: src/content/pt/blog/sortdata.txt $(articles.pt.blog.homeentry) src/content/pt/eslaides/sortdata.txt $(articles.pt.eslaides.homeentry) src/content/pt/hea/sortdata.txt $(articles.pt.hea.homeentry) src/content/pt/pastebin/sortdata.txt $(articles.pt.pastebin.homeentry) src/content/pt/podcast/sortdata.txt $(articles.pt.podcast.homeentry) src/content/pt/screencast/sortdata.txt $(articles.pt.screencast.homeentry)
+
+
+translations.map = src/translations.map
+
+src/translations.map: deps.mk
+ printf '%s\n' \
+ 'src/content/en/index.adoc src/content/pt/index.adoc src/content/fr/index.adoc src/content/eo/index.adoc src/content/es/index.adoc src/content/de/index.adoc' \
+ 'src/content/en/about.adoc src/content/pt/sobre.adoc src/content/fr/a-propos.adoc src/content/eo/pri.adoc src/content/es/acerca.adoc src/content/de/ueber.adoc' \
+ 'src/content/en/music.adoc src/content/pt/musica.adoc src/content/fr/musique.adoc src/content/eo/muziko.adoc src/content/es/musica.adoc src/content/de/musik.adoc' \
+ 'src/content/en/blog/index.adoc src/content/pt/blog/index.adoc src/content/fr/blog/index.adoc src/content/eo/blog/index.adoc src/content/es/blog/index.adoc src/content/de/blog/index.adoc' \
+ 'src/content/en/blog/categories.adoc src/content/pt/blog/categorias.adoc src/content/fr/blog/categories.adoc src/content/eo/blog/kategorioj.adoc src/content/es/blog/categorias.adoc src/content/de/blog/kategorien.adoc' \
+ 'src/content/en/til/index.adoc src/content/pt/hea/index.adoc src/content/fr/aja/index.adoc src/content/eo/hml/index.adoc src/content/es/haa/index.adoc src/content/de/hhg/index.adoc' \
+ 'src/content/en/til/categories.adoc src/content/pt/hea/categorias.adoc src/content/fr/aja/categories.adoc src/content/eo/hml/kategorioj.adoc src/content/es/haa/categorias.adoc src/content/de/hhg/kategorien.adoc' \
+ 'src/content/en/pastebin/index.adoc src/content/pt/pastebin/index.adoc src/content/fr/pastebin/index.adoc src/content/eo/pastebin/index.adoc src/content/es/pastebin/index.adoc src/content/de/pastebin/index.adoc' \
+ 'src/content/en/pastebin/categories.adoc src/content/pt/pastebin/categorias.adoc src/content/fr/pastebin/categories.adoc src/content/eo/pastebin/kategorioj.adoc src/content/es/pastebin/categorias.adoc src/content/de/pastebin/kategorien.adoc' \
+ 'src/content/en/podcast/index.adoc src/content/pt/podcast/index.adoc src/content/fr/podcast/index.adoc src/content/eo/podkasto/index.adoc src/content/es/podcast/index.adoc src/content/de/podcast/index.adoc' \
+ 'src/content/en/podcast/categories.adoc src/content/pt/podcast/categorias.adoc src/content/fr/podcast/categories.adoc src/content/eo/podkasto/kategorioj.adoc src/content/es/podcast/categorias.adoc src/content/de/podcast/kategorien.adoc' \
+ 'src/content/en/screencast/index.adoc src/content/pt/screencast/index.adoc src/content/fr/screencast/index.adoc src/content/eo/screencast/index.adoc src/content/es/screencast/index.adoc src/content/de/screencast/index.adoc' \
+ 'src/content/en/screencast/categories.adoc src/content/pt/screencast/categorias.adoc src/content/fr/screencast/categories.adoc src/content/eo/screencast/kategorioj.adoc src/content/es/screencast/categorias.adoc src/content/de/screencast/kategorien.adoc' \
+ 'src/content/en/slide/index.adoc src/content/pt/eslaides/index.adoc src/content/fr/diapositives/index.adoc src/content/eo/lumbildoj/index.adoc src/content/es/diapositivas/index.adoc src/content/de/folien/index.adoc' \
+ 'src/content/en/slide/categories.adoc src/content/pt/eslaides/categorias.adoc src/content/fr/diapositives/categories.adoc src/content/eo/lumbildoj/kategorioj.adoc src/content/es/diapositivas/categorias.adoc src/content/de/folien/kategorien.adoc' \
+ 'src/content/en/til/2020/08/12/filename-timestamp.adoc src/content/pt/hea/2020/08/12/arquivo-datado.adoc' \
+ > $@
+
+
+## mkwb canonical site rules. Sites consume this content by
+## inlining it at the end of their generated deps.mk: mkdeps.sh
+## runs `mkwb rules` (which cats this file) so deps.mk ends up
+## self-contained -- no make-time `include` of an external file.
+## The site Makefile only needs an `all:` anchor followed by
+## `include deps.mk`. Order matters: `all:` must come first
+## (otherwise deps.mk's first target becomes the default), and
+## the canonical rules below must follow deps.mk's variable
+## assignments so that `all: $(derived-assets)` can resolve its
+## prereq list at parse time.
+##
+## The including Makefile is expected to define at minimum:
+##
+## NAME -- site name (used for SRCDIR/HTMLDIR install paths)
+## VERSION
+## DATE
+##
+## To override any of PREFIX / BINDIR / SHAREDIR / SRCDIR /
+## HTMLDIR / PUBURL / BASEURL / FFMFLAGS, set them AFTER the
+## include (plain `=`, POSIX -- no `?=` conditional assignment),
+## or pass `-e` to make to let env vars take precedence.
+
+
+
+## Default values. Sites override AFTER the include or via env.
+PREFIX = /usr
+BINDIR = $(PREFIX)/bin
+LIBDIR = $(PREFIX)/lib
+INCLUDEDIR = $(PREFIX)/include
+SHAREDIR = $(PREFIX)/share
+LOCALEDIR = $(SHAREDIR)/locale
+MANDIR = $(SHAREDIR)/man
+DOCDIR = $(SHAREDIR)/doc/$(NAME)
+HTMLDIR = $(SHAREDIR)/html/$(NAME)
+SRCDIR = $(PREFIX)/src/$(NAME)
+DESTDIR =
+## How many of each collection a front page lists.
+HOMECOUNT = 3
+PUBURL = public.asc.txt
+BASEURL = /
+FFMFLAGS = -y -hide_banner -loglevel warning
+
+
+
+.SUFFIXES:
+.SUFFIXES: .adoc .json .snippets .indexentry .feedentry .mapentry .sortdata .xml
+.SUFFIXES: .htmlbody .htmlheader .htmlfooter .htmllisting .html .links .caslinks
+.SUFFIXES: .comments .homeentry
+.SUFFIXES: .txt .categorydata .torrent .flac .ogg .ps .pdf .sentinel
+.SUFFIXES: .ly .midi .eslaides
+
+.adoc.json:
+ mkwb conf src/global.json $< > $@
+
+.adoc.htmlbody:
+ mkwb htmlbody $< > $@
+
+.htmlbody.html:
+ mkwb html $< > $@
+
+.json.htmlheader:
+ mkwb html -H $< > $@
+
+.json.htmlfooter:
+ mkwb html -F $< > $@
+
+.adoc.snippets:
+ mkwb snippets $< > $@
+
+.json.indexentry:
+ mkwb indexentry $< > $@
+
+## The same entry, linked from the root of the language rather
+## than the root of the collection, for a front page.
+.json.homeentry:
+ mkwb indexentry -l $< > $@
+
+.htmlbody.feedentry:
+ mkwb feedentry $< > $@
+
+.json.comments:
+ mkwb comments $< > $@
+
+.json.mapentry:
+ mkwb mapentry $< > $@
+
+.json.sortdata:
+ mkwb sortdata $< > $@
+
+.json.categorydata:
+ mkwb categorydata $< > $@
+
+.adoc.links:
+ mkwb links $< > $@
+
+.links.caslinks:
+ mkwb caslinks $< > $@
+
+.flac.ogg:
+ ffmpeg $(FFMFLAGS) -i $< -ar 48000 -vn -c:a libvorbis -b:a 320k $@
+
+.eslaides.ps:
+ eslaides < $< > $@
+
+.ly.midi:
+ lilypond -o $* $<
+
+.ps.pdf:
+ ps2pdf - < $< > $@
+
+
+
+## Derived file lists from auto-generated source lists in deps.mk.
+listings.adoc = $(categories.adoc) $(indexes.adoc) \
+ $(homes.adoc)
+homes.htmllisting = $(homes.adoc:.adoc=.htmllisting)
+sources.adoc = $(articles.adoc) $(listings.adoc) $(pages.adoc)
+sources.htmlbody = $(sources.adoc:.adoc=.htmlbody)
+sources.html = $(sources.adoc:.adoc=.html)
+sources.snippets = $(sources.adoc:.adoc=.snippets)
+sources.json = $(sources.adoc:.adoc=.json)
+sources.links = $(sources.adoc:.adoc=.links)
+sources.caslinks = $(sources.adoc:.adoc=.caslinks)
+sources.comments = $(sources.adoc:.adoc=.comments)
+sources.mapentry = $(sources.adoc:.adoc=.mapentry)
+articles.indexentry = $(articles.adoc:.adoc=.indexentry)
+articles.homeentry = $(articles.adoc:.adoc=.homeentry)
+articles.feedentry = $(articles.adoc:.adoc=.feedentry)
+articles.sortdata = $(articles.adoc:.adoc=.sortdata)
+articles.categorydata = $(articles.adoc:.adoc=.categorydata)
+listings.htmlheader = $(listings.adoc:.adoc=.htmlheader)
+listings.htmlfooter = $(listings.adoc:.adoc=.htmlfooter)
+listings.html = $(listings.adoc:.adoc=.html)
+indexes.htmllisting = $(indexes.adoc:.adoc=.htmllisting)
+categories.htmllisting = $(categories.adoc:.adoc=.htmllisting)
+categories.txt = $(categories.adoc:.adoc=.txt)
+categories.xml = $(categories.adoc:.adoc=.xml)
+sources.media.torrent = $(sources.media:=.torrent)
+music.midi = $(music.ly:.ly=.midi)
+music.pdf = $(music.ly:.ly=.pdf)
+slides.ps = $(slides.eslaides:.eslaides=.ps)
+slides.pdf = $(slides.eslaides:.eslaides=.pdf)
+
+sources = \
+ $(sources.adoc) \
+ $(sources.extras) \
+ $(images.svg) \
+ src/content/favicon.ico \
+ src/content/favicon.png \
+ src/content/style.css \
+ src/content/$(PUBURL) \
+
+dynamic-contents = \
+ $(sources.html) \
+ $(slides.pdf) \
+ $(feeds.xml) \
+ $(sources.media.torrent) \
+ src/content/sitemap.xml \
+
+static-contents = \
+ $(sources.extras) \
+ $(music.ly) \
+ $(music.include) \
+ $(music.ogg) \
+ $(music.midi) \
+ $(music.pdf) \
+ $(images.svg) \
+ src/content/favicon.ico \
+ src/content/favicon.png \
+ src/content/style.css \
+ src/content/$(PUBURL) \
+ src/content/.well-known/security.txt \
+
+contents = \
+ $(dynamic-contents) \
+ $(static-contents) \
+
+all-filelists = \
+ $(sources.snippets) \
+ $(categories.xml) \
+
+all-contents = \
+ $(contents) \
+
+captured-assets = \
+ src/content/$(PUBURL) \
+ src/content/favicon.ico \
+ src/content/favicon.png \
+
+captured-assets.sentinel = $(captured-assets:=.sentinel)
+
+derived-assets = \
+ $(dynamic-contents) \
+ $(music.midi) \
+ $(music.pdf) \
+ $(sources.html) \
+ $(sources.htmlbody) \
+ $(sources.snippets) \
+ $(sources.json) \
+ $(sources.links) \
+ $(sources.caslinks) \
+ $(sources.comments) \
+ src/comments.stamp \
+ $(sources.mapentry) \
+ src/dyn.conf \
+ src/base.conf \
+ src/global.json \
+ $(translations.map) \
+ $(articles.indexentry) \
+ $(articles.homeentry) \
+ $(articles.feedentry) \
+ $(articles.sortdata) \
+ $(articles.categorydata) \
+ $(listings.htmlheader) \
+ $(listings.htmlfooter) \
+ $(listings.html) \
+ $(indexes.htmllisting) \
+ $(categories.htmllisting) \
+ $(homes.htmllisting) \
+ $(categories.txt) \
+ $(categories.xml) \
+ $(slides.ps) \
+ $(slides.pdf) \
+ email.txt \
+ baseurl.txt \
+ fingerprint.txt \
+ expiry.txt \
+ expiry-epoch.txt \
+ now.txt \
+ src/content/.well-known/security.txt \
+ src/all-contents.txt \
+ src/all-filelists.txt \
+ src/all-symlinks.txt \
+ src/install.txt \
+ src/sort-expected.txt \
+ src/sort-given.txt \
+ src/sources.txt \
+ install.txt \
+ sources.txt \
+ src/content/.gitignore \
+ $(captured-assets.sentinel) \
+
+side-assets = \
+ src/collections/*/*/*/*/*/*.html.*.txt \
+ src/collections/*/*/index.html.*.txt \
+ src/collections/*/*/sortdata.txt \
+ src/collections/*/*/feed.*.xml \
+ src/collections/*/*/*.sortdata \
+ src/pages/*/*.html.*.txt \
+ src/content/.well-known/ \
+ src/comments/ \
+ $$(cat src/all-symlinks.txt 2>/dev/null) \
+
+
+
+## Default target -- builds all derived + captured artifacts.
+## $(static-contents) is named too: most of it the site ships, but
+## the fallback rules below craft what it doesn't (style.css, the
+## favicon), and nothing else asks for those now that no .gz twin
+## does.
+all: $(derived-assets) $(captured-assets) $(static-contents)
+
+
+## A page's configuration names the same page in the other
+## languages, so it is built after the map that says which those
+## are. A site with no translations sets no map, and the variable
+## is empty.
+$(sources.json): src/global.json $(translations.map)
+
+
+## Optional inputs default to empty / stub when the site doesn't
+## ship them, so make doesn't bail on a missing prerequisite.
+src/symlinks.txt:
+ : > $@
+
+## The list archive as the list exports it, and the messages split
+## out of it. One file is what gets committed; make cuts it up.
+## The names are not known in advance, so a stamp stands for them.
+src/comments.mbox:
+ : > $@
+
+src/comments.stamp: src/comments.mbox
+ mkwb mbox src/comments < src/comments.mbox > /dev/null
+ touch $@
+
+$(sources.comments): src/comments.stamp src/comments.map
+
+## A page renders the thread belonging to it, so it is built after
+## the whole archive is: a message arriving anywhere can be the one
+## that belongs here.
+$(sources.html): $(sources.comments)
+
+src/content/style.css:
+ mkdir -p $(@D)
+ : > $@
+
+src/content/img/favicon.svg:
+ mkdir -p $(@D)
+ printf '%s\n' \
+ '<?xml version="1.0" encoding="UTF-8"?>' \
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">' \
+ ' <rect width="16" height="16" fill="#888"/>' \
+ '</svg>' > $@
+
+src/content/.gitignore: src/symlinks.txt
+ cd src/content/ && mkwb symlinks ../symlinks.txt > $(@F)
+
+src/dyn.conf: email.txt baseurl.txt fingerprint.txt
+ printf "export url_pre='%s'\n" "$$(cat baseurl.txt)" > $@
+ printf "export email='%s'\n" "$$(cat email.txt)" >> $@
+ printf "export publickey='%s'\n" "$$(cat fingerprint.txt)" >> $@
+ printf "export publickey_url='$(PUBURL)'\n" >> $@
+ printf 'export sourcecode_url="$$url_pre/git/$(NAME)"\n' >> $@
+ ## Expose the site's LANGUAGES so mkwb conf can decide
+ ## whether to path-derive lang (multi-lang sites with
+ ## src/content/<lang>/...) or use empty (flat / single-
+ ## idiom sites with src/content/X.adoc directly). Skip
+ ## the var entirely when empty so flat-layout sites' confs
+ ## stay clean -- mkwb conf treats unset and empty the same.
+ if [ -n '$(LANGUAGES)' ]; then \
+ printf "export languages='%s'\n" '$(LANGUAGES)' >> $@; \
+ fi
+ ## Defaults for globals previously required in every site's
+ ## static.conf. base.conf concatenates dyn.conf then
+ ## static.conf, so anything the site sets in static.conf
+ ## overrides these (shell honours the last export). Keeping
+ ## them defined lets a content-only site with no static.conf
+ ## still build, and protects downstream consumers under set -u
+ ## from undefined vars.
+ printf 'export site_name=$(NAME)\n' >> $@
+ printf 'export author=$(NAME)\n' >> $@
+ printf 'export headers_dir=src/headers\n' >> $@
+ printf 'export names_dir=src/names\n' >> $@
+ printf 'export comments_dir=src/comments\n' >> $@
+ printf 'export comments_map=src/comments.map\n' >> $@
+ printf 'export me_urls=\n' >> $@
+ printf 'export translations_map=$(translations.map)\n' >> $@
+ printf 'export root_dir=src/content\n' >> $@
+ printf 'export feed_title=""\n' >> $@
+ printf 'export feed_url=""\n' >> $@
+ printf 'export feed_alternate_url=""\n' >> $@
+ printf 'export list_addr=""\n' >> $@
+ printf 'export discussions_url_prefix=""\n' >> $@
+ printf 'export sourcecode_url_prefix=""\n' >> $@
+
+## src/static.conf is optional: when the site doesn't ship one,
+## dyn.conf's defaults (above) suffice. Default to empty so
+## `cat dyn.conf static.conf > base.conf` doesn't fail.
+src/static.conf:
+ : > $@
+
+src/base.conf: src/dyn.conf src/static.conf
+ cat src/dyn.conf src/static.conf > $@
+
+src/global.json: src/base.conf
+ mkwb conf -G src/base.conf > $@
+
+## POSIX make defines $* only for inference (suffix) rules. These
+## explicit rules derive their stem from $@ via shell instead.
+$(listings.html):
+ stem=$$(echo $@ | sed 's/\.html$$//'); \
+ cat $$stem.htmlheader $$stem.htmlbody \
+ $$stem.htmllisting $$stem.htmlfooter > $@
+
+$(indexes.htmllisting):
+ stem=$$(echo $@ | sed 's/\.htmllisting$$//'); \
+ mkwb indexbody $$stem.json > $@
+
+## A language's front page: each collection the navigation lists,
+## with the newest $(HOMECOUNT) of it.
+$(homes.htmllisting):
+ stem=$$(echo $@ | sed 's/\.htmllisting$$//'); \
+ mkwb homebody -c $(HOMECOUNT) $$stem.json > $@
+
+$(categories.htmllisting):
+ stem=$$(echo $@ | sed 's/\.htmllisting$$//'); \
+ mkwb categoriesbody $$stem.json > $@
+
+$(categories.txt): src/global.json
+ mkwb categories src/global.json $(@D) > $@
+
+$(categories.xml): src/global.json
+ stem=$$(echo $@ | sed 's/\.xml$$//'); \
+ for f in $$(cat $$stem.txt); do \
+ c="$$(printf '%s\n' "$$f" | cut -d. -f2)"; \
+ mkwb feed src/global.json "$$f" \
+ $(@D)/feed."$$c".xml > $(@D)/feed."$$c".xml; \
+ printf '%s\n' $(@D)/feed."$$c".xml; \
+ done > $@
+
+$(feeds.xml): src/global.json
+ mkwb feed src/global.json $(@D)/sortdata.txt $@ > $@
+
+src/content/$(PUBURL):
+ mkdir -p $(@D)
+ : > $@
+src/content/$(PUBURL).sentinel: email.txt src/content/$(PUBURL)
+ out=$$(echo $@ | sed 's/\.sentinel$$//'); \
+ gpg --export --armour "$$(cat email.txt)" | ifne ifnew "$$out"
+ touch $@
+
+src/content/favicon.ico.sentinel: src/content/img/favicon.svg
+ out=$$(echo $@ | sed 's/\.sentinel$$//'); \
+ convert src/content/img/favicon.svg -strip ico:- | ifnew "$$out"
+ touch $@
+
+src/content/favicon.png.sentinel: src/content/img/favicon.svg
+ out=$$(echo $@ | sed 's/\.sentinel$$//'); \
+ convert src/content/img/favicon.svg -strip png:- | ifnew "$$out"
+ touch $@
+
+$(sources.media.torrent):
+ src=$$(echo $@ | sed 's/\.torrent$$//'); \
+ rel=$$(echo "$$src" | cut -d/ -f3-); \
+ base=$$(basename "$$src"); \
+ mktorrent -xfd -n "$$base" -o $@ \
+ -w "https://$(NAME)$(BASEURL)$$rel" "$$src"
+
+src/content/sitemap.xml: $(sources.mapentry)
+ mkwb sitemap $(sources.mapentry) > $@
+
+email.txt: meta.capim
+ cat meta.capim | awk '$$1 == ":email" && $$0=$$2' | tr -d '"' > $@
+
+baseurl.txt: meta.capim
+ cat meta.capim | awk '$$1 == ":baseurl" && $$0=$$2' | tr -d '"' > $@
+
+## fingerprint.txt / expiry.txt tolerate a missing public key: if
+## src/content/$(PUBURL) is empty (no key was exported), fall back
+## to an empty fingerprint and a fixed far-future expiry (2099-12-31
+## UTC, epoch 4102444800). check-unit-expiry stays meaningful
+## because expiry > now holds; security.txt stays valid (RFC 9116
+## requires an Expires line).
+fingerprint.txt: src/content/$(PUBURL)
+ if [ -s $< ]; then \
+ gpg --always-trust --no-keyring --show-key --with-colons \
+ $< | \
+ awk -F: '/^pub:/ { print $$5 }' > $@; \
+ else \
+ : > $@; \
+ fi
+
+expiry.txt: src/content/$(PUBURL)
+ if [ -s $< ]; then \
+ gpg --always-trust --no-keyring --show-key --with-colons \
+ $< | \
+ awk -F: '/^pub:/ { print $$7 }' | \
+ xargs -I% date -Is -d@% > $@; \
+ else \
+ date -Is -d '@4102444800' > $@; \
+ fi
+
+expiry-epoch.txt: expiry.txt
+ date -d "$$(cat expiry.txt)" '+%s' > $@
+
+now.txt:
+ now > $@
+
+src/content/.well-known/security.txt: email.txt baseurl.txt expiry.txt \
+ src/content/$(PUBURL)
+ mkdir -p $(@D)
+ printf 'Contact: mailto:%s\n' "$$(cat email.txt)" > $@
+ printf 'Expires: %s\n' "$$(cat expiry.txt)" >> $@
+ if [ -s src/content/$(PUBURL) ]; then \
+ printf 'Encryption: %s/$(PUBURL)\n' "$$(cat baseurl.txt)" >> $@; \
+ fi
+ printf 'Preferred-Languages: en, pt, fr, eo, es, de\n' >> $@
+
+src/sources.txt:
+ printf '%s\n' $(sources) > $@
+
+src/all-contents.txt:
+ printf '%s\n' $(all-contents) > $@
+
+src/all-filelists.txt: src/all-symlinks.txt
+ printf '%s\n' $(all-filelists) src/all-symlinks.txt > $@
+
+src/all-symlinks.txt: src/content/.gitignore
+ sed 's|^|src/content|' src/content/.gitignore > $@
+
+src/install.txt: src/all-contents.txt src/all-filelists.txt $(all-filelists)
+ cat src/all-contents.txt $$(cat src/all-filelists.txt) > $@
+
+sources.txt: src/sources.txt
+install.txt: src/install.txt
+sources.txt install.txt:
+ sed 's|^src/content/||' src/$(@F) > $@
+
+
+
+## ---- check-unit-* targets ---------------------------------------
+
+src/sort-expected.txt:
+ if [ -n "$(articles.adoc)" ]; then \
+ dirname $(articles.adoc) | env LANG=POSIX.UTF-8 sort | \
+ uniq -c | \
+ awk '{ printf "%s\t%s\n", $$2, $$1 }'; \
+ fi > $@
+
+src/sort-given.txt: $(sources.json) src/sort-expected.txt
+ awk '{ \
+ "grep \"\\\"sort\\\":\" " $$1 "/*.json | wc -l" | \
+ getline cnt; \
+ printf "%s\t%s\n", $$1, cnt; \
+ }' src/sort-expected.txt > $@
+
+check-unit-sorting: src/sort-expected.txt src/sort-given.txt
+ diff -U10 src/sort-expected.txt src/sort-given.txt
+
+
+.SUFFIXES: .updatedat-check
+sources.updatedat-check = $(sources.adoc:.adoc=.updatedat-check)
+$(sources.updatedat-check):
+ stem=$$(echo $@ | sed 's/\.updatedat-check$$//'); \
+ u=$$(mkwb get updatedat_epoch $$stem.json); \
+ d=$$(mkwb get date_epoch $$stem.json); \
+ if [ -n "$$u" ] && [ "$$u" -le "$$d" ]; then exit 3; fi
+
+check-unit-updatedat: $(sources.updatedat-check)
+
+
+.SUFFIXES: .links-internal-check
+sources.links-internal-check = $(sources.adoc:.adoc=.links-internal-check)
+$(sources.links-internal-check): $(sources.html) $(slides.pdf)
+ stem=$$(echo $@ | sed 's/\.links-internal-check$$//'); \
+ dir=$$(dirname "$$stem"); \
+ grep -E '^(link|image):' $$stem.links | cut -d: -f2- | \
+ xargs -I% test -e "$$dir"/%
+
+check-unit-links-internal: $(sources.links-internal-check)
+
+
+check-unit-links-external:
+
+
+## Every page carries a title. A source the transformer could not
+## read one from -- a deck in another format, say -- renders one
+## that is empty, and nothing downstream notices.
+.SUFFIXES: .title-check
+sources.title-check = $(sources.adoc:.adoc=.title-check)
+$(sources.title-check): $(sources.html)
+ stem=$$(echo $@ | sed 's/\.title-check$$//'); \
+ grep -q '<title>[^<]' $$stem.html || { \
+ printf '%s: rendered with an empty title\n' "$$stem.html" >&2; \
+ exit 1; \
+ }
+
+check-unit-titles: $(sources.title-check)
+
+
+## A listing block that renders empty means the transformer lost
+## the code it was handed, and usually the prose after it too.
+.SUFFIXES: .listing-check
+sources.listing-check = $(sources.adoc:.adoc=.listing-check)
+$(sources.listing-check): $(sources.html)
+ stem=$$(echo $@ | sed 's/\.listing-check$$//'); \
+ if grep -q '<div class="content"></div>' $$stem.html; then \
+ printf '%s: a listing block rendered empty\n' "$$stem.html" >&2; \
+ exit 1; \
+ fi
+
+check-unit-listings: $(sources.listing-check)
+
+
+## check-unit-links-internal sees only the link: and image: macros
+## a page wrote. This sees what the templates emitted around it:
+## the feed, the stylesheet, the icons, the navigation.
+.SUFFIXES: .links-html-check
+sources.links-html-check = $(sources.adoc:.adoc=.links-html-check)
+$(sources.links-html-check): $(sources.html)
+ stem=$$(echo $@ | sed 's/\.links-html-check$$//'); \
+ dir=$$(dirname "$$stem"); \
+ missing=$$(grep -oE '(href|src)="[^"#?:]+"' $$stem.html | \
+ sed -e 's/.*="//' -e 's/"$$//' | \
+ while read -r h; do \
+ case "$$h" in /*|'') continue;; esac; \
+ test -e "$$dir/$$h" || printf '%s ' "$$h"; \
+ done); \
+ if [ -n "$$missing" ]; then \
+ printf '%s: unresolved: %s\n' "$$stem.html" "$$missing" >&2; \
+ exit 1; \
+ fi
+
+check-unit-links-html: $(sources.links-html-check)
+
+
+## Atom gives every feed a distinct id. A site that pins one feed
+## URL for the whole build publishes several feeds claiming to be
+## the same one.
+check-unit-feed-ids: $(feeds.xml) $(categories.xml)
+ dupes=$$(grep -h 'rel="self"' \
+ $$(find src/content -name 'feed*.xml' -type f) | \
+ sort | uniq -d); \
+ if [ -n "$$dupes" ]; then \
+ printf 'feeds sharing an id:\n%s\n' "$$dupes" >&2; \
+ exit 1; \
+ fi
+
+
+## Only meaningful where content is partitioned by language: the
+## directory a page sits in is what it should say it speaks.
+check-unit-lang: $(sources.html)
+ if [ -z '$(LANGUAGES)' ]; then exit 0; fi; \
+ bad=''; \
+ for f in $(sources.html); do \
+ l=$$(printf '%s\n' "$${f#src/content/}" | cut -d/ -f1); \
+ case "$$l" in ??) ;; *) continue;; esac; \
+ d=$$(grep -m1 -o '<html lang="[^"]*"' "$$f" | \
+ sed -e 's/.*="//' -e 's/"//'); \
+ if [ "$$d" != "$$l" ]; then bad="$$bad $$f($$d)"; fi; \
+ done; \
+ if [ -n "$$bad" ]; then \
+ printf 'pages declaring a language their path does not:%s\n' \
+ "$$bad" >&2; \
+ exit 1; \
+ fi
+
+
+## po4a writes a translation only when it has one, and a stale
+## fuzzy flag is enough to skip a page without saying so.
+check-unit-i18n:
+ if [ ! -f po/po4a.cfg ]; then exit 0; fi; \
+ missing=$$(grep -oE '[a-z][a-z]:src/content/[^[:space:]]+' \
+ po/po4a.cfg | \
+ cut -d: -f2 | \
+ while read -r t; do \
+ test -e "$$t" || printf '%s ' "$$t"; \
+ done); \
+ if [ -n "$$missing" ]; then \
+ printf 'declared in po4a.cfg, never generated: %s\n' \
+ "$$missing" >&2; \
+ exit 1; \
+ fi
+
+
+## "clean" removes what the current deps.mk lists, so an artifact
+## whose source was renamed or deleted stays behind for good --
+## and gitignored, so no status command shows it either.
+##
+## Three rules cover everything whose name says what produced it:
+## nearly every artifact is <source>.<ext>, a code sidecar is
+## <page>.html.<n>.txt, and a torrent or a sentinel is named for
+## the file it describes. What is left out is derived from a
+## directory rather than from one named file -- the dated sortdata
+## markers, the feeds, the collection lists -- and so carries no
+## name to check a source against.
+check-unit-orphans: $(derived-assets)
+ orphans=$$(find src/content -type f | \
+ while read -r f; do \
+ case "$$f" in \
+ *.html.*.txt) \
+ [ -e "$${f%.html.*.txt}.adoc" ] || \
+ printf '%s ' "$$f";; \
+ *.torrent|*.sentinel) \
+ [ -e "$${f%.*}" ] || printf '%s ' "$$f";; \
+ *.htmlbody|*.json|*.snippets|*.links|*.caslinks|\
+ *.html|*.indexentry|*.feedentry|*.mapentry|\
+ *.categorydata|*.htmlheader|*.htmlfooter|\
+ *.htmllisting|*.ps|*.pdf|*.midi) \
+ s=$${f%.*}; \
+ [ -e "$$s.adoc" ] || [ -e "$$s.eslaides" ] || \
+ [ -e "$$s.ly" ] || printf '%s ' "$$f";; \
+ esac; \
+ done); \
+ if [ -n "$$orphans" ]; then \
+ printf 'built artifacts whose source is gone: %s\n' \
+ "$$orphans" >&2; \
+ exit 1; \
+ fi
+
+
+
+symlink-deps = \
+ $(sources.html) \
+ $(feeds.xml) \
+ $(categories.xml) \
+ src/content/.well-known/security.txt \
+ $(sources.media.torrent) \
+
+## A symlink naming a FILE.gz is satisfied by FILE: the twin is
+## the packager's to craft over the installed tree, not this
+## build's -- see gzipdir(1). Every other link has to resolve
+## here and now.
+check-unit-links-symlinks: src/all-symlinks.txt $(symlink-deps)
+ dangling=''; \
+ for l in $$(cat src/all-symlinks.txt); do \
+ if [ -e "$$l" ]; then continue; fi; \
+ t=$$(readlink "$$l"); \
+ case "$$t" in \
+ *.gz) test -e "$$(dirname "$$l")/$${t%.gz}" && \
+ continue;; \
+ esac; \
+ dangling="$$dangling $$l"; \
+ done; \
+ if [ -n "$$dangling" ]; then \
+ printf 'symlinks resolving to nothing:%s\n' \
+ "$$dangling" >&2; \
+ exit 1; \
+ fi
+
+
+check-unit-links: check-unit-links-internal check-unit-links-external
+check-unit-links: check-unit-links-symlinks check-unit-links-html
+
+
+MAXSIZE = 52428800 # from spec: https://www.sitemaps.org/protocol.html
+check-unit-sitemap-size: src/content/sitemap.xml
+ test "$$(stat --printf='%s' src/content/sitemap.xml)" -le $(MAXSIZE)
+
+check-unit-sitemap-count: src/content/sitemap.xml
+ test "$$(grep -cF '</url>' src/content/sitemap.xml)" -le 50000
+
+check-unit-sitemap: check-unit-sitemap-count check-unit-sitemap-size
+
+
+check-unit-expiry: expiry-epoch.txt now.txt
+ test "$$(cat expiry-epoch.txt)" -gt "$$(cat now.txt)"
+
+
+check-unit: check-unit-sorting check-unit-updatedat check-unit-links
+check-unit: check-unit-sitemap check-unit-expiry
+check-unit: check-unit-titles check-unit-listings check-unit-feed-ids
+check-unit: check-unit-lang check-unit-i18n check-unit-orphans
+
+
+## ---- standard housekeeping --------------------------------------
+
+## Remove all derived artifacts produced during the build.
+clean:
+ rm -rf $(derived-assets) $(side-assets)
+ ## The legacy-URL symlinks sit in directories mkwb symlinks had
+ ## to create; removing the links leaves those empty. Naming
+ ## them drifts the moment a link is added --- which it did ---
+ ## so let rmdir decide: git cannot track an empty directory, so
+ ## under the content root an empty one is always residue.
+ ## -depth walks bottom-up, which is what prunes a whole branch.
+ find src/content -depth -type d -exec rmdir {} + 2>/dev/null || true
+
+
+## Install into $(DESTDIR)$(PREFIX). A tar(1) pipe rather than
+## rsync(1): one fewer dependency outside the fleet, and the file
+## lists name exactly what to carry. Unlike rsync --delete it
+## adds without pruning -- an install over a tree still holding a
+## page whose source is gone leaves that page there. Install into
+## an empty $(DESTDIR), or uninstall first.
+install: all
+ mkdir -p '$(DESTDIR)$(HTMLDIR)' '$(DESTDIR)$(SRCDIR)'
+ tar -c -f - -C src/content $$(cat install.txt) | \
+ tar -x -f - -C '$(DESTDIR)$(HTMLDIR)'
+ tar -c -f - -C src/content $$(cat sources.txt) | \
+ tar -x -f - -C '$(DESTDIR)$(SRCDIR)'
+
+## Remove what install placed.
+uninstall:
+ rm -rf \
+ '$(DESTDIR)$(SRCDIR)' \
+ '$(DESTDIR)$(HTMLDIR)' \
+
+
+ALWAYS:
diff --git a/mkdeps.sh b/mkdeps.sh
index 9df3e5a..9ddf0ff 100755
--- a/mkdeps.sh
+++ b/mkdeps.sh
@@ -8,8 +8,17 @@ normalize() {
xargs realpath --relative-to=. | sort
}
+## A language's front page is excluded: it is a listing now, and
+## naming it here as well puts it in $(sources.adoc) twice.
pages() {
- find src/pages/*/*.adoc -type f | normalize
+ find src/pages/*/*.adoc -type f ! -name 'index.adoc' | normalize
+}
+
+## The front page of each language, which lists what is newest
+## across that language's collections rather than holding articles
+## of its own.
+homes() {
+ find src/pages/*/index.adoc -type f | normalize
}
articles() {
@@ -17,7 +26,7 @@ articles() {
}
slides() {
- find src/slides/*/*/ -type f -name '*.adoc' | normalize
+ find src/slides/*/*/ -type f -name '*.eslaides' | normalize
}
indexes() {
@@ -29,11 +38,19 @@ categories() {
}
media() {
- find src/content/ \( -type f -and \! -type l \) -and \( \
+ # src/content/music is excluded: those .ogg files are score
+ # renderings, not published media, and the old site never
+ # offered them over BitTorrent.
+ find src/content/ -path 'src/content/music' -prune -or \
+ \( \( -type f -and \! -type l \) -and \( \
-name '*.flac' -or \
-name '*.ogg' -or \
-name '*.webm' \
- \)
+ \) -print \)
+}
+
+music() {
+ find src/content/music/*.ly | normalize
}
tarballs() {
@@ -50,6 +67,29 @@ extras() {
listings() {
indexes
categories
+ homes
+}
+
+## Which pages are one another's translations. po4a already says
+## so, set by set, so read it there rather than keep a second list
+## that can quietly disagree with the first.
+po4a_sets() {
+ awk '
+ /^\[type: asciidoc\]/ { set = "" }
+ /^\[type: asciidoc\]/ || cont {
+ line = $0
+ sub(/^\[type: asciidoc\][ \t]*/, "", line)
+ cont = (line ~ /\\$/)
+ sub(/[ \t]*\\$/, "", line)
+ n = split(line, f, /[ \t]+/)
+ for (i = 1; i <= n; i++) {
+ if (f[i] == "" || f[i] ~ /^add_/) continue
+ sub(/^[a-z]+:/, "", f[i])
+ set = set (set == "" ? "" : " ") f[i]
+ }
+ if (!cont && set != "") { print set; set = "" }
+ }
+ ' po/po4a.cfg
}
files() {
@@ -60,43 +100,41 @@ files() {
pages | varlist 'pages.adoc'
+homes | varlist 'homes.adoc'
articles | varlist 'articles.adoc'
-slides | varlist 'slides.adoc'
+slides | varlist 'slides.eslaides'
categories | varlist 'categories.adoc'
indexes | varlist 'indexes.adoc'
indexes | sed 's|/index\.adoc$|/feed.xml|' | varlist 'feeds.xml'
-find src/content/img/ -name '*.svg' | varlist 'images.svg'
+find src/content/img/ -name '*.svg' | sort | varlist 'images.svg'
media | varlist 'sources.media'
tarballs | varlist 'sources.tarballs'
extras | varlist 'sources.extras'
find po/*.po po/*.pot | varlist 'sources.po'
+music | varlist 'music.ly'
+find src/content/music/*.ogg | varlist 'music.ogg'
+find src/content/music/*.ly.include | varlist 'music.include'
+# lilypond emits the score alongside the MIDI, from one run.
+music | sed 's/^\(.*\)\.ly$/\1.pdf:\t\1.midi/'
-{
- files | sed 's/^\(.*\)\.adoc$/\1.html/'
- files | sed 's/^\(.*\)\.adoc$/\1.snippets/'
- slides | sed 's/^\(.*\)\.adoc$/\1.pdf/'
- indexes | sed 's|^\(.*\)/index\.adoc$|\1/feed.xml|'
- media | sed 's/^\(.*\)$/\1.torrent/'
-} | sed 's/^\(.*\)$/\1.gz:\t\1/'
-printf '\n'
-files | sed 's/^\(.*\)\.adoc$/\1.htmlbody\t\1.snippets\t\1.conf:\t\1.adoc/'
-files | sed 's/^\(.*\)\.adoc$/\1.html:\t\1.conf\t\1.htmlbody/'
+files | sed 's/^\(.*\)\.adoc$/\1.htmlbody\t\1.snippets\t\1.json:\t\1.adoc/'
+files | sed 's/^\(.*\)\.adoc$/\1.html:\t\1.json\t\1.htmlbody/'
printf '\n'
-files | sed 's/^\(.*\)\.adoc$/\1.updatedat-check:\t\1.conf/'
+files | sed 's/^\(.*\)\.adoc$/\1.updatedat-check:\t\1.json/'
files | sed 's/^\(.*\)\.adoc$/\1.links-internal-check:\t\1.links/'
files | sed 's/^\(.*\)\.adoc$/\1.caslinks:\t\1.links/'
printf '\n'
-articles | sed 's/^\(.*\)\.adoc$/\1.feedentry:\t\1.conf\t\1.htmlbody/'
-articles | sed 's/^\(.*\)\.adoc$/\1.sortdata:\t\1.conf/'
-articles | sed 's/^\(.*\)\.adoc$/\1.categorydata:\t\1.conf/'
+articles | sed 's/^\(.*\)\.adoc$/\1.feedentry:\t\1.json\t\1.htmlbody/'
+articles | sed 's/^\(.*\)\.adoc$/\1.sortdata:\t\1.json/'
+articles | sed 's/^\(.*\)\.adoc$/\1.categorydata:\t\1.json/'
printf '\n'
-listings | sed 's/^\(.*\)\.adoc$/\1.htmlheader\t\1.htmlfooter:\t\1.conf/'
-listings | sed 's/^\(.*\)\.adoc$/\1.htmllisting:\t\1.conf/'
+listings | sed 's/^\(.*\)\.adoc$/\1.htmlheader\t\1.htmlfooter:\t\1.json/'
+listings | sed 's/^\(.*\)\.adoc$/\1.htmllisting:\t\1.json/'
listings | sed 's/^\(.*\)\.adoc$/\1.html:\t\1.htmlheader\t\1.htmlfooter/'
listings | sed 's/^\(.*\)\.adoc$/\1.html:\t\1.htmllisting\t\1.htmlbody/'
printf '\n'
@@ -104,8 +142,8 @@ printf '\n'
media | sed 's/^\(.*\)$/\1.torrent:\t\1/'
printf '\n'
-slides | sed 's/^\(.*\)\.adoc$/\1.ps:\t\1.adoc/'
-slides | sed 's/^\(.*\)\.adoc$/\1.pdf:\t\1.ps/'
+slides | sed 's/^\(.*\)\.eslaides$/\1.ps:\t\1.eslaides/'
+slides | sed 's/^\(.*\)\.eslaides$/\1.pdf:\t\1.ps/'
printf '\n'
@@ -120,11 +158,15 @@ collectionentries() {
printf '\n\n'
name="$(basename "$c")"
art=articles."$lang"
- find "$c"/*/ -type f -name '*.adoc' | varlist "$art.$name.adoc"
+ # A collection with no articles yet -- every non-English one --
+ # has no YYYY/ directories for the glob to match.
+ find "$c"/*/ -type f -name '*.adoc' 2>/dev/null |
+ varlist "$art.$name.adoc"
echo "$art.$name.sortdata = \$($art.$name.adoc:.adoc=.sortdata)"
echo "$art.$name.indexentry = \$($art.$name.adoc:.adoc=.indexentry)"
echo "$art.$name.feedentry = \$($art.$name.adoc:.adoc=.feedentry)"
echo "$art.$name.categorydata = \$($art.$name.adoc:.adoc=.categorydata)"
+ echo "$art.$name.homeentry = \$($art.$name.adoc:.adoc=.homeentry)"
printf '%s/sortdata.txt:\tdeps.mk\n' "$c"
printf '\tprintf %s $(%s.%s.sortdata) > $@\n\n' "'%s\n'" "$art" "$name"
@@ -154,8 +196,6 @@ collectionentries() {
printf '%s/%s.htmllisting\t' "$c" "$plural"
printf '%s/%s.xml:\t' "$c" "$plural"
printf '%s/%s.txt\n' "$c" "$plural"
-
- printf '%s/%s.xml.gz:\t%s/%s.xml\n' "$c" "$plural" "$c" "$plural"
}
@@ -164,3 +204,34 @@ for langlink in src/collections/*; do
collectionentries "$langlink" "$colllink"
done
done
+
+## A front page lists each collection, so it is rebuilt when any
+## of that language's collections gains an entry.
+printf '\n'
+for langlink in src/collections/*; do
+ lang="$(basename "$langlink")"
+ deps=
+ for colllink in "$langlink"/*; do
+ [ -d "$colllink" ] || continue
+ name="$(basename "$colllink")"
+ deps="$deps src/content/$lang/$name/sortdata.txt"
+ deps="$deps \$(articles.$lang.$name.homeentry)"
+ done
+ [ -n "$deps" ] || continue
+ printf 'src/content/%s/index.htmllisting:%s\n\n' "$lang" "$deps"
+done
+
+
+## The map mkwb conf reads to link a page to its translations,
+## written out here so it cannot drift from po4a.cfg.
+printf '\ntranslations.map = src/translations.map\n\n'
+printf 'src/translations.map: deps.mk\n'
+printf '\tprintf %s \\\n' "'%s\\n'"
+po4a_sets | sed "s/^/\t\t'/; s/\$/' \\\\/"
+printf '\t\t> $@\n\n'
+
+
+## The canonical rules, inlined so no include is needed
+## at make time.
+echo
+mkwb rules
diff --git a/po/de.po b/po/de.po
index 3524d73..ddfac3b 100644
--- a/po/de.po
+++ b/po/de.po
@@ -6,7 +6,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2025-05-01 19:02-0300\n"
+"POT-Creation-Date: 2026-08-27 14:39-0300\n"
"PO-Revision-Date: 2025-04-29 19:46-0300\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
@@ -19,90 +19,261 @@ msgstr ""
#. type: Plain text
#: src/headers/ref.txt:2
msgid "en/blog/\tBlog"
-msgstr ""
+msgstr "de/blog/\tBlog"
#. type: Plain text
#: src/headers/ref.txt:4
msgid "en/til/\tTIL"
-msgstr ""
+msgstr "de/hhg/\tHHG"
#. type: Plain text
#: src/headers/ref.txt:6
msgid "en/podcast/\tPodcasts"
-msgstr ""
+msgstr "de/podcast/\tPodcast"
#. type: Plain text
#: src/headers/ref.txt:8
msgid "en/screencast/\tScreencasts"
-msgstr ""
+msgstr "de/screencast/\tScreencasts"
#. type: Plain text
#: src/headers/ref.txt:10
msgid "en/pastebin/\tPastebins"
-msgstr ""
+msgstr "de/pastebin/\tPastebins"
#. type: Plain text
#: src/headers/ref.txt:12
msgid "en/slide/\tSlides"
-msgstr ""
+msgstr "de/folien/\tFolien"
#. type: Plain text
#: src/headers/ref.txt:13
msgid "en/about.html\tAbout"
-msgstr ""
+msgstr "de/ueber.html\tÜber"
#. type: Plain text
#: src/names/categories/ref.txt:1
msgid "categories"
-msgstr ""
+msgstr "kategorien"
#. type: Plain text
#: src/names/category/ref.txt:1
msgid "category"
-msgstr ""
+msgstr "kategorie"
+
+#. type: Plain text
+#: src/names/feed/ref.txt:1
+msgid "Atom feed"
+msgstr "Atom-Feed"
+
+#. type: Plain text
+#: src/names/tagline/ref.txt:1
+msgid "Robust software, for everybody."
+msgstr "Robuste Software, für alle."
+
+#. type: Plain text
+#: src/names/feedicon_alt/ref.txt:1
+msgid "The Atom feed icon"
+msgstr "Das Symbol des Atom-Feeds"
+
+#. type: Title =
+#: src/content/en/index.adoc:1
+#, no-wrap
+msgid "Home"
+msgstr "Startseite"
+
+#. type: Plain text
+#: src/content/en/index.adoc:3
+msgid "The newest of each collection is below."
+msgstr "Das Neueste aus jeder Sammlung steht unten."
#. type: Title =
#: src/content/en/about.adoc:1
#, no-wrap
msgid "About"
-msgstr ""
+msgstr "Über"
#. type: Plain text
-#: src/content/en/about.adoc:9
+#: src/content/en/about.adoc:17
msgid ""
-"Hi, I'm EuAndreh. I write software and, occasionally, music. You can find "
-"my contact information in the footer of this page, or mail my {mailto}"
-"[public inbox] ({archive}[archive])."
+"Hi, I'm EuAndreh -- André Miranda Moreira. I write software and, "
+"occasionally, music. You can find my contact information in the footer of "
+"this page, or mail my {mailto}[public inbox] ({archive}[archive])."
msgstr ""
+"Hallo, ich bin EuAndreh -- André Miranda Moreira. Ich schreibe Software und "
+"gelegentlich Musik. Meine Kontaktdaten stehen im Fußbereich dieser Seite, "
+"oder schreib an meinen {mailto}[öffentlichen Posteingang] ({archive}"
+"[Archiv])."
#. type: Plain text
-#: src/content/en/about.adoc:11
+#: src/content/en/about.adoc:20
msgid ""
"This is my personal website where I write articles, publish software and "
"more related work."
msgstr ""
+"Dies ist meine persönliche Website, auf der ich Artikel schreibe, Software "
+"veröffentliche und verwandte Arbeiten zeige."
+
+#. type: Plain text
+#: src/content/en/about.adoc:23
+msgid ""
+"I write about robust software: the kind that still works after the network "
+"drops, after the vendor leaves, and after you stop paying attention."
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/about.adoc:33
+msgid ""
+"That shows up in a few places. I care about reproducible systems, so I run "
+"a link:{stateless}[stateless NixOS workstation] and have written bridges "
+"like link:{cargo2nix}[cargo2nix] and link:{swift2nix}[swift2nix] to make "
+"Rust and Swift first-class citizens inside Nix builds. I care about durable "
+"data, which is why I keep returning to local-first software, persistent data "
+"structures, and link:{database}[the database I wish existed]: embedded, "
+"immutable, and syncable. And I care about tools that don't need a company "
+"behind them, so I reach for Git, email and plain text before I reach for a "
+"service -- far enough that link:{bugtracker}[my bug tracker is a directory]."
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/about.adoc:37
+msgid ""
+"I also think about how teams ship -- fault injection (link:{fallible}"
+"[fallible]), link:{flags}[feature flags], and link:{interviewing}[how we "
+"interview engineers] -- because robustness is a team habit before it is a "
+"technology."
+msgstr ""
+
+#. type: Title =
+#: src/content/en/music.adoc:1
+#, no-wrap
+msgid "Music"
+msgstr "Musik"
+
+#. type: Plain text
+#: src/content/en/music.adoc:8
+msgid ""
+"Scores I have engraved, with a synthesised rendering of each. The LilyPond "
+"sources are in the repository; the PDFs are built from them."
+msgstr ""
+"Von mir gestochene Notenblätter, jeweils mit einer synthetisierten "
+"Wiedergabe. Die LilyPond-Quellen liegen im Repository; die PDFs werden "
+"daraus gebaut."
+
+#. type: Title ==
+#: src/content/en/music.adoc:9
+#, no-wrap
+msgid "Scores"
+msgstr "Notenblätter"
+
+#. type: Plain text
+#: src/content/en/music.adoc:12
+msgid "João Pernambuco, link:{dengoso}.pdf[Dengoso]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:17
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/dengoso.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/music.adoc:20
+msgid "Canhoto, link:{marcha}.pdf[Marcha dos Marinheiros]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:25
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/marcha-dos-marinheiros.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/music.adoc:28
+msgid "Agustín Barrios, link:{choro}.pdf[Choro da Saudade]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:33
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/choro-da-saudade.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Title =
+#: src/content/en/blog/index.adoc:1
+#, no-wrap
+msgid "Blog"
+msgstr "Blog"
+
+#. type: Title =
+#: src/content/en/blog/categories.adoc:1 src/content/en/til/categories.adoc:1
+#: src/content/en/pastebin/categories.adoc:1
+#: src/content/en/podcast/categories.adoc:1
+#: src/content/en/screencast/categories.adoc:1
+#: src/content/en/slide/categories.adoc:1
+#, no-wrap
+msgid "Articles by category"
+msgstr "Artikel nach Kategorie"
#. type: Title =
#: src/content/en/til/index.adoc:1
#, no-wrap
msgid "Today I Learned"
-msgstr ""
+msgstr "Heute Habe ich Gelernt"
#. type: Plain text
#: src/content/en/til/index.adoc:6
msgid "**T**oday **I** **L**earned: small entries of useful knowledge."
msgstr ""
+"**H**eute **H**abe ich **G**elernt (aus dem Englischen, _**T**oday **I** "
+"**L**earned_): kleine Einträge nützlichen Wissens."
#. type: Plain text
#: src/content/en/til/index.adoc:7
msgid "Shameless rip-off of {anna-e-so}[Anna e só]."
-msgstr ""
+msgstr "Schamlose Kopie von {anna-e-so}[Anna e só]."
#. type: Title =
-#: src/content/en/til/categories.adoc:1
+#: src/content/en/pastebin/index.adoc:1
#, no-wrap
-msgid "Articles by category"
+msgid "Pastebins"
+msgstr "Pastebins"
+
+#. type: Title =
+#: src/content/en/podcast/index.adoc:1
+#, no-wrap
+msgid "Podcasts"
+msgstr "Podcast"
+
+#. type: Title =
+#: src/content/en/screencast/index.adoc:1
+#, no-wrap
+msgid "Screencasts"
+msgstr "Screencasts"
+
+#. type: Title =
+#: src/content/en/slide/index.adoc:1
+#, no-wrap
+msgid "Slides"
+msgstr "Folien"
+
+#. type: Plain text
+#: src/content/en/slide/index.adoc:4
+msgid ""
+"Slide decks, rendered to PDF: no browser needed to read them, and no "
+"JavaScript presentation framework to carry along."
msgstr ""
+"Foliensätze als PDF gesetzt: kein Browser nötig, um sie zu lesen, und kein "
+"JavaScript-Präsentationsframework im Schlepptau."
#. type: Title =
#: src/content/en/til/2020/08/12/filename-timestamp.adoc:1
@@ -159,3 +330,14 @@ msgid ""
"left. This means that you can easily sort them (and even tab-complete "
"them) with no extra effort or tool required."
msgstr ""
+
+#~ msgid "en/start.html\tStart here"
+#~ msgstr "de/anfang.html\tHier anfangen"
+
+#, no-wrap
+#~ msgid "Start here"
+#~ msgstr "Hier anfangen"
+
+#, no-wrap
+#~ msgid "Slides by category"
+#~ msgstr "Folien nach Kategorie"
diff --git a/po/en.po b/po/en.po
index cc244ff..d3a044e 100644
--- a/po/en.po
+++ b/po/en.po
@@ -6,7 +6,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2025-05-01 19:02-0300\n"
+"POT-Creation-Date: 2026-08-27 14:39-0300\n"
"PO-Revision-Date: 2025-04-29 19:46-0300\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
@@ -61,6 +61,32 @@ msgstr ""
msgid "category"
msgstr ""
+#. type: Plain text
+#: src/names/feed/ref.txt:1
+msgid "Atom feed"
+msgstr ""
+
+#. type: Plain text
+#: src/names/tagline/ref.txt:1
+msgid "Robust software, for everybody."
+msgstr ""
+
+#. type: Plain text
+#: src/names/feedicon_alt/ref.txt:1
+msgid "The Atom feed icon"
+msgstr ""
+
+#. type: Title =
+#: src/content/en/index.adoc:1
+#, no-wrap
+msgid "Home"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/index.adoc:3
+msgid "The newest of each collection is below."
+msgstr ""
+
#. type: Title =
#: src/content/en/about.adoc:1
#, no-wrap
@@ -68,23 +94,23 @@ msgid "About"
msgstr "About"
#. type: Plain text
-#: src/content/en/about.adoc:9
+#: src/content/en/about.adoc:17
#, fuzzy
#| msgid ""
-#| "Hi, I'm EuAndreh. I write software and occasionally music. You can find "
-#| "my contact information in the footer of this page, or mail my {mailto}"
-#| "[public inbox] ({archive}[archive])."
+#| "Hi, I'm EuAndreh. I write software and, occasionally, music. You can "
+#| "find my contact information in the footer of this page, or mail my "
+#| "{mailto}[public inbox] ({archive}[archive])."
msgid ""
-"Hi, I'm EuAndreh. I write software and, occasionally, music. You can find "
-"my contact information in the footer of this page, or mail my {mailto}"
-"[public inbox] ({archive}[archive])."
+"Hi, I'm EuAndreh -- André Miranda Moreira. I write software and, "
+"occasionally, music. You can find my contact information in the footer of "
+"this page, or mail my {mailto}[public inbox] ({archive}[archive])."
msgstr ""
"Hi, I'm EuAndreh. I write software and occasionally music. You can find my "
"contact information in the footer of this page, or mail my {mailto}[public "
"inbox] ({archive}[archive])."
#. type: Plain text
-#: src/content/en/about.adoc:11
+#: src/content/en/about.adoc:20
msgid ""
"This is my personal website where I write articles, publish software and "
"more related work."
@@ -92,6 +118,113 @@ msgstr ""
"This is my personal website where I write articles, publish software and "
"more related work."
+#. type: Plain text
+#: src/content/en/about.adoc:23
+msgid ""
+"I write about robust software: the kind that still works after the network "
+"drops, after the vendor leaves, and after you stop paying attention."
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/about.adoc:33
+msgid ""
+"That shows up in a few places. I care about reproducible systems, so I run "
+"a link:{stateless}[stateless NixOS workstation] and have written bridges "
+"like link:{cargo2nix}[cargo2nix] and link:{swift2nix}[swift2nix] to make "
+"Rust and Swift first-class citizens inside Nix builds. I care about durable "
+"data, which is why I keep returning to local-first software, persistent data "
+"structures, and link:{database}[the database I wish existed]: embedded, "
+"immutable, and syncable. And I care about tools that don't need a company "
+"behind them, so I reach for Git, email and plain text before I reach for a "
+"service -- far enough that link:{bugtracker}[my bug tracker is a directory]."
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/about.adoc:37
+msgid ""
+"I also think about how teams ship -- fault injection (link:{fallible}"
+"[fallible]), link:{flags}[feature flags], and link:{interviewing}[how we "
+"interview engineers] -- because robustness is a team habit before it is a "
+"technology."
+msgstr ""
+
+#. type: Title =
+#: src/content/en/music.adoc:1
+#, no-wrap
+msgid "Music"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/music.adoc:8
+msgid ""
+"Scores I have engraved, with a synthesised rendering of each. The LilyPond "
+"sources are in the repository; the PDFs are built from them."
+msgstr ""
+
+#. type: Title ==
+#: src/content/en/music.adoc:9
+#, no-wrap
+msgid "Scores"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/music.adoc:12
+msgid "João Pernambuco, link:{dengoso}.pdf[Dengoso]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:17
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/dengoso.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/music.adoc:20
+msgid "Canhoto, link:{marcha}.pdf[Marcha dos Marinheiros]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:25
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/marcha-dos-marinheiros.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/music.adoc:28
+msgid "Agustín Barrios, link:{choro}.pdf[Choro da Saudade]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:33
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/choro-da-saudade.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Title =
+#: src/content/en/blog/index.adoc:1
+#, no-wrap
+msgid "Blog"
+msgstr ""
+
+#. type: Title =
+#: src/content/en/blog/categories.adoc:1 src/content/en/til/categories.adoc:1
+#: src/content/en/pastebin/categories.adoc:1
+#: src/content/en/podcast/categories.adoc:1
+#: src/content/en/screencast/categories.adoc:1
+#: src/content/en/slide/categories.adoc:1
+#, no-wrap
+msgid "Articles by category"
+msgstr ""
+
#. type: Title =
#: src/content/en/til/index.adoc:1
#, no-wrap
@@ -109,9 +242,34 @@ msgid "Shameless rip-off of {anna-e-so}[Anna e só]."
msgstr ""
#. type: Title =
-#: src/content/en/til/categories.adoc:1
+#: src/content/en/pastebin/index.adoc:1
#, no-wrap
-msgid "Articles by category"
+msgid "Pastebins"
+msgstr ""
+
+#. type: Title =
+#: src/content/en/podcast/index.adoc:1
+#, no-wrap
+msgid "Podcasts"
+msgstr ""
+
+#. type: Title =
+#: src/content/en/screencast/index.adoc:1
+#, no-wrap
+msgid "Screencasts"
+msgstr ""
+
+#. type: Title =
+#: src/content/en/slide/index.adoc:1
+#, no-wrap
+msgid "Slides"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/slide/index.adoc:4
+msgid ""
+"Slide decks, rendered to PDF: no browser needed to read them, and no "
+"JavaScript presentation framework to carry along."
msgstr ""
#. type: Title =
diff --git a/po/eo.po b/po/eo.po
index d0cda59..2855537 100644
--- a/po/eo.po
+++ b/po/eo.po
@@ -6,7 +6,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2025-05-01 19:02-0300\n"
+"POT-Creation-Date: 2026-08-27 14:39-0300\n"
"PO-Revision-Date: 2025-04-29 19:46-0300\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
@@ -19,90 +19,260 @@ msgstr ""
#. type: Plain text
#: src/headers/ref.txt:2
msgid "en/blog/\tBlog"
-msgstr ""
+msgstr "eo/blog/\tBlogo"
#. type: Plain text
#: src/headers/ref.txt:4
msgid "en/til/\tTIL"
-msgstr ""
+msgstr "eo/hml/\tHML"
#. type: Plain text
#: src/headers/ref.txt:6
msgid "en/podcast/\tPodcasts"
-msgstr ""
+msgstr "eo/podkasto/\tPodkasto"
#. type: Plain text
#: src/headers/ref.txt:8
msgid "en/screencast/\tScreencasts"
-msgstr ""
+msgstr "eo/screencast/\tScreencasts"
#. type: Plain text
#: src/headers/ref.txt:10
msgid "en/pastebin/\tPastebins"
-msgstr ""
+msgstr "eo/pastebin/\tPastebins"
#. type: Plain text
#: src/headers/ref.txt:12
msgid "en/slide/\tSlides"
-msgstr ""
+msgstr "eo/lumbildoj/\tLumbildoj"
#. type: Plain text
#: src/headers/ref.txt:13
msgid "en/about.html\tAbout"
-msgstr ""
+msgstr "eo/pri.html\tPri"
#. type: Plain text
#: src/names/categories/ref.txt:1
msgid "categories"
-msgstr ""
+msgstr "kategorioj"
#. type: Plain text
#: src/names/category/ref.txt:1
msgid "category"
-msgstr ""
+msgstr "kategorio"
+
+#. type: Plain text
+#: src/names/feed/ref.txt:1
+msgid "Atom feed"
+msgstr "Atom-fluo"
+
+#. type: Plain text
+#: src/names/tagline/ref.txt:1
+msgid "Robust software, for everybody."
+msgstr "Fortika programaro, por ĉiuj."
+
+#. type: Plain text
+#: src/names/feedicon_alt/ref.txt:1
+msgid "The Atom feed icon"
+msgstr "La bildsimbolo de la Atom-fluo"
+
+#. type: Title =
+#: src/content/en/index.adoc:1
+#, no-wrap
+msgid "Home"
+msgstr "Hejmo"
+
+#. type: Plain text
+#: src/content/en/index.adoc:3
+msgid "The newest of each collection is below."
+msgstr "La plej novaj el ĉiu kolekto estas sube."
#. type: Title =
#: src/content/en/about.adoc:1
#, no-wrap
msgid "About"
-msgstr ""
+msgstr "Pri"
#. type: Plain text
-#: src/content/en/about.adoc:9
+#: src/content/en/about.adoc:17
msgid ""
-"Hi, I'm EuAndreh. I write software and, occasionally, music. You can find "
-"my contact information in the footer of this page, or mail my {mailto}"
-"[public inbox] ({archive}[archive])."
+"Hi, I'm EuAndreh -- André Miranda Moreira. I write software and, "
+"occasionally, music. You can find my contact information in the footer of "
+"this page, or mail my {mailto}[public inbox] ({archive}[archive])."
msgstr ""
+"Saluton, mi estas EuAndreh -- André Miranda Moreira. Mi verkas programojn "
+"kaj foje muzikojn. Vi povas trovi la kielo por kontakti min ĉe la fino de "
+"ĉi tiu paĝo, aŭ vi ankaŭ povas sendi mesaĝon al mia {mailto}[publika "
+"leterkesto] ({archive}[arkivo])."
#. type: Plain text
-#: src/content/en/about.adoc:11
+#: src/content/en/about.adoc:20
msgid ""
"This is my personal website where I write articles, publish software and "
"more related work."
msgstr ""
+"Ĉi tiu estas mia persona retejo, kie mi verkas artikolojn, publikas "
+"programojn kaj aliajn rilatajn laborojn."
+
+#. type: Plain text
+#: src/content/en/about.adoc:23
+msgid ""
+"I write about robust software: the kind that still works after the network "
+"drops, after the vendor leaves, and after you stop paying attention."
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/about.adoc:33
+msgid ""
+"That shows up in a few places. I care about reproducible systems, so I run "
+"a link:{stateless}[stateless NixOS workstation] and have written bridges "
+"like link:{cargo2nix}[cargo2nix] and link:{swift2nix}[swift2nix] to make "
+"Rust and Swift first-class citizens inside Nix builds. I care about durable "
+"data, which is why I keep returning to local-first software, persistent data "
+"structures, and link:{database}[the database I wish existed]: embedded, "
+"immutable, and syncable. And I care about tools that don't need a company "
+"behind them, so I reach for Git, email and plain text before I reach for a "
+"service -- far enough that link:{bugtracker}[my bug tracker is a directory]."
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/about.adoc:37
+msgid ""
+"I also think about how teams ship -- fault injection (link:{fallible}"
+"[fallible]), link:{flags}[feature flags], and link:{interviewing}[how we "
+"interview engineers] -- because robustness is a team habit before it is a "
+"technology."
+msgstr ""
+
+#. type: Title =
+#: src/content/en/music.adoc:1
+#, no-wrap
+msgid "Music"
+msgstr "Muziko"
+
+#. type: Plain text
+#: src/content/en/music.adoc:8
+msgid ""
+"Scores I have engraved, with a synthesised rendering of each. The LilyPond "
+"sources are in the repository; the PDFs are built from them."
+msgstr ""
+"Partituroj kiujn mi gravuris, ĉiu kun sintezita plenumo. La LilyPond-fontoj "
+"estas en la deponejo; la PDF-oj estas generitaj el ili."
+
+#. type: Title ==
+#: src/content/en/music.adoc:9
+#, no-wrap
+msgid "Scores"
+msgstr "Partituroj"
+
+#. type: Plain text
+#: src/content/en/music.adoc:12
+msgid "João Pernambuco, link:{dengoso}.pdf[Dengoso]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:17
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/dengoso.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/music.adoc:20
+msgid "Canhoto, link:{marcha}.pdf[Marcha dos Marinheiros]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:25
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/marcha-dos-marinheiros.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/music.adoc:28
+msgid "Agustín Barrios, link:{choro}.pdf[Choro da Saudade]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:33
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/choro-da-saudade.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Title =
+#: src/content/en/blog/index.adoc:1
+#, no-wrap
+msgid "Blog"
+msgstr "Blogo"
+
+#. type: Title =
+#: src/content/en/blog/categories.adoc:1 src/content/en/til/categories.adoc:1
+#: src/content/en/pastebin/categories.adoc:1
+#: src/content/en/podcast/categories.adoc:1
+#: src/content/en/screencast/categories.adoc:1
+#: src/content/en/slide/categories.adoc:1
+#, no-wrap
+msgid "Articles by category"
+msgstr "Artikoloj laŭ kategorio"
#. type: Title =
#: src/content/en/til/index.adoc:1
#, no-wrap
msgid "Today I Learned"
-msgstr ""
+msgstr "Hodiaŭ Mi Lernis"
#. type: Plain text
#: src/content/en/til/index.adoc:6
msgid "**T**oday **I** **L**earned: small entries of useful knowledge."
msgstr ""
+"**H**odiaŭ **M**i **L**ernis (du angla, _**T**oday **I** **L**earned_): "
+"malgrandaj afiŝoj de utilaj scioj."
#. type: Plain text
#: src/content/en/til/index.adoc:7
msgid "Shameless rip-off of {anna-e-so}[Anna e só]."
-msgstr ""
+msgstr "Senhonta kopio de {anna-e-so}[Anna e só]."
#. type: Title =
-#: src/content/en/til/categories.adoc:1
+#: src/content/en/pastebin/index.adoc:1
#, no-wrap
-msgid "Articles by category"
+msgid "Pastebins"
+msgstr "Pastebins"
+
+#. type: Title =
+#: src/content/en/podcast/index.adoc:1
+#, no-wrap
+msgid "Podcasts"
+msgstr "Podkasto"
+
+#. type: Title =
+#: src/content/en/screencast/index.adoc:1
+#, no-wrap
+msgid "Screencasts"
+msgstr "Screencasts"
+
+#. type: Title =
+#: src/content/en/slide/index.adoc:1
+#, no-wrap
+msgid "Slides"
+msgstr "Lumbildoj"
+
+#. type: Plain text
+#: src/content/en/slide/index.adoc:4
+msgid ""
+"Slide decks, rendered to PDF: no browser needed to read them, and no "
+"JavaScript presentation framework to carry along."
msgstr ""
+"Prezentaĵoj bildigitaj al PDF: neniu retumilo necesas por legi ilin, kaj "
+"neniu JavaScript-prezenta kadro por kunporti."
#. type: Title =
#: src/content/en/til/2020/08/12/filename-timestamp.adoc:1
@@ -159,3 +329,14 @@ msgid ""
"left. This means that you can easily sort them (and even tab-complete "
"them) with no extra effort or tool required."
msgstr ""
+
+#~ msgid "en/start.html\tStart here"
+#~ msgstr "eo/komencu.html\tKomencu ĉi tie"
+
+#, no-wrap
+#~ msgid "Start here"
+#~ msgstr "Komencu ĉi tie"
+
+#, no-wrap
+#~ msgid "Slides by category"
+#~ msgstr "Lumbildoj laŭ kategorio"
diff --git a/po/es.po b/po/es.po
index 43e2cbf..d3e7dc9 100644
--- a/po/es.po
+++ b/po/es.po
@@ -6,7 +6,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2025-05-01 19:02-0300\n"
+"POT-Creation-Date: 2026-08-27 14:39-0300\n"
"PO-Revision-Date: 2025-04-29 19:46-0300\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
@@ -19,90 +19,260 @@ msgstr ""
#. type: Plain text
#: src/headers/ref.txt:2
msgid "en/blog/\tBlog"
-msgstr ""
+msgstr "es/blog/\tBlog"
#. type: Plain text
#: src/headers/ref.txt:4
msgid "en/til/\tTIL"
-msgstr ""
+msgstr "es/haa/\tHAA"
#. type: Plain text
#: src/headers/ref.txt:6
msgid "en/podcast/\tPodcasts"
-msgstr ""
+msgstr "es/podcast/\tPodcast"
#. type: Plain text
#: src/headers/ref.txt:8
msgid "en/screencast/\tScreencasts"
-msgstr ""
+msgstr "es/screencast/\tScreencasts"
#. type: Plain text
#: src/headers/ref.txt:10
msgid "en/pastebin/\tPastebins"
-msgstr ""
+msgstr "es/pastebin/\tPastebins"
#. type: Plain text
#: src/headers/ref.txt:12
msgid "en/slide/\tSlides"
-msgstr ""
+msgstr "es/diapositivas/\tDiapositivas"
#. type: Plain text
#: src/headers/ref.txt:13
msgid "en/about.html\tAbout"
-msgstr ""
+msgstr "es/acerca.html\tAcerca"
#. type: Plain text
#: src/names/categories/ref.txt:1
msgid "categories"
-msgstr ""
+msgstr "categorias"
#. type: Plain text
#: src/names/category/ref.txt:1
msgid "category"
-msgstr ""
+msgstr "categoria"
+
+#. type: Plain text
+#: src/names/feed/ref.txt:1
+msgid "Atom feed"
+msgstr "Fuente Atom"
+
+#. type: Plain text
+#: src/names/tagline/ref.txt:1
+msgid "Robust software, for everybody."
+msgstr "Software robusto, para todos."
+
+#. type: Plain text
+#: src/names/feedicon_alt/ref.txt:1
+msgid "The Atom feed icon"
+msgstr "El icono de la fuente Atom"
+
+#. type: Title =
+#: src/content/en/index.adoc:1
+#, no-wrap
+msgid "Home"
+msgstr "Inicio"
+
+#. type: Plain text
+#: src/content/en/index.adoc:3
+msgid "The newest of each collection is below."
+msgstr "Lo más reciente de cada colección está abajo."
#. type: Title =
#: src/content/en/about.adoc:1
#, no-wrap
msgid "About"
-msgstr ""
+msgstr "Acerca"
#. type: Plain text
-#: src/content/en/about.adoc:9
+#: src/content/en/about.adoc:17
msgid ""
-"Hi, I'm EuAndreh. I write software and, occasionally, music. You can find "
-"my contact information in the footer of this page, or mail my {mailto}"
-"[public inbox] ({archive}[archive])."
+"Hi, I'm EuAndreh -- André Miranda Moreira. I write software and, "
+"occasionally, music. You can find my contact information in the footer of "
+"this page, or mail my {mailto}[public inbox] ({archive}[archive])."
msgstr ""
+"Hola, soy EuAndreh -- André Miranda Moreira. Escribo software y, "
+"ocasionalmente, música. Puedes encontrar mis datos de contacto al pie de "
+"esta página, o escribir a mi {mailto}[buzón público] ({archive}[archivo])."
#. type: Plain text
-#: src/content/en/about.adoc:11
+#: src/content/en/about.adoc:20
msgid ""
"This is my personal website where I write articles, publish software and "
"more related work."
msgstr ""
+"Este es mi sitio personal, donde escribo artículos, publico software y otros "
+"trabajos relacionados."
+
+#. type: Plain text
+#: src/content/en/about.adoc:23
+msgid ""
+"I write about robust software: the kind that still works after the network "
+"drops, after the vendor leaves, and after you stop paying attention."
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/about.adoc:33
+msgid ""
+"That shows up in a few places. I care about reproducible systems, so I run "
+"a link:{stateless}[stateless NixOS workstation] and have written bridges "
+"like link:{cargo2nix}[cargo2nix] and link:{swift2nix}[swift2nix] to make "
+"Rust and Swift first-class citizens inside Nix builds. I care about durable "
+"data, which is why I keep returning to local-first software, persistent data "
+"structures, and link:{database}[the database I wish existed]: embedded, "
+"immutable, and syncable. And I care about tools that don't need a company "
+"behind them, so I reach for Git, email and plain text before I reach for a "
+"service -- far enough that link:{bugtracker}[my bug tracker is a directory]."
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/about.adoc:37
+msgid ""
+"I also think about how teams ship -- fault injection (link:{fallible}"
+"[fallible]), link:{flags}[feature flags], and link:{interviewing}[how we "
+"interview engineers] -- because robustness is a team habit before it is a "
+"technology."
+msgstr ""
+
+#. type: Title =
+#: src/content/en/music.adoc:1
+#, no-wrap
+msgid "Music"
+msgstr "Música"
+
+#. type: Plain text
+#: src/content/en/music.adoc:8
+msgid ""
+"Scores I have engraved, with a synthesised rendering of each. The LilyPond "
+"sources are in the repository; the PDFs are built from them."
+msgstr ""
+"Partituras que he grabado, cada una con una interpretación sintetizada. Las "
+"fuentes de LilyPond están en el repositorio; los PDF se generan a partir de "
+"ellas."
+
+#. type: Title ==
+#: src/content/en/music.adoc:9
+#, no-wrap
+msgid "Scores"
+msgstr "Partituras"
+
+#. type: Plain text
+#: src/content/en/music.adoc:12
+msgid "João Pernambuco, link:{dengoso}.pdf[Dengoso]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:17
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/dengoso.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/music.adoc:20
+msgid "Canhoto, link:{marcha}.pdf[Marcha dos Marinheiros]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:25
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/marcha-dos-marinheiros.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/music.adoc:28
+msgid "Agustín Barrios, link:{choro}.pdf[Choro da Saudade]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:33
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/choro-da-saudade.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Title =
+#: src/content/en/blog/index.adoc:1
+#, no-wrap
+msgid "Blog"
+msgstr "Blog"
+
+#. type: Title =
+#: src/content/en/blog/categories.adoc:1 src/content/en/til/categories.adoc:1
+#: src/content/en/pastebin/categories.adoc:1
+#: src/content/en/podcast/categories.adoc:1
+#: src/content/en/screencast/categories.adoc:1
+#: src/content/en/slide/categories.adoc:1
+#, no-wrap
+msgid "Articles by category"
+msgstr "Artículos por categoría"
#. type: Title =
#: src/content/en/til/index.adoc:1
#, no-wrap
msgid "Today I Learned"
-msgstr ""
+msgstr "Hoy Aprendí Algo"
#. type: Plain text
#: src/content/en/til/index.adoc:6
msgid "**T**oday **I** **L**earned: small entries of useful knowledge."
msgstr ""
+"**H**oy **A**prendí **A**lgo (del inglés, _**T**oday **I** **L**earned_): "
+"pequeñas entradas de conocimiento útil."
#. type: Plain text
#: src/content/en/til/index.adoc:7
msgid "Shameless rip-off of {anna-e-so}[Anna e só]."
-msgstr ""
+msgstr "Copia descarada de {anna-e-so}[Anna e só]."
#. type: Title =
-#: src/content/en/til/categories.adoc:1
+#: src/content/en/pastebin/index.adoc:1
#, no-wrap
-msgid "Articles by category"
+msgid "Pastebins"
+msgstr "Pastebins"
+
+#. type: Title =
+#: src/content/en/podcast/index.adoc:1
+#, no-wrap
+msgid "Podcasts"
+msgstr "Podcast"
+
+#. type: Title =
+#: src/content/en/screencast/index.adoc:1
+#, no-wrap
+msgid "Screencasts"
+msgstr "Screencasts"
+
+#. type: Title =
+#: src/content/en/slide/index.adoc:1
+#, no-wrap
+msgid "Slides"
+msgstr "Diapositivas"
+
+#. type: Plain text
+#: src/content/en/slide/index.adoc:4
+msgid ""
+"Slide decks, rendered to PDF: no browser needed to read them, and no "
+"JavaScript presentation framework to carry along."
msgstr ""
+"Presentaciones renderizadas en PDF: no hace falta un navegador para leerlas, "
+"ni un marco de presentación en JavaScript que arrastrar."
#. type: Title =
#: src/content/en/til/2020/08/12/filename-timestamp.adoc:1
@@ -159,3 +329,14 @@ msgid ""
"left. This means that you can easily sort them (and even tab-complete "
"them) with no extra effort or tool required."
msgstr ""
+
+#~ msgid "en/start.html\tStart here"
+#~ msgstr "es/empieza.html\tEmpieza aquí"
+
+#, no-wrap
+#~ msgid "Start here"
+#~ msgstr "Empieza aquí"
+
+#, no-wrap
+#~ msgid "Slides by category"
+#~ msgstr "Diapositivas por categoría"
diff --git a/po/euandre.org.pot b/po/euandre.org.pot
index 783e8ab..db95401 100644
--- a/po/euandre.org.pot
+++ b/po/euandre.org.pot
@@ -7,7 +7,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2025-05-01 19:02-0300\n"
+"POT-Creation-Date: 2026-08-27 14:39-0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -61,6 +61,32 @@ msgstr ""
msgid "category"
msgstr ""
+#. type: Plain text
+#: src/names/feed/ref.txt:1
+msgid "Atom feed"
+msgstr ""
+
+#. type: Plain text
+#: src/names/tagline/ref.txt:1
+msgid "Robust software, for everybody."
+msgstr ""
+
+#. type: Plain text
+#: src/names/feedicon_alt/ref.txt:1
+msgid "The Atom feed icon"
+msgstr ""
+
+#. type: Title =
+#: src/content/en/index.adoc:1
+#, no-wrap
+msgid "Home"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/index.adoc:3
+msgid "The newest of each collection is below."
+msgstr ""
+
#. type: Title =
#: src/content/en/about.adoc:1
#, no-wrap
@@ -68,20 +94,128 @@ msgid "About"
msgstr ""
#. type: Plain text
-#: src/content/en/about.adoc:9
+#: src/content/en/about.adoc:17
msgid ""
-"Hi, I'm EuAndreh. I write software and, occasionally, music. You can find "
-"my contact information in the footer of this page, or mail my "
-"{mailto}[public inbox] ({archive}[archive])."
+"Hi, I'm EuAndreh -- André Miranda Moreira. I write software and, "
+"occasionally, music. You can find my contact information in the footer of "
+"this page, or mail my {mailto}[public inbox] ({archive}[archive])."
msgstr ""
#. type: Plain text
-#: src/content/en/about.adoc:11
+#: src/content/en/about.adoc:20
msgid ""
"This is my personal website where I write articles, publish software and "
"more related work."
msgstr ""
+#. type: Plain text
+#: src/content/en/about.adoc:23
+msgid ""
+"I write about robust software: the kind that still works after the network "
+"drops, after the vendor leaves, and after you stop paying attention."
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/about.adoc:33
+msgid ""
+"That shows up in a few places. I care about reproducible systems, so I run "
+"a link:{stateless}[stateless NixOS workstation] and have written bridges "
+"like link:{cargo2nix}[cargo2nix] and link:{swift2nix}[swift2nix] to make "
+"Rust and Swift first-class citizens inside Nix builds. I care about durable "
+"data, which is why I keep returning to local-first software, persistent data "
+"structures, and link:{database}[the database I wish existed]: embedded, "
+"immutable, and syncable. And I care about tools that don't need a company "
+"behind them, so I reach for Git, email and plain text before I reach for a "
+"service -- far enough that link:{bugtracker}[my bug tracker is a directory]."
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/about.adoc:37
+msgid ""
+"I also think about how teams ship -- fault injection "
+"(link:{fallible}[fallible]), link:{flags}[feature flags], and "
+"link:{interviewing}[how we interview engineers] -- because robustness is a "
+"team habit before it is a technology."
+msgstr ""
+
+#. type: Title =
+#: src/content/en/music.adoc:1
+#, no-wrap
+msgid "Music"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/music.adoc:8
+msgid ""
+"Scores I have engraved, with a synthesised rendering of each. The LilyPond "
+"sources are in the repository; the PDFs are built from them."
+msgstr ""
+
+#. type: Title ==
+#: src/content/en/music.adoc:9
+#, no-wrap
+msgid "Scores"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/music.adoc:12
+msgid "João Pernambuco, link:{dengoso}.pdf[Dengoso]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:17
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/dengoso.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/music.adoc:20
+msgid "Canhoto, link:{marcha}.pdf[Marcha dos Marinheiros]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:25
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/marcha-dos-marinheiros.ogg\" type=\"audio/ogg\" "
+"/>\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/music.adoc:28
+msgid "Agustín Barrios, link:{choro}.pdf[Choro da Saudade]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:33
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/choro-da-saudade.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Title =
+#: src/content/en/blog/index.adoc:1
+#, no-wrap
+msgid "Blog"
+msgstr ""
+
+#. type: Title =
+#: src/content/en/blog/categories.adoc:1 src/content/en/til/categories.adoc:1
+#: src/content/en/pastebin/categories.adoc:1
+#: src/content/en/podcast/categories.adoc:1
+#: src/content/en/screencast/categories.adoc:1
+#: src/content/en/slide/categories.adoc:1
+#, no-wrap
+msgid "Articles by category"
+msgstr ""
+
#. type: Title =
#: src/content/en/til/index.adoc:1
#, no-wrap
@@ -99,9 +233,34 @@ msgid "Shameless rip-off of {anna-e-so}[Anna e só]."
msgstr ""
#. type: Title =
-#: src/content/en/til/categories.adoc:1
+#: src/content/en/pastebin/index.adoc:1
#, no-wrap
-msgid "Articles by category"
+msgid "Pastebins"
+msgstr ""
+
+#. type: Title =
+#: src/content/en/podcast/index.adoc:1
+#, no-wrap
+msgid "Podcasts"
+msgstr ""
+
+#. type: Title =
+#: src/content/en/screencast/index.adoc:1
+#, no-wrap
+msgid "Screencasts"
+msgstr ""
+
+#. type: Title =
+#: src/content/en/slide/index.adoc:1
+#, no-wrap
+msgid "Slides"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/slide/index.adoc:4
+msgid ""
+"Slide decks, rendered to PDF: no browser needed to read them, and no "
+"JavaScript presentation framework to carry along."
msgstr ""
#. type: Title =
diff --git a/po/fr.po b/po/fr.po
index 7093686..ef77976 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -6,7 +6,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2025-05-01 19:02-0300\n"
+"POT-Creation-Date: 2026-08-27 14:39-0300\n"
"PO-Revision-Date: 2025-04-29 19:46-0300\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
@@ -19,90 +19,260 @@ msgstr ""
#. type: Plain text
#: src/headers/ref.txt:2
msgid "en/blog/\tBlog"
-msgstr ""
+msgstr "fr/blog/\tBlogue"
#. type: Plain text
#: src/headers/ref.txt:4
msgid "en/til/\tTIL"
-msgstr ""
+msgstr "fr/aja/\tAJA"
#. type: Plain text
#: src/headers/ref.txt:6
msgid "en/podcast/\tPodcasts"
-msgstr ""
+msgstr "fr/podcast/\tPodcast"
#. type: Plain text
#: src/headers/ref.txt:8
msgid "en/screencast/\tScreencasts"
-msgstr ""
+msgstr "fr/screencast/\tScreencasts"
#. type: Plain text
#: src/headers/ref.txt:10
msgid "en/pastebin/\tPastebins"
-msgstr ""
+msgstr "fr/pastebin/\tPastebins"
#. type: Plain text
#: src/headers/ref.txt:12
msgid "en/slide/\tSlides"
-msgstr ""
+msgstr "fr/diapositives/\tDiapositives"
#. type: Plain text
#: src/headers/ref.txt:13
msgid "en/about.html\tAbout"
-msgstr ""
+msgstr "fr/a-propos.html\tÀ propos"
#. type: Plain text
#: src/names/categories/ref.txt:1
msgid "categories"
-msgstr ""
+msgstr "categories"
#. type: Plain text
#: src/names/category/ref.txt:1
msgid "category"
-msgstr ""
+msgstr "categorie"
+
+#. type: Plain text
+#: src/names/feed/ref.txt:1
+msgid "Atom feed"
+msgstr "Flux Atom"
+
+#. type: Plain text
+#: src/names/tagline/ref.txt:1
+msgid "Robust software, for everybody."
+msgstr "Du logiciel robuste, pour tout le monde."
+
+#. type: Plain text
+#: src/names/feedicon_alt/ref.txt:1
+msgid "The Atom feed icon"
+msgstr "L'icône du flux Atom"
+
+#. type: Title =
+#: src/content/en/index.adoc:1
+#, no-wrap
+msgid "Home"
+msgstr "Accueil"
+
+#. type: Plain text
+#: src/content/en/index.adoc:3
+msgid "The newest of each collection is below."
+msgstr "Les plus récents de chaque collection sont ci-dessous."
#. type: Title =
#: src/content/en/about.adoc:1
#, no-wrap
msgid "About"
-msgstr ""
+msgstr "À propos"
#. type: Plain text
-#: src/content/en/about.adoc:9
+#: src/content/en/about.adoc:17
msgid ""
-"Hi, I'm EuAndreh. I write software and, occasionally, music. You can find "
-"my contact information in the footer of this page, or mail my {mailto}"
-"[public inbox] ({archive}[archive])."
+"Hi, I'm EuAndreh -- André Miranda Moreira. I write software and, "
+"occasionally, music. You can find my contact information in the footer of "
+"this page, or mail my {mailto}[public inbox] ({archive}[archive])."
msgstr ""
+"Salut, je suis EuAndreh -- André Miranda Moreira. J'écris logiciel et "
+"parfois musique. Vous pouvez rencontrer de donnés pour me contacter au bas "
+"cette de page, ou vous pouvez également envoyer une message dans ma {mailto}"
+"[boîte de réception publique] ({archive}[archive])."
#. type: Plain text
-#: src/content/en/about.adoc:11
+#: src/content/en/about.adoc:20
msgid ""
"This is my personal website where I write articles, publish software and "
"more related work."
msgstr ""
+"Ceci est mon site personnel où j'écris des articles, publie des logicielles "
+"et d'autres travaux connexes."
+
+#. type: Plain text
+#: src/content/en/about.adoc:23
+msgid ""
+"I write about robust software: the kind that still works after the network "
+"drops, after the vendor leaves, and after you stop paying attention."
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/about.adoc:33
+msgid ""
+"That shows up in a few places. I care about reproducible systems, so I run "
+"a link:{stateless}[stateless NixOS workstation] and have written bridges "
+"like link:{cargo2nix}[cargo2nix] and link:{swift2nix}[swift2nix] to make "
+"Rust and Swift first-class citizens inside Nix builds. I care about durable "
+"data, which is why I keep returning to local-first software, persistent data "
+"structures, and link:{database}[the database I wish existed]: embedded, "
+"immutable, and syncable. And I care about tools that don't need a company "
+"behind them, so I reach for Git, email and plain text before I reach for a "
+"service -- far enough that link:{bugtracker}[my bug tracker is a directory]."
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/about.adoc:37
+msgid ""
+"I also think about how teams ship -- fault injection (link:{fallible}"
+"[fallible]), link:{flags}[feature flags], and link:{interviewing}[how we "
+"interview engineers] -- because robustness is a team habit before it is a "
+"technology."
+msgstr ""
+
+#. type: Title =
+#: src/content/en/music.adoc:1
+#, no-wrap
+msgid "Music"
+msgstr "Musique"
+
+#. type: Plain text
+#: src/content/en/music.adoc:8
+msgid ""
+"Scores I have engraved, with a synthesised rendering of each. The LilyPond "
+"sources are in the repository; the PDFs are built from them."
+msgstr ""
+"Partitions que j'ai gravées, chacune avec un rendu synthétisé. Les sources "
+"LilyPond sont dans le dépôt; les PDF en sont générés."
+
+#. type: Title ==
+#: src/content/en/music.adoc:9
+#, no-wrap
+msgid "Scores"
+msgstr "Partitions"
+
+#. type: Plain text
+#: src/content/en/music.adoc:12
+msgid "João Pernambuco, link:{dengoso}.pdf[Dengoso]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:17
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/dengoso.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/music.adoc:20
+msgid "Canhoto, link:{marcha}.pdf[Marcha dos Marinheiros]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:25
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/marcha-dos-marinheiros.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/music.adoc:28
+msgid "Agustín Barrios, link:{choro}.pdf[Choro da Saudade]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:33
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/choro-da-saudade.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Title =
+#: src/content/en/blog/index.adoc:1
+#, no-wrap
+msgid "Blog"
+msgstr "Blogue"
+
+#. type: Title =
+#: src/content/en/blog/categories.adoc:1 src/content/en/til/categories.adoc:1
+#: src/content/en/pastebin/categories.adoc:1
+#: src/content/en/podcast/categories.adoc:1
+#: src/content/en/screencast/categories.adoc:1
+#: src/content/en/slide/categories.adoc:1
+#, no-wrap
+msgid "Articles by category"
+msgstr "Articles par catégorie"
#. type: Title =
#: src/content/en/til/index.adoc:1
#, no-wrap
msgid "Today I Learned"
-msgstr ""
+msgstr "Aujourd'hui J'ai Appris"
#. type: Plain text
#: src/content/en/til/index.adoc:6
msgid "**T**oday **I** **L**earned: small entries of useful knowledge."
msgstr ""
+"**A**ujourd'hui **J**'ai **A**ppris (de l'anglais, _**T**oday **I** "
+"**L**earned_): petites postes de connaissances utiles."
#. type: Plain text
#: src/content/en/til/index.adoc:7
msgid "Shameless rip-off of {anna-e-so}[Anna e só]."
-msgstr ""
+msgstr "Copie éhontée de {anna-e-so}[Anna e só]."
#. type: Title =
-#: src/content/en/til/categories.adoc:1
+#: src/content/en/pastebin/index.adoc:1
#, no-wrap
-msgid "Articles by category"
+msgid "Pastebins"
+msgstr "Pastebins"
+
+#. type: Title =
+#: src/content/en/podcast/index.adoc:1
+#, no-wrap
+msgid "Podcasts"
+msgstr "Podcast"
+
+#. type: Title =
+#: src/content/en/screencast/index.adoc:1
+#, no-wrap
+msgid "Screencasts"
+msgstr "Screencasts"
+
+#. type: Title =
+#: src/content/en/slide/index.adoc:1
+#, no-wrap
+msgid "Slides"
+msgstr "Diapositives"
+
+#. type: Plain text
+#: src/content/en/slide/index.adoc:4
+msgid ""
+"Slide decks, rendered to PDF: no browser needed to read them, and no "
+"JavaScript presentation framework to carry along."
msgstr ""
+"Présentations rendues en PDF: aucun navigateur n'est nécessaire pour les "
+"lire, et aucun cadriciel de présentation en JavaScript à traîner."
#. type: Title =
#: src/content/en/til/2020/08/12/filename-timestamp.adoc:1
@@ -159,3 +329,14 @@ msgid ""
"left. This means that you can easily sort them (and even tab-complete "
"them) with no extra effort or tool required."
msgstr ""
+
+#~ msgid "en/start.html\tStart here"
+#~ msgstr "fr/commencer.html\tCommencer ici"
+
+#, no-wrap
+#~ msgid "Start here"
+#~ msgstr "Commencer ici"
+
+#, no-wrap
+#~ msgid "Slides by category"
+#~ msgstr "Diapositives par catégorie"
diff --git a/po/po4a.cfg b/po/po4a.cfg
index 77f48a6..5a670cd 100644
--- a/po/po4a.cfg
+++ b/po/po4a.cfg
@@ -5,8 +5,131 @@
[type: text] src/headers/ref.txt $lang:src/headers/$lang.txt
[type: text] src/names/categories/ref.txt $lang:src/names/categories/$lang.txt
[type: text] src/names/category/ref.txt $lang:src/names/category/$lang.txt
+[type: text] src/names/feed/ref.txt $lang:src/names/feed/$lang.txt
+[type: text] src/names/tagline/ref.txt $lang:src/names/tagline/$lang.txt
+[type: text] src/names/feedicon_alt/ref.txt \
+ $lang:src/names/feedicon_alt/$lang.txt
-[type: asciidoc] src/content/en/about.adoc pt:src/content/pt/sobre.adoc add_$lang:po/note.txt
-[type: asciidoc] src/content/en/til/index.adoc pt:src/content/pt/hea/index.adoc add_$lang:po/note.txt
-[type: asciidoc] src/content/en/til/categories.adoc pt:src/content/pt/hea/categorias.adoc add_$lang:po/note.txt
-[type: asciidoc] src/content/en/til/2020/08/12/filename-timestamp.adoc pt:src/content/pt/hea/2020/08/12/arquivo-datado.adoc add_$lang:po/note.txt
+[type: asciidoc] src/content/en/index.adoc \
+ pt:src/content/pt/index.adoc \
+ fr:src/content/fr/index.adoc \
+ eo:src/content/eo/index.adoc \
+ es:src/content/es/index.adoc \
+ de:src/content/de/index.adoc \
+ add_$lang:po/note.txt
+
+[type: asciidoc] src/content/en/about.adoc \
+ pt:src/content/pt/sobre.adoc \
+ fr:src/content/fr/a-propos.adoc \
+ eo:src/content/eo/pri.adoc \
+ es:src/content/es/acerca.adoc \
+ de:src/content/de/ueber.adoc \
+ add_$lang:po/note.txt
+
+[type: asciidoc] src/content/en/music.adoc \
+ pt:src/content/pt/musica.adoc \
+ fr:src/content/fr/musique.adoc \
+ eo:src/content/eo/muziko.adoc \
+ es:src/content/es/musica.adoc \
+ de:src/content/de/musik.adoc \
+ add_$lang:po/note.txt
+
+[type: asciidoc] src/content/en/blog/index.adoc \
+ pt:src/content/pt/blog/index.adoc \
+ fr:src/content/fr/blog/index.adoc \
+ eo:src/content/eo/blog/index.adoc \
+ es:src/content/es/blog/index.adoc \
+ de:src/content/de/blog/index.adoc \
+ add_$lang:po/note.txt
+
+[type: asciidoc] src/content/en/blog/categories.adoc \
+ pt:src/content/pt/blog/categorias.adoc \
+ fr:src/content/fr/blog/categories.adoc \
+ eo:src/content/eo/blog/kategorioj.adoc \
+ es:src/content/es/blog/categorias.adoc \
+ de:src/content/de/blog/kategorien.adoc \
+ add_$lang:po/note.txt
+
+[type: asciidoc] src/content/en/til/index.adoc \
+ pt:src/content/pt/hea/index.adoc \
+ fr:src/content/fr/aja/index.adoc \
+ eo:src/content/eo/hml/index.adoc \
+ es:src/content/es/haa/index.adoc \
+ de:src/content/de/hhg/index.adoc \
+ add_$lang:po/note.txt
+
+[type: asciidoc] src/content/en/til/categories.adoc \
+ pt:src/content/pt/hea/categorias.adoc \
+ fr:src/content/fr/aja/categories.adoc \
+ eo:src/content/eo/hml/kategorioj.adoc \
+ es:src/content/es/haa/categorias.adoc \
+ de:src/content/de/hhg/kategorien.adoc \
+ add_$lang:po/note.txt
+
+[type: asciidoc] src/content/en/pastebin/index.adoc \
+ pt:src/content/pt/pastebin/index.adoc \
+ fr:src/content/fr/pastebin/index.adoc \
+ eo:src/content/eo/pastebin/index.adoc \
+ es:src/content/es/pastebin/index.adoc \
+ de:src/content/de/pastebin/index.adoc \
+ add_$lang:po/note.txt
+
+[type: asciidoc] src/content/en/pastebin/categories.adoc \
+ pt:src/content/pt/pastebin/categorias.adoc \
+ fr:src/content/fr/pastebin/categories.adoc \
+ eo:src/content/eo/pastebin/kategorioj.adoc \
+ es:src/content/es/pastebin/categorias.adoc \
+ de:src/content/de/pastebin/kategorien.adoc \
+ add_$lang:po/note.txt
+
+[type: asciidoc] src/content/en/podcast/index.adoc \
+ pt:src/content/pt/podcast/index.adoc \
+ fr:src/content/fr/podcast/index.adoc \
+ eo:src/content/eo/podkasto/index.adoc \
+ es:src/content/es/podcast/index.adoc \
+ de:src/content/de/podcast/index.adoc \
+ add_$lang:po/note.txt
+
+[type: asciidoc] src/content/en/podcast/categories.adoc \
+ pt:src/content/pt/podcast/categorias.adoc \
+ fr:src/content/fr/podcast/categories.adoc \
+ eo:src/content/eo/podkasto/kategorioj.adoc \
+ es:src/content/es/podcast/categorias.adoc \
+ de:src/content/de/podcast/kategorien.adoc \
+ add_$lang:po/note.txt
+
+[type: asciidoc] src/content/en/screencast/index.adoc \
+ pt:src/content/pt/screencast/index.adoc \
+ fr:src/content/fr/screencast/index.adoc \
+ eo:src/content/eo/screencast/index.adoc \
+ es:src/content/es/screencast/index.adoc \
+ de:src/content/de/screencast/index.adoc \
+ add_$lang:po/note.txt
+
+[type: asciidoc] src/content/en/screencast/categories.adoc \
+ pt:src/content/pt/screencast/categorias.adoc \
+ fr:src/content/fr/screencast/categories.adoc \
+ eo:src/content/eo/screencast/kategorioj.adoc \
+ es:src/content/es/screencast/categorias.adoc \
+ de:src/content/de/screencast/kategorien.adoc \
+ add_$lang:po/note.txt
+
+[type: asciidoc] src/content/en/slide/index.adoc \
+ pt:src/content/pt/eslaides/index.adoc \
+ fr:src/content/fr/diapositives/index.adoc \
+ eo:src/content/eo/lumbildoj/index.adoc \
+ es:src/content/es/diapositivas/index.adoc \
+ de:src/content/de/folien/index.adoc \
+ add_$lang:po/note.txt
+
+[type: asciidoc] src/content/en/slide/categories.adoc \
+ pt:src/content/pt/eslaides/categorias.adoc \
+ fr:src/content/fr/diapositives/categories.adoc \
+ eo:src/content/eo/lumbildoj/kategorioj.adoc \
+ es:src/content/es/diapositivas/categorias.adoc \
+ de:src/content/de/folien/kategorien.adoc \
+ add_$lang:po/note.txt
+
+[type: asciidoc] src/content/en/til/2020/08/12/filename-timestamp.adoc \
+ pt:src/content/pt/hea/2020/08/12/arquivo-datado.adoc \
+ add_$lang:po/note.txt
diff --git a/po/pt.po b/po/pt.po
index 4794358..17cfcc7 100644
--- a/po/pt.po
+++ b/po/pt.po
@@ -6,7 +6,7 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
-"POT-Creation-Date: 2025-05-01 19:02-0300\n"
+"POT-Creation-Date: 2026-08-27 14:39-0300\n"
"PO-Revision-Date: 2025-05-01 19:08-0300\n"
"Last-Translator: EuAndreh <eu@euandre.org>\n"
"Language-Team: none\n"
@@ -20,7 +20,7 @@ msgstr ""
#. type: Plain text
#: src/headers/ref.txt:2
msgid "en/blog/\tBlog"
-msgstr "#"
+msgstr "pt/blog/\tBlog"
#. type: Plain text
#: src/headers/ref.txt:4
@@ -30,22 +30,22 @@ msgstr "pt/hea/\tHEA"
#. type: Plain text
#: src/headers/ref.txt:6
msgid "en/podcast/\tPodcasts"
-msgstr "#"
+msgstr "pt/podcast/\tPodcast"
#. type: Plain text
#: src/headers/ref.txt:8
msgid "en/screencast/\tScreencasts"
-msgstr "#"
+msgstr "pt/screencast/\tScreencasts"
#. type: Plain text
#: src/headers/ref.txt:10
msgid "en/pastebin/\tPastebins"
-msgstr "#"
+msgstr "pt/pastebin/\tPastebins"
#. type: Plain text
#: src/headers/ref.txt:12
msgid "en/slide/\tSlides"
-msgstr "#"
+msgstr "pt/eslaides/\tEslaides"
#. type: Plain text
#: src/headers/ref.txt:13
@@ -62,6 +62,32 @@ msgstr "categorias"
msgid "category"
msgstr "categoria"
+#. type: Plain text
+#: src/names/feed/ref.txt:1
+msgid "Atom feed"
+msgstr "Feed Atom"
+
+#. type: Plain text
+#: src/names/tagline/ref.txt:1
+msgid "Robust software, for everybody."
+msgstr "Software robusto, para todo mundo."
+
+#. type: Plain text
+#: src/names/feedicon_alt/ref.txt:1
+msgid "The Atom feed icon"
+msgstr "O ícone do feed Atom"
+
+#. type: Title =
+#: src/content/en/index.adoc:1
+#, no-wrap
+msgid "Home"
+msgstr "Início"
+
+#. type: Plain text
+#: src/content/en/index.adoc:3
+msgid "The newest of each collection is below."
+msgstr "O mais recente de cada coleção está abaixo."
+
#. type: Title =
#: src/content/en/about.adoc:1
#, no-wrap
@@ -69,26 +95,135 @@ msgid "About"
msgstr "Sobre"
#. type: Plain text
-#: src/content/en/about.adoc:9
+#: src/content/en/about.adoc:17
msgid ""
-"Hi, I'm EuAndreh. I write software and, occasionally, music. You can find "
-"my contact information in the footer of this page, or mail my {mailto}"
-"[public inbox] ({archive}[archive])."
+"Hi, I'm EuAndreh -- André Miranda Moreira. I write software and, "
+"occasionally, music. You can find my contact information in the footer of "
+"this page, or mail my {mailto}[public inbox] ({archive}[archive])."
msgstr ""
-"Oi, eu sou EuAndreh. Eu escrevo software e, ocasionalmente, música. Você "
-"encontra meu dados para entrar em contato no rodapé desta página, ou pode "
-"mandar também uma mensagem para minha {mailto}[caixa de entrada pública] "
-"({archive}[arquivo])."
+"Oi, eu sou EuAndreh -- André Miranda Moreira. Eu escrevo software e "
+"ocasionalmente música. Você encontra dados para me contatar no rodapé desta "
+"página, ou pode mandar também uma mensagem para minha {mailto}[caixa de "
+"entrada pública] ({archive}[arquivo])."
#. type: Plain text
-#: src/content/en/about.adoc:11
+#: src/content/en/about.adoc:20
msgid ""
"This is my personal website where I write articles, publish software and "
"more related work."
msgstr ""
-"Esse é o meu site pessoal onde eu escrevo artigos, publico software e outros "
+"Esse é o meu site pessoal onde eu escrevo artigos, público software e outros "
"trabalhos relacionados."
+#. type: Plain text
+#: src/content/en/about.adoc:23
+msgid ""
+"I write about robust software: the kind that still works after the network "
+"drops, after the vendor leaves, and after you stop paying attention."
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/about.adoc:33
+msgid ""
+"That shows up in a few places. I care about reproducible systems, so I run "
+"a link:{stateless}[stateless NixOS workstation] and have written bridges "
+"like link:{cargo2nix}[cargo2nix] and link:{swift2nix}[swift2nix] to make "
+"Rust and Swift first-class citizens inside Nix builds. I care about durable "
+"data, which is why I keep returning to local-first software, persistent data "
+"structures, and link:{database}[the database I wish existed]: embedded, "
+"immutable, and syncable. And I care about tools that don't need a company "
+"behind them, so I reach for Git, email and plain text before I reach for a "
+"service -- far enough that link:{bugtracker}[my bug tracker is a directory]."
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/about.adoc:37
+msgid ""
+"I also think about how teams ship -- fault injection (link:{fallible}"
+"[fallible]), link:{flags}[feature flags], and link:{interviewing}[how we "
+"interview engineers] -- because robustness is a team habit before it is a "
+"technology."
+msgstr ""
+
+#. type: Title =
+#: src/content/en/music.adoc:1
+#, no-wrap
+msgid "Music"
+msgstr "Música"
+
+#. type: Plain text
+#: src/content/en/music.adoc:8
+msgid ""
+"Scores I have engraved, with a synthesised rendering of each. The LilyPond "
+"sources are in the repository; the PDFs are built from them."
+msgstr ""
+"Partituras que eu gravei, cada uma com uma execução sintetizada. Os fontes "
+"em LilyPond estão no repositório; os PDFs são gerados a partir deles."
+
+#. type: Title ==
+#: src/content/en/music.adoc:9
+#, no-wrap
+msgid "Scores"
+msgstr "Partituras"
+
+#. type: Plain text
+#: src/content/en/music.adoc:12
+msgid "João Pernambuco, link:{dengoso}.pdf[Dengoso]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:17
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/dengoso.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/music.adoc:20
+msgid "Canhoto, link:{marcha}.pdf[Marcha dos Marinheiros]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:25
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/marcha-dos-marinheiros.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Plain text
+#: src/content/en/music.adoc:28
+msgid "Agustín Barrios, link:{choro}.pdf[Choro da Saudade]"
+msgstr ""
+
+#. type: delimited block +
+#: src/content/en/music.adoc:33
+#, no-wrap
+msgid ""
+"<audio controls>\n"
+" <source src=\"../music/choro-da-saudade.ogg\" type=\"audio/ogg\" />\n"
+"</audio>\n"
+msgstr ""
+
+#. type: Title =
+#: src/content/en/blog/index.adoc:1
+#, no-wrap
+msgid "Blog"
+msgstr "Blog"
+
+#. type: Title =
+#: src/content/en/blog/categories.adoc:1 src/content/en/til/categories.adoc:1
+#: src/content/en/pastebin/categories.adoc:1
+#: src/content/en/podcast/categories.adoc:1
+#: src/content/en/screencast/categories.adoc:1
+#: src/content/en/slide/categories.adoc:1
+#, no-wrap
+msgid "Articles by category"
+msgstr "Artigos por categoria"
+
#. type: Title =
#: src/content/en/til/index.adoc:1
#, no-wrap
@@ -108,10 +243,37 @@ msgid "Shameless rip-off of {anna-e-so}[Anna e só]."
msgstr "Cópia descarada da {anna-e-so}[Anna e só]."
#. type: Title =
-#: src/content/en/til/categories.adoc:1
+#: src/content/en/pastebin/index.adoc:1
#, no-wrap
-msgid "Articles by category"
-msgstr "Artigos por categoria"
+msgid "Pastebins"
+msgstr "Pastebins"
+
+#. type: Title =
+#: src/content/en/podcast/index.adoc:1
+#, no-wrap
+msgid "Podcasts"
+msgstr "Podcast"
+
+#. type: Title =
+#: src/content/en/screencast/index.adoc:1
+#, no-wrap
+msgid "Screencasts"
+msgstr "Screencasts"
+
+#. type: Title =
+#: src/content/en/slide/index.adoc:1
+#, no-wrap
+msgid "Slides"
+msgstr "Eslaides"
+
+#. type: Plain text
+#: src/content/en/slide/index.adoc:4
+msgid ""
+"Slide decks, rendered to PDF: no browser needed to read them, and no "
+"JavaScript presentation framework to carry along."
+msgstr ""
+"Apresentações renderizadas em PDF: nenhum navegador é necessário para lê-"
+"las, e nenhum framework de apresentação em JavaScript para carregar junto."
#. type: Title =
#: src/content/en/til/2020/08/12/filename-timestamp.adoc:1
@@ -188,3 +350,14 @@ msgstr ""
"Ambos os formatos de data são hierárquicos, com intervalos de tempo maior à "
"esquerda. Isso significa que você pode facilmente ordená-los (e até usar "
"TAB para completar) sem esforço ou ferramenta extra."
+
+#~ msgid "en/start.html\tStart here"
+#~ msgstr "pt/comece.html\tComece aqui"
+
+#, no-wrap
+#~ msgid "Start here"
+#~ msgstr "Comece aqui"
+
+#, no-wrap
+#~ msgid "Slides by category"
+#~ msgstr "Eslaides por categoria"
diff --git a/src/collections/de/blog b/src/collections/de/blog
new file mode 120000
index 0000000..947e0f1
--- /dev/null
+++ b/src/collections/de/blog
@@ -0,0 +1 @@
+../../content/de/blog \ No newline at end of file
diff --git a/src/collections/de/folien b/src/collections/de/folien
new file mode 120000
index 0000000..d5e8154
--- /dev/null
+++ b/src/collections/de/folien
@@ -0,0 +1 @@
+../../content/de/folien \ No newline at end of file
diff --git a/src/collections/de/hhg b/src/collections/de/hhg
new file mode 120000
index 0000000..988f1f8
--- /dev/null
+++ b/src/collections/de/hhg
@@ -0,0 +1 @@
+../../content/de/hhg \ No newline at end of file
diff --git a/src/collections/de/pastebin b/src/collections/de/pastebin
new file mode 120000
index 0000000..78622df
--- /dev/null
+++ b/src/collections/de/pastebin
@@ -0,0 +1 @@
+../../content/de/pastebin \ No newline at end of file
diff --git a/src/collections/de/podcast b/src/collections/de/podcast
new file mode 120000
index 0000000..68c6a7e
--- /dev/null
+++ b/src/collections/de/podcast
@@ -0,0 +1 @@
+../../content/de/podcast \ No newline at end of file
diff --git a/src/collections/de/screencast b/src/collections/de/screencast
new file mode 120000
index 0000000..492d04f
--- /dev/null
+++ b/src/collections/de/screencast
@@ -0,0 +1 @@
+../../content/de/screencast \ No newline at end of file
diff --git a/src/collections/en/slide b/src/collections/en/slide
new file mode 120000
index 0000000..f84c929
--- /dev/null
+++ b/src/collections/en/slide
@@ -0,0 +1 @@
+../../content/en/slide \ No newline at end of file
diff --git a/src/collections/eo/blog b/src/collections/eo/blog
new file mode 120000
index 0000000..31e02df
--- /dev/null
+++ b/src/collections/eo/blog
@@ -0,0 +1 @@
+../../content/eo/blog \ No newline at end of file
diff --git a/src/collections/eo/hml b/src/collections/eo/hml
new file mode 120000
index 0000000..1fc4701
--- /dev/null
+++ b/src/collections/eo/hml
@@ -0,0 +1 @@
+../../content/eo/hml \ No newline at end of file
diff --git a/src/collections/eo/lumbildoj b/src/collections/eo/lumbildoj
new file mode 120000
index 0000000..e8da1ca
--- /dev/null
+++ b/src/collections/eo/lumbildoj
@@ -0,0 +1 @@
+../../content/eo/lumbildoj \ No newline at end of file
diff --git a/src/collections/eo/pastebin b/src/collections/eo/pastebin
new file mode 120000
index 0000000..5f58c89
--- /dev/null
+++ b/src/collections/eo/pastebin
@@ -0,0 +1 @@
+../../content/eo/pastebin \ No newline at end of file
diff --git a/src/collections/eo/podkasto b/src/collections/eo/podkasto
new file mode 120000
index 0000000..660af32
--- /dev/null
+++ b/src/collections/eo/podkasto
@@ -0,0 +1 @@
+../../content/eo/podkasto \ No newline at end of file
diff --git a/src/collections/eo/screencast b/src/collections/eo/screencast
new file mode 120000
index 0000000..d2bf50b
--- /dev/null
+++ b/src/collections/eo/screencast
@@ -0,0 +1 @@
+../../content/eo/screencast \ No newline at end of file
diff --git a/src/collections/es/blog b/src/collections/es/blog
new file mode 120000
index 0000000..7991857
--- /dev/null
+++ b/src/collections/es/blog
@@ -0,0 +1 @@
+../../content/es/blog \ No newline at end of file
diff --git a/src/collections/es/diapositivas b/src/collections/es/diapositivas
new file mode 120000
index 0000000..0d85a73
--- /dev/null
+++ b/src/collections/es/diapositivas
@@ -0,0 +1 @@
+../../content/es/diapositivas \ No newline at end of file
diff --git a/src/collections/es/haa b/src/collections/es/haa
new file mode 120000
index 0000000..c715b9d
--- /dev/null
+++ b/src/collections/es/haa
@@ -0,0 +1 @@
+../../content/es/haa \ No newline at end of file
diff --git a/src/collections/es/pastebin b/src/collections/es/pastebin
new file mode 120000
index 0000000..e4963de
--- /dev/null
+++ b/src/collections/es/pastebin
@@ -0,0 +1 @@
+../../content/es/pastebin \ No newline at end of file
diff --git a/src/collections/es/podcast b/src/collections/es/podcast
new file mode 120000
index 0000000..a1f8002
--- /dev/null
+++ b/src/collections/es/podcast
@@ -0,0 +1 @@
+../../content/es/podcast \ No newline at end of file
diff --git a/src/collections/es/screencast b/src/collections/es/screencast
new file mode 120000
index 0000000..68c7c06
--- /dev/null
+++ b/src/collections/es/screencast
@@ -0,0 +1 @@
+../../content/es/screencast \ No newline at end of file
diff --git a/src/collections/fr/aja b/src/collections/fr/aja
new file mode 120000
index 0000000..119a421
--- /dev/null
+++ b/src/collections/fr/aja
@@ -0,0 +1 @@
+../../content/fr/aja \ No newline at end of file
diff --git a/src/collections/fr/blog b/src/collections/fr/blog
new file mode 120000
index 0000000..50e28f1
--- /dev/null
+++ b/src/collections/fr/blog
@@ -0,0 +1 @@
+../../content/fr/blog \ No newline at end of file
diff --git a/src/collections/fr/diapositives b/src/collections/fr/diapositives
new file mode 120000
index 0000000..bba2dfa
--- /dev/null
+++ b/src/collections/fr/diapositives
@@ -0,0 +1 @@
+../../content/fr/diapositives \ No newline at end of file
diff --git a/src/collections/fr/pastebin b/src/collections/fr/pastebin
new file mode 120000
index 0000000..1492727
--- /dev/null
+++ b/src/collections/fr/pastebin
@@ -0,0 +1 @@
+../../content/fr/pastebin \ No newline at end of file
diff --git a/src/collections/fr/podcast b/src/collections/fr/podcast
new file mode 120000
index 0000000..27c205e
--- /dev/null
+++ b/src/collections/fr/podcast
@@ -0,0 +1 @@
+../../content/fr/podcast \ No newline at end of file
diff --git a/src/collections/fr/screencast b/src/collections/fr/screencast
new file mode 120000
index 0000000..eaecedb
--- /dev/null
+++ b/src/collections/fr/screencast
@@ -0,0 +1 @@
+../../content/fr/screencast \ No newline at end of file
diff --git a/src/collections/pt/blog b/src/collections/pt/blog
new file mode 120000
index 0000000..51f4355
--- /dev/null
+++ b/src/collections/pt/blog
@@ -0,0 +1 @@
+../../content/pt/blog \ No newline at end of file
diff --git a/src/collections/pt/eslaides b/src/collections/pt/eslaides
new file mode 120000
index 0000000..7aff6f4
--- /dev/null
+++ b/src/collections/pt/eslaides
@@ -0,0 +1 @@
+../../content/pt/eslaides \ No newline at end of file
diff --git a/src/collections/pt/pastebin b/src/collections/pt/pastebin
new file mode 120000
index 0000000..22f4bb0
--- /dev/null
+++ b/src/collections/pt/pastebin
@@ -0,0 +1 @@
+../../content/pt/pastebin \ No newline at end of file
diff --git a/src/collections/pt/podcast b/src/collections/pt/podcast
new file mode 120000
index 0000000..39911a5
--- /dev/null
+++ b/src/collections/pt/podcast
@@ -0,0 +1 @@
+../../content/pt/podcast \ No newline at end of file
diff --git a/src/collections/pt/screencast b/src/collections/pt/screencast
new file mode 120000
index 0000000..fde5d5b
--- /dev/null
+++ b/src/collections/pt/screencast
@@ -0,0 +1 @@
+../../content/pt/screencast \ No newline at end of file
diff --git a/src/comments.map b/src/comments.map
new file mode 100644
index 0000000..99da91a
--- /dev/null
+++ b/src/comments.map
@@ -0,0 +1,17 @@
+## Threads whose subject names no page, and where they belong.
+##
+## The subject is the join between a list thread and a page: the
+## Comment link puts the page's title in it. That breaks when
+## somebody starts a new thread instead of replying, or writes the
+## name of the thing down wrong. Both happened here.
+##
+## <THREAD-ROOT-MESSAGE-ID><TAB><the page's path>
+
+## Three follow-ups to "The database I wish I had", each begun as a
+## fresh thread rather than a reply.
+<d78064d2-5441-4275-96d9-463a08dfd7a9@www.fastmail.com> en/blog/2020/08/31/database-i-wish-i-had.html
+<000001d6d87f$90db0f90$b2912eb0$@kluv.in> en/blog/2020/08/31/database-i-wish-i-had.html
+<99C92E06-E0BC-4DB0-BF63-20FD44DC4062@rhyason.org> en/blog/2020/08/31/database-i-wish-i-had.html
+
+## "Just saw your crate2nix code" --- the tool is cargo2nix.
+<20201006075258.52tz5u5a36fpbfwy@wrt> en/blog/2020/10/05/cargo2nix.html
diff --git a/src/comments.mbox b/src/comments.mbox
new file mode 100644
index 0000000..edf49e0
--- /dev/null
+++ b/src/comments.mbox
@@ -0,0 +1,3464 @@
+From rory@roryokane.com Mon Aug 31 16:44:13 2020
+Received: from mail-qv1-f42.google.com (mail-qv1-f42.google.com [209.85.219.42])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 1E84AFF0FB
+ for <~euandreh/public-inbox@lists.sr.ht>; Mon, 31 Aug 2020 16:44:13 +0000 (UTC)
+Received: by mail-qv1-f42.google.com with SMTP id m14so1468478qvt.1
+ for <~euandreh/public-inbox@lists.sr.ht>; Mon, 31 Aug 2020 09:44:13 -0700 (PDT)
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=1e100.net; s=20161025;
+ h=x-gm-message-state:mime-version:from:date:message-id:subject:to;
+ bh=1N/5MWE9d7/I+SxiuWn81KliArG9XSFF8duwpxyY0oA=;
+ b=GiBnVI+Pf7DSl9ycPdfwPRjfdrlqlQQdgGKKyGsiIXPB1mLh6sBlzhoEQASxa/9hJm
+ rfhT8wRfQyBwscG2FpECYK09N/0ks7a71Pi3vy9ePKvSHJl0FYhywyAWzZ9Z7Xfp0/SF
+ oohs7Vzovs/sA9D9m2oKPcyfrj6lHWqf5qHJdfatP4LfVHCdRg1uwj3CmtCJWzmIFzuM
+ aIkUXLrK5YyBbfVl2L6EvEU1L7EDqOaTrXJt/+hWOev813kGYdYWsxMbYmirBAmO8sal
+ rgaF9vR/Jp8C+bxV/LSONsVOi4CA8azpLd1SYYpmS5yM4H5ISjaIOZfrZycx/Zd5Rle8
+ tn6Q==
+X-Gm-Message-State: AOAM530nGnF5g0fpYzDDTNbp5T67sHNZCqeZcVlHkD7CLIqa78AzYkW0
+ 44VZGvmhPwGnRAVlt7ZPbGJQAlmieuw=
+X-Google-Smtp-Source: ABdhPJwgZnnyjMFpfNBttjOyxb0b4VZgNwyJNN7HkBIHn457LHtZ8CSNy1RJMts49AmqIUIw2YT9Yg==
+X-Received: by 2002:a0c:b255:: with SMTP id k21mr1811354qve.134.1598892252561;
+ Mon, 31 Aug 2020 09:44:12 -0700 (PDT)
+Received: from mail-qt1-f176.google.com (mail-qt1-f176.google.com. [209.85.160.176])
+ by smtp.gmail.com with ESMTPSA id n33sm10800778qtd.43.2020.08.31.09.44.12
+ for <~euandreh/public-inbox@lists.sr.ht>
+ (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);
+ Mon, 31 Aug 2020 09:44:12 -0700 (PDT)
+Received: by mail-qt1-f176.google.com with SMTP id z2so5166925qtv.12
+ for <~euandreh/public-inbox@lists.sr.ht>; Mon, 31 Aug 2020 09:44:12 -0700 (PDT)
+X-Received: by 2002:ac8:73d0:: with SMTP id v16mr2161160qtp.384.1598892251883;
+ Mon, 31 Aug 2020 09:44:11 -0700 (PDT)
+MIME-Version: 1.0
+From: =?UTF-8?Q?Rory_O=E2=80=99Kane?= <rory@roryokane.com>
+Date: Mon, 31 Aug 2020 12:44:01 -0400
+X-Gmail-Original-Message-ID: <CAD_ag1NQoRdA3CyDPFyXBs-KcuU36dKqtZ8HCk-bKMeKRCe_UA@mail.gmail.com>
+Message-ID: <CAD_ag1NQoRdA3CyDPFyXBs-KcuU36dKqtZ8HCk-bKMeKRCe_UA@mail.gmail.com>
+Subject: Re: The database I wish I had
+To: ~euandreh/public-inbox@lists.sr.ht
+Content-Type: text/plain; charset="UTF-8"
+
+In this discussion of your post on Lobsters, there are some comments
+that may aid you in your quest:
+https://lobste.rs/s/m9vkg4/database_i_wish_i_had
+
+From eu@euandre.org Mon Aug 31 23:00:25 2020
+Received: from box.euandre.org (box.euandre.org [46.101.160.115])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 95690FF10A
+ for <~euandreh/public-inbox@lists.sr.ht>; Mon, 31 Aug 2020 23:00:24 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (2048-bit key) header.d=euandre.org header.i=@euandre.org header.b=UPSgUUXq
+Received: from authenticated-user (box.euandre.org [46.101.160.115])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
+ (No client certificate requested)
+ by box.euandre.org (Postfix) with ESMTPSA id 70152FC81E;
+ Mon, 31 Aug 2020 20:00:20 -0300 (-03)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=euandre.org; s=mail;
+ t=1598914822; bh=f4y8R0BR6plzUtLJhSLdKJQrnDt5TiYAaxpeGwaCRBQ=;
+ h=From:To:Cc:Subject:In-Reply-To:References:Date:From;
+ b=UPSgUUXqESOz5UhGXtlYTFrg0Vy42raDUdQSF1jRvRJpFdVQ7MWErphZhOvLjMrSB
+ UxSspR/+WTYJDMf61hcqMfCqFYqGLpFH6Q6I6V4M7MABddX6mKgoyL16fBJl2V/FcH
+ DVJBcrI+FeDrr3eoLTbXR5ZZzddSTSzfS2w93/QLad95xyd443VBEtUXEvl+G6pLC1
+ Bzv7oeG07V2OZF+jIsfW116e0tyUsT1JTY377yiWTIxoqP27+PIyP2SZjPyBsS/Xzb
+ 0MqK6PhL3IbwQ8xvv/h2SklKD9PYXtrjU5hupBXJSb/puN7fZHoaSv/qM69mfNuMxI
+ yTfUF1RUxJy5A==
+From: EuAndreh <eu@euandre.org>
+To: Rory =?utf-8?Q?O=E2=80=99Kane?= <rory@roryokane.com>,
+ ~euandreh/public-inbox@lists.sr.ht
+Cc:
+Subject: Re: The database I wish I had
+In-Reply-To: <CAD_ag1NQoRdA3CyDPFyXBs-KcuU36dKqtZ8HCk-bKMeKRCe_UA@mail.gmail.com>
+References: <CAD_ag1NQoRdA3CyDPFyXBs-KcuU36dKqtZ8HCk-bKMeKRCe_UA@mail.gmail.com>
+Date: Mon, 31 Aug 2020 19:55:51 -0300
+Message-ID: <87r1rmzaq0.fsf@euandre.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: quoted-printable
+
+Rory O=E2=80=99Kane <rory@roryokane.com> writes:
+
+> In this discussion of your post on Lobsters, there are some comments
+> that may aid you in your quest:
+> https://lobste.rs/s/m9vkg4/database_i_wish_i_had
+
+I don't have an account to discuss things there, and I don't have an
+invite, either.
+
+If you could invite me there I would gladly engage on the discussion =E2=98=
+=BA=EF=B8=8F.
+
+From rory@roryokane.com Mon Aug 31 23:43:09 2020
+Received: from mail-qk1-f180.google.com (mail-qk1-f180.google.com [209.85.222.180])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 43D60FF107
+ for <~euandreh/public-inbox@lists.sr.ht>; Mon, 31 Aug 2020 23:43:09 +0000 (UTC)
+Received: by mail-qk1-f180.google.com with SMTP id w186so7938858qkd.1
+ for <~euandreh/public-inbox@lists.sr.ht>; Mon, 31 Aug 2020 16:43:09 -0700 (PDT)
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=1e100.net; s=20161025;
+ h=x-gm-message-state:mime-version:references:in-reply-to:from:date
+ :message-id:subject:to:cc:content-transfer-encoding;
+ bh=gaaBELP8x2iHF+2fExDi20fg3n/w86cYg/IGy9Qn3xc=;
+ b=DRAjD5Bo+V1b4ugqTEX8uvyep/1C0WyUO+myK/uHn6v95bQRDYNS1+whm98sXMPXrb
+ qcafP0m0kbUfsTjEboLvrqZBZa3Ztv+8GFW6XmRjrtfJuZXx7RL0wXj16daqa2ik/dEe
+ RA0WM9UrrczXtTBjwnrG7ozNOGy1SKDKHO/c4f7aylfYGP+wVxyDl6nAaMQk+wHGkJcY
+ I37uXMATBkcsNPO+R8/lsyiCLkgk3wED+XJpYKZekgRA8p2I4S7wOP1SqmIpIyQlNllq
+ cYtPJkv8wi5MNtwR8t0k+DU6QZwYk8H1U+3YhWfwuVl+S+zHN1DC3Tw5ZrlxcIlpsP5v
+ 0fQA==
+X-Gm-Message-State: AOAM530oi09/wtTwog1b67r3kLqfg4eevHc/vZXa69bokmICUCS/wXgK
+ ZU0PXrAbUDFg2UNYNvv/oKkU5//k/mo=
+X-Google-Smtp-Source: ABdhPJyzZixNEwElMWvVTgTATohR8FEqbOW0p/68jnNsN8u+gCY6xUFeVZhDVtPlvGcDFDsynVK1gQ==
+X-Received: by 2002:a05:620a:2224:: with SMTP id n4mr4016163qkh.18.1598917388635;
+ Mon, 31 Aug 2020 16:43:08 -0700 (PDT)
+Received: from mail-qk1-f178.google.com (mail-qk1-f178.google.com. [209.85.222.178])
+ by smtp.gmail.com with ESMTPSA id u15sm12587433qtj.3.2020.08.31.16.43.08
+ for <~euandreh/public-inbox@lists.sr.ht>
+ (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);
+ Mon, 31 Aug 2020 16:43:08 -0700 (PDT)
+Received: by mail-qk1-f178.google.com with SMTP id p25so7942117qkp.2
+ for <~euandreh/public-inbox@lists.sr.ht>; Mon, 31 Aug 2020 16:43:08 -0700 (PDT)
+X-Received: by 2002:a37:b6c6:: with SMTP id g189mr4006715qkf.491.1598917387885;
+ Mon, 31 Aug 2020 16:43:07 -0700 (PDT)
+MIME-Version: 1.0
+References: <CAD_ag1NQoRdA3CyDPFyXBs-KcuU36dKqtZ8HCk-bKMeKRCe_UA@mail.gmail.com>
+ <87r1rmzaq0.fsf@euandre.org>
+In-Reply-To: <87r1rmzaq0.fsf@euandre.org>
+From: =?UTF-8?Q?Rory_O=E2=80=99Kane?= <rory@roryokane.com>
+Date: Mon, 31 Aug 2020 19:42:57 -0400
+X-Gmail-Original-Message-ID: <CAD_ag1Obqre+uRN+fQgX1-O7WWkT=2dTo4-HxabLXMBE9od6DQ@mail.gmail.com>
+Message-ID: <CAD_ag1Obqre+uRN+fQgX1-O7WWkT=2dTo4-HxabLXMBE9od6DQ@mail.gmail.com>
+Subject: Re: The database I wish I had
+To: EuAndreh <eu@euandre.org>
+Cc: ~euandreh/public-inbox@lists.sr.ht
+Content-Type: text/plain; charset="UTF-8"
+Content-Transfer-Encoding: quoted-printable
+
+EuAndreh <eu@euandre.org> wrote:
+
+> I don't have an account to discuss things there, and I don't have an
+> invite, either.
+>
+> If you could invite me there I would gladly engage on the discussion =EF=
+=B8=8F.
+
+I checked out your website and based on your writing, I can probably
+trust you with an invite. Invite sent=E2=80=94enjoy.
+
+From eu@euandre.org Tue Sep 1 01:00:13 2020
+Received: from box.euandre.org (box.euandre.org [46.101.160.115])
+ by mail-b.sr.ht (Postfix) with ESMTPS id B6E38FF11C
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 1 Sep 2020 01:00:12 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (2048-bit key) header.d=euandre.org header.i=@euandre.org header.b=P61dJynV
+Received: from authenticated-user (box.euandre.org [46.101.160.115])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
+ (No client certificate requested)
+ by box.euandre.org (Postfix) with ESMTPSA id 167EDFC820;
+ Mon, 31 Aug 2020 22:00:09 -0300 (-03)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=euandre.org; s=mail;
+ t=1598922011; bh=VOay/ul6frlnGv4X4iPDMI75WLW6Uop6PCjAV5nwAac=;
+ h=From:To:Cc:Cc:Subject:In-Reply-To:References:Date:From;
+ b=P61dJynVkkTLwtYleZjejOi5dPaIMpaJR1omX34om5nTFtkTU/7ckXbqlU/AlUxfb
+ IC516Xu9/pkk1uW45SdeoU8+9nYnK9/KK7VCiifC5yQeMLZwlzNupanCYeycFes500
+ D5Ygal4+Kz6prDX2cKrF4O97/nBKQKG6QQDzRG61Q1v+Tg2OMoJ45XENtQ7ONYzt9H
+ RkbSKiNA4ipJDzLx1vd9VX+cKSC9VljZn9dFxIBA6KQ+nmbMNXtNmirc10sREJ2g3u
+ zYefnUA3vRF1csdbIkTjfbiWnD2w2RN3Zj/oMwhNiAjHix4FyIg89WXwzv/CHmP9gr
+ 3+SrXBIC6XxzQ==
+From: EuAndreh <eu@euandre.org>
+To: Rory =?utf-8?Q?O=E2=80=99Kane?= <rory@roryokane.com>
+Cc: ~euandreh/public-inbox@lists.sr.ht
+Cc:
+Subject: Re: The database I wish I had
+In-Reply-To: <CAD_ag1Obqre+uRN+fQgX1-O7WWkT=2dTo4-HxabLXMBE9od6DQ@mail.gmail.com>
+References: <CAD_ag1NQoRdA3CyDPFyXBs-KcuU36dKqtZ8HCk-bKMeKRCe_UA@mail.gmail.com>
+ <87r1rmzaq0.fsf@euandre.org>
+ <CAD_ag1Obqre+uRN+fQgX1-O7WWkT=2dTo4-HxabLXMBE9od6DQ@mail.gmail.com>
+Date: Mon, 31 Aug 2020 21:42:38 -0300
+Message-ID: <87mu2az5s1.fsf@euandre.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: quoted-printable
+
+Rory O=E2=80=99Kane <rory@roryokane.com> writes:
+
+> I checked out your website and based on your writing, I can probably
+> trust you with an invite. Invite sent=E2=80=94enjoy.
+
+Thanks for the invitation, I hope I can add to the discussion and the
+community.=20
+
+From me@josephg.com Tue Sep 1 03:22:08 2020
+Received: from out4-smtp.messagingengine.com (out4-smtp.messagingengine.com [66.111.4.28])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 85748FF119
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 1 Sep 2020 03:22:08 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (1024-bit key) header.d=josephg.com header.i=@josephg.com header.b=XpX/7nfb;
+ dkim=fail reason="key not found in DNS" (0-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=MSacOUNm
+Received: from compute1.internal (compute1.nyi.internal [10.202.2.41])
+ by mailout.nyi.internal (Postfix) with ESMTP id 4403D5C0190
+ for <~euandreh/public-inbox@lists.sr.ht>; Mon, 31 Aug 2020 23:22:08 -0400 (EDT)
+Received: from imap35 ([10.202.2.85])
+ by compute1.internal (MEProxy); Mon, 31 Aug 2020 23:22:08 -0400
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=josephg.com; h=
+ mime-version:message-id:date:from:to:subject:content-type; s=
+ mesmtp; bh=D0CRtslwgxf19V66w3zMnP44hZ3wW3VGv7uaTI0Tszs=; b=XpX/7
+ nfbLe6I0o40Nfy5xKR+AcUanUComIWaSJMa5Gz5V2bCbIFdj6j2sSHEJWMJ/OvnG
+ K4hgJmu5ZXcYOmhZBRIrGpjackRdPfipduHOMYuWsT/tdMecLsxBU79WhNR71xfi
+ gCQK/U2ChiHuu2JvN2lhP9UZHk7EoaAkBGVtwQ=
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=
+ messagingengine.com; h=content-type:date:from:message-id
+ :mime-version:subject:to:x-me-proxy:x-me-proxy:x-me-sender
+ :x-me-sender:x-sasl-enc; s=fm3; bh=D0CRtslwgxf19V66w3zMnP44hZ3wW
+ 3VGv7uaTI0Tszs=; b=MSacOUNm/cU+7m2AkVNHn56i7XS7rZXVQlRheFQ10MXFQ
+ gY7AEoJYghtK1hl+FZhe4yTOOUJfj7xGQsnp7ENjd0yHSZPIqh4w1WDWMeWgvIwg
+ p3v7xHQc/ce/1zKCV7NNftJh9ietQ63JTujrUTGC0Hszv5+D+7mkLO+OGtMwAmVR
+ auNg0sFFa6BdTfaQLRWAX/BqQkiYcJ9oFW+Lxw1wiN6D/K005aRgHbbOwCAHGi9v
+ 6qkg/cVFzzqR52ydek4Oq0AtGwRBVMGK7llRBDRe83VDOcjzRNZScU/Z1hlEhDWF
+ fYm0B4nWev8MbeQUup3DjryE9U0WrMv/1MmI+G25A==
+X-ME-Sender: <xms:X75NX0NkXIq-arNl9tbSsnRDUhjMNpg-2ozeoZqLY6_2_W3DklLffQ>
+X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeduiedrudefiedgjedtucetufdoteggodetrfdotf
+ fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen
+ uceurghilhhouhhtmecufedttdenucenucfjughrpefofgggkfffhffvufgtsehttdertd
+ erredtnecuhfhrohhmpedfufgvphhhucfivghnthhlvgdfuceomhgvsehjohhsvghphhhg
+ rdgtohhmqeenucggtffrrghtthgvrhhnpeekgffhuedttedvjeelkeffkeeuueelkeeite
+ dttefgleejledtueetgfeijefhueenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgr
+ mhepmhgrihhlfhhrohhmpehmvgesjhhoshgvphhhghdrtghomh
+X-ME-Proxy: <xmx:X75NX6_-JLu-Eneod1EKsHyKkOEIRYFaAZQH4uBldS7GxRe_0WzDdw>
+ <xmx:X75NX7QCRm4jcdxXVZf_-SgheExet0j7JbTijuDCSZfm7EpG8GoXxg>
+ <xmx:X75NX8tKELWH7ryIzS_tU-Gi7kQXm8uw2XHfCRdbB4HJ08Y4s4Nheg>
+ <xmx:YL5NX4HEO3VR6t7Yz1-vKIi99YuUZV2t1wRHH3Tqv8f1bMEvEjAbAw>
+Received: by mailuser.nyi.internal (Postfix, from userid 501)
+ id C51F714C00E5; Mon, 31 Aug 2020 23:22:07 -0400 (EDT)
+X-Mailer: MessagingEngine.com Webmail Interface
+User-Agent: Cyrus-JMAP/3.3.0-232-g4bdb081-fm-20200825.002-g4bdb081a
+Mime-Version: 1.0
+Message-Id: <d78064d2-5441-4275-96d9-463a08dfd7a9@www.fastmail.com>
+Date: Tue, 01 Sep 2020 03:21:47 +0000
+From: "Seph Gentle" <me@josephg.com>
+To: ~euandreh/public-inbox@lists.sr.ht
+Subject: Making a database
+Content-Type: text/plain
+
+I've been thinking along similar lines, and am talking to some folks about building something on top of CRDTs. Modern CRDTs like automerge and y.js are just getting to the point where they're fast and small enough to work fine. I can see something really valuable that looks kind of like git, except where you can store arbitrary data. And live-sync peers - so if I make changes you see those changes live on your computer too - without (necessarily) needing a centralized server.
+
+Love to chat if you & others are keen - I/we are still in the process of figuring out what this thing will look like. But, I feel like the database piece is really core here too, and I like a lot of your thinking in this space.
+
+-Seph
+
+From me@jonas-schuermann.name Tue Sep 1 09:26:09 2020
+Received: from mout-p-102.mailbox.org (mout-p-102.mailbox.org [80.241.56.152])
+ by mail-b.sr.ht (Postfix) with ESMTPS id CEBA2FF127
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 1 Sep 2020 09:26:08 +0000 (UTC)
+Received: from smtp2.mailbox.org (smtp2.mailbox.org [IPv6:2001:67c:2050:105:465:1:2:0])
+ (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits))
+ (No client certificate requested)
+ by mout-p-102.mailbox.org (Postfix) with ESMTPS id 4BghWz4k2gzKmTx
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 1 Sep 2020 11:26:07 +0200 (CEST)
+X-Virus-Scanned: amavisd-new at heinlein-support.de
+Received: from smtp2.mailbox.org ([80.241.60.241])
+ by spamfilter04.heinlein-hosting.de (spamfilter04.heinlein-hosting.de [80.241.56.122]) (amavisd-new, port 10030)
+ with ESMTP id lwr5j4QAvkHQ for <~euandreh/public-inbox@lists.sr.ht>;
+ Tue, 1 Sep 2020 11:26:02 +0200 (CEST)
+To: ~euandreh/public-inbox@lists.sr.ht
+Subject: Re: The database I wish I had
+From: =?UTF-8?Q?Jonas_Sch=c3=bcrmann?= <me@jonas-schuermann.name>
+Message-ID: <e0b3b042-5eda-bcde-59a5-a5b61fecd2e5@jonas-schuermann.name>
+Date: Tue, 1 Sep 2020 11:26:02 +0200
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8; format=flowed
+Content-Language: en-US
+X-MBO-SPAM-Probability:
+X-Rspamd-Score: -1.02 / 15.00 / 15.00
+X-Rspamd-Queue-Id: 6015B17AB
+X-Rspamd-UID: af255d
+Content-Transfer-Encoding: quoted-printable
+
+Hello EuAndreh,
+
+I read your blog post with excitement because I'm currently on a very
+similar quest. My goal is to bring back application data into the hands
+of the users (and onto their devices) to replace centralized,
+cloud-based data stores. I wrote down what I wanted in this blog post
+[1].
+
+Like you, I didn't find anything that seemed to work like I wanted, so I
+decided to start exploring the problem myself. While you go for the
+Git-like approach of three-way-merges, I am trying to model conflict
+situations with completed categories.
+
+I can't say that I've come very far yet. I realized that properly
+modelling conflicts with completed categories is a hard problem and
+while I had some progress already, there isn't anything that I would
+consider ready to be shared just yet (if you're curious you can look at
+the source [2]).
+
+But through discussions on lobste.rs [3] and further research I built a
+list of references, prior work and concepts in this space [4]. From your
+blog post I even got a few more references that I just added :) Perhaps
+this list is also useful for you.
+
+I wish you the best of luck with your endeavor and I hope to see further
+posts about your progress on the blog :) When I have more time I will
+also write more about what I've found and what's next.
+
+Cordially, Jonas Sch=C3=BCrmann
+
+[1]=20
+https://jonas-schuermann.name/projects/dvcs-for-structured-data/blog/2020=
+-03-22-manifesto.html
+[2]=20
+https://gitlab.com/MazeChaZer/dvcs-for-structured-data/-/blob/master/sour=
+ce-code/EventSourcing.hs
+[3] https://lobste.rs/s/qsehiu/i_want_decentralized_version_control_for
+[4]=20
+https://jonas-schuermann.name/projects/dvcs-for-structured-data/notes/ref=
+erences.html
+
+From eu@euandre.org Tue Sep 1 10:00:15 2020
+Received: from box.euandre.org (box.euandre.org [46.101.160.115])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 6FC96FF127
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 1 Sep 2020 10:00:14 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (2048-bit key) header.d=euandre.org header.i=@euandre.org header.b=Fm0cCCjb
+Received: from authenticated-user (box.euandre.org [46.101.160.115])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
+ (No client certificate requested)
+ by box.euandre.org (Postfix) with ESMTPSA id 82F61FCB45;
+ Tue, 1 Sep 2020 07:00:11 -0300 (-03)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=euandre.org; s=mail;
+ t=1598954412; bh=d3MsFaC9js1laWdI/WHjnTEpvQ9IMPR4/17eUYzh5Z8=;
+ h=From:To:Cc:Subject:In-Reply-To:References:Date:From;
+ b=Fm0cCCjbaKJpKhoedrPl0XWIYJujunIk9LXBI7OUyABb7hgxRqEkLmW93LCA60QAE
+ +aZMzweJYdcNtmRBMeQmlkFYwnZZq8L9OBlsqi+BOILAEA5jv8Ly8RMQ6ZV6kogRpY
+ rV0RwyBEvskQ0vao5a/IWLuCSSalXShntwRIy44liQYZqxjFwJTQEXPDeb+gvmYaz1
+ 9aEvg5edL/OEGfo0VZpRk//f47V6a8o/3/uEaLpJAtEfCtQY1YYk5BdQGafCp5LrVR
+ fdp/6ju5YnM3yjM52MxTUvEjFpvK+FL/FSQDJ2DhdtbrH/EHHmNpImLP9WL1Bw9mNY
+ N2ar7lD/+9A6g==
+From: EuAndreh <eu@euandre.org>
+To: Seph Gentle <me@josephg.com>, ~euandreh/public-inbox@lists.sr.ht
+Cc:
+Subject: Re: Making a database
+In-Reply-To: <d78064d2-5441-4275-96d9-463a08dfd7a9@www.fastmail.com>
+References: <d78064d2-5441-4275-96d9-463a08dfd7a9@www.fastmail.com>
+Date: Tue, 01 Sep 2020 06:39:19 -0300
+Message-ID: <87ft81zvi0.fsf@euandre.org>
+MIME-Version: 1.0
+Content-Type: text/plain
+
+"Seph Gentle" <me@josephg.com> writes:
+
+> I've been thinking along similar lines, and am talking to some folks
+> about building something on top of CRDTs. Modern CRDTs like automerge
+> and y.js are just getting to the point where they're fast and small
+> enough to work fine. I can see something really valuable that looks
+> kind of like git, except where you can store arbitrary data. And
+> live-sync peers - so if I make changes you see those changes live on
+> your computer too - without (necessarily) needing a centralized
+> server.
+>
+> Love to chat if you & others are keen - I/we are still in the process
+> of figuring out what this thing will look like. But, I feel like the
+> database piece is really core here too, and I like a lot of your
+> thinking in this space.
+
+Thanks for the encouragement :)
+
+I've looked at CRDTs, but I haven't done a deep dive yet, because my
+first impression was that they generally choose to limit the types of
+data structures and operations that you can perform on them in favour of
+being conflict-free.
+
+That's why I didn't mention it on my article directly, but I admit to
+having it as pre-requisite to making an informed decision on the
+subject.
+
+WDYT?
+
+From eu@euandre.org Tue Sep 1 11:00:18 2020
+Received: from box.euandre.org (box.euandre.org [46.101.160.115])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 7E055FF122
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 1 Sep 2020 11:00:17 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (2048-bit key) header.d=euandre.org header.i=@euandre.org header.b=aexrWU2E
+Received: from authenticated-user (box.euandre.org [46.101.160.115])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
+ (No client certificate requested)
+ by box.euandre.org (Postfix) with ESMTPSA id A3E67FD6B9;
+ Tue, 1 Sep 2020 08:00:14 -0300 (-03)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=euandre.org; s=mail;
+ t=1598958016; bh=RLLLMwMzFeXBwtfwXnEsHK/UTMXFBGSC5qgHaz/ZmOE=;
+ h=From:To:Cc:Subject:In-Reply-To:References:Date:From;
+ b=aexrWU2Ee5zBZdVCWHCoLentiEWPHDdm7cDXSVNCrsIO+wkZPDXWbT+YwGbX7pldB
+ rs//2AD5Bz0VOPByW33jD53/lmCCBeZ8eBjSLpfN2/w3sXHKPXh348R6/YLLRMSuwj
+ 9bCKuxK3X8qJ2SMS3fg8OGIzHgBq8rL59U0pFgCiV6WR5Z92mG80P8iogtSI8LzU/g
+ yBhu4sv4gJ3IlLhTiwX4HgAQkSoOCu6rdm5AbDdkwPwteUyy+7F2KlHc6eD7UTktU8
+ ic7l2oVEyQrUYddwtZmUfJJCG9tffy5RDAyBO/TaLjVJpesdFEY5nRIG/Xm+1geH1j
+ fv++ZMqsLTgxQ==
+From: EuAndreh <eu@euandre.org>
+To: Jonas =?utf-8?Q?Sch=C3=BCrmann?= <me@jonas-schuermann.name>,
+ ~euandreh/public-inbox@lists.sr.ht
+Cc:
+Subject: Re: The database I wish I had
+In-Reply-To: <e0b3b042-5eda-bcde-59a5-a5b61fecd2e5@jonas-schuermann.name>
+References: <e0b3b042-5eda-bcde-59a5-a5b61fecd2e5@jonas-schuermann.name>
+Date: Tue, 01 Sep 2020 07:26:47 -0300
+Message-ID: <87y2ltyeqg.fsf@euandre.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: quoted-printable
+
+Jonas Sch=C3=BCrmann <me@jonas-schuermann.name> writes:
+
+> Hello EuAndreh,
+
+Hi there!
+
+> I read your blog post with excitement because I'm currently on a very
+> similar quest. My goal is to bring back application data into the hands
+> of the users (and onto their devices) to replace centralized,
+> cloud-based data stores. I wrote down what I wanted in this blog post
+> [1].
+>
+> Like you, I didn't find anything that seemed to work like I wanted, so I
+> decided to start exploring the problem myself. While you go for the
+> Git-like approach of three-way-merges, I am trying to model conflict
+> situations with completed categories.
+
+Hmmm, I haven't heard this term before. Are these [0] the complete
+categories that you mean?
+
+[0]: https://en.wikipedia.org/wiki/Complete_category
+=20
+> I can't say that I've come very far yet. I realized that properly
+> modelling conflicts with completed categories is a hard problem and
+> while I had some progress already, there isn't anything that I would
+> consider ready to be shared just yet (if you're curious you can look at
+> the source [2]).
+>
+> But through discussions on lobste.rs [3] and further research I built a
+> list of references, prior work and concepts in this space [4]. From your
+> blog post I even got a few more references that I just added :) Perhaps
+> this list is also useful for you.
+
+Thanks for all the links, I'll try to use them to enrich my
+understanding on the subject.
+
+From me@josephg.com Tue Sep 1 11:25:21 2020
+Received: from wout2-smtp.messagingengine.com (wout2-smtp.messagingengine.com [64.147.123.25])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 50AC3FF122
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 1 Sep 2020 11:25:21 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (1024-bit key) header.d=josephg.com header.i=@josephg.com header.b=O0rYtvTA;
+ dkim=fail reason="key not found in DNS" (0-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=f1mPF/1x
+Received: from compute1.internal (compute1.nyi.internal [10.202.2.41])
+ by mailout.west.internal (Postfix) with ESMTP id E4956E77;
+ Tue, 1 Sep 2020 07:25:19 -0400 (EDT)
+Received: from imap35 ([10.202.2.85])
+ by compute1.internal (MEProxy); Tue, 01 Sep 2020 07:25:20 -0400
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=josephg.com; h=
+ mime-version:message-id:in-reply-to:references:date:from:to
+ :subject:content-type:content-transfer-encoding; s=mesmtp; bh=BO
+ dAgwWsH67HEzbojnfogot17D0WKCAzyItvgTLmJCw=; b=O0rYtvTAoINKZTJObn
+ vUnmONdChl7CPOEklbKnqUQ+Sk8qaAoCe0YyqwTLq0YZywk49u56Zwff5QaSJfLm
+ SYA9bd2RUEagih/gPTR4BHJarqfWSHQeUsHsvMmVtj4w8jrJt261gcmh1Git8Jkt
+ lJSUIZ9tpOecI/nFsnhxCleA0=
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=
+ messagingengine.com; h=content-transfer-encoding:content-type
+ :date:from:in-reply-to:message-id:mime-version:references
+ :subject:to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender
+ :x-sasl-enc; s=fm3; bh=BOdAgwWsH67HEzbojnfogot17D0WKCAzyItvgTLmJ
+ Cw=; b=f1mPF/1xX+9SrbduF4hyrnDZlojUnotl/ZuV6F+yuzbQlOHFTtLgMSJ5u
+ ghKQ7iTGJdFGqxzM87y1JJLCWb6BZjbRw2TjhDK+z24JeMwOVOpZgDsZKZ4vWlHI
+ x6C8Ca/TnlF8Glsyvepoczx5SP6kbYCOe/JrBGp3gTIfTGohNHuxCsxxHvW3gn35
+ hQLIa42DMt3y1p0q2rl4ltdrCYpCzQVvfbhKssX3AmCkwndYDhbP6l4jLnWH6jeM
+ oBw77aR3keR94fhUnSCsnlX5ZSljRkIecR7Z9sGsR8KeB5j72A/ND3rncJyOOQ/e
+ +DXRIAj+KVgqvbY2AimyrweAA9mQg==
+X-ME-Sender: <xms:nS9OX8TbEfFCRuUyS6F4hwgBx8v03SzB2Ggzuy63tMjyFVg-UrVkNQ>
+ <xme:nS9OX5zGsoiG5l8lthnVpFgOEb1gqC7oeFR2NwTbn4uDQFjSZ-ZvMZeo0aSeRF8GV
+ exRk5tGsHuiPRtDjw>
+X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeduiedrudefjedggedtucetufdoteggodetrfdotf
+ fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen
+ uceurghilhhouhhtmecufedttdenucenucfjughrpefofgggkfgjfhffhffvufgtgfesth
+ hqredtreerjeenucfhrhhomhepfdfuvghphhcuifgvnhhtlhgvfdcuoehmvgesjhhoshgv
+ phhhghdrtghomheqnecuggftrfgrthhtvghrnhepkeegtdfhgfdvheetveetleejgfdvhf
+ egtdegfedvhfegveffgfdthfehgedukeegnecuffhomhgrihhnpehslhgrtghkrdgtohhm
+ necuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepmhgvse
+ hjohhsvghphhhgrdgtohhm
+X-ME-Proxy: <xmx:nS9OX50QvymIMNNSVjvYuwnELZacWmVFdJvm4gYZ7wZ28Ysp8XeqaA>
+ <xmx:nS9OXwCN5irAMSLyp8Oejw_bDz4dedc6-GQj4ThImfuyCt5P3RUUhA>
+ <xmx:nS9OX1jNG59H8yiSSKrEdc830ToS-wnjIO8Dqrr2hhrFB4opADJCoQ>
+ <xmx:ny9OX6tPG_S_xI2Z3IHEmzknqb1-PlAmuX_wv6n9qglqXIcgp0MqSQ>
+Received: by mailuser.nyi.internal (Postfix, from userid 501)
+ id 72BAB14C034C; Tue, 1 Sep 2020 07:25:17 -0400 (EDT)
+X-Mailer: MessagingEngine.com Webmail Interface
+User-Agent: Cyrus-JMAP/3.3.0-248-gcd102cb-fm-20200901.001-gcd102cb9
+Mime-Version: 1.0
+Message-Id: <9e513706-7ebc-4a40-8e5e-a5f94d84bc77@www.fastmail.com>
+In-Reply-To: <87ft81zvi0.fsf@euandre.org>
+References: <d78064d2-5441-4275-96d9-463a08dfd7a9@www.fastmail.com>
+ <87ft81zvi0.fsf@euandre.org>
+Date: Tue, 01 Sep 2020 21:24:55 +1000
+From: "Seph Gentle" <me@josephg.com>
+To: EuAndreh <eu@euandre.org>, ~euandreh/public-inbox@lists.sr.ht
+Subject: Re: Making a database
+Content-Type: text/plain;charset=utf-8
+Content-Transfer-Encoding: quoted-printable
+
+On Tue, Sep 1, 2020, at 7:39 PM, EuAndreh wrote:
+> "Seph Gentle" <me@josephg.com> writes:
+>=20
+> > I've been thinking along similar lines, and am talking to some folks=
+
+> > about building something on top of CRDTs. Modern CRDTs like automerg=
+e
+> > and y.js are just getting to the point where they're fast and small
+> > enough to work fine. I can see something really valuable that looks
+> > kind of like git, except where you can store arbitrary data. And
+> > live-sync peers - so if I make changes you see those changes live on=
+
+> > your computer too - without (necessarily) needing a centralized
+> > server.
+> >
+> > Love to chat if you & others are keen - I/we are still in the proces=
+s
+> > of figuring out what this thing will look like. But, I feel like the=
+
+> > database piece is really core here too, and I like a lot of your
+> > thinking in this space.
+>=20
+> Thanks for the encouragement :)
+>=20
+> I've looked at CRDTs, but I haven't done a deep dive yet, because my
+> first impression was that they generally choose to limit the types of
+> data structures and operations that you can perform on them in favour =
+of
+> being conflict-free.
+>=20
+> That's why I didn't mention it on my article directly, but I admit to
+> having it as pre-requisite to making an informed decision on the
+> subject.
+>=20
+> WDYT?
+
+Well, they=E2=80=99re conflict-free in the sense that (unlike Postgres/f=
+oundationdb/etc) the database doesn=E2=80=99t reject conflicting concurr=
+ent updates. Which is a property you need if you=E2=80=99re going to com=
+mit local writes while you=E2=80=99re offline. And if I=E2=80=99m readin=
+g your blog post right it sounds like that=E2=80=99s what you=E2=80=99re=
+ going for.
+
+You kinda gave a good description of them in your post already. They wor=
+k by requiring all peers to resolve concurrent edits to an object the sa=
+me way. That could be last-writer-wins (based on time stamps like you sa=
+id in your article). Or it could store all conflicting versions together=
+ to be resolved by the next reader (Eg riak). Or it could do something m=
+ore clever (like merge changes using automerge or y.js or the like). Git=
+ is sort of a CRDT - it=E2=80=99s just it=E2=80=99s 3 way merge algorith=
+m is a bit of a dogs breakfast as far as these things go.
+
+Anyway, happy to chat about this stuff in more detail if you wanna zoom =
+or something. Or hop into the automerge slack (with pvh from ink&switch,=
+ and Martin Kleppmann, and others) and we can talk about this stuff - ht=
+tps://join.slack.com/t/automerge/shared_invite/zt-e4p3760n-kKh7r3KRH1Yww=
+NfiZM8ktw
+
+As I said - I=E2=80=99m looking to build something like this too and it=E2=
+=80=99d be good to compare notes on our approaches!
+
+From rnewman@twinql.com Tue Sep 1 15:46:34 2020
+Received: from a27-232.smtp-out.us-west-2.amazonses.com (a27-232.smtp-out.us-west-2.amazonses.com [54.240.27.232])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 02A70FF0E3
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 1 Sep 2020 15:46:33 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (1024-bit key) header.d=twinql.com header.i=@twinql.com header.b=cNOZcfp4;
+ dkim=pass (1024-bit key) header.d=amazonses.com header.i=@amazonses.com header.b=Hg/JwA02
+DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/simple;
+ s=qvs2k2sd2ivtrflyu2nswmravuqizjml; d=twinql.com; t=1598975192;
+ h=Subject:From:To:Date:Mime-Version:Content-Type:Content-Transfer-Encoding:References:Message-Id;
+ bh=h5tYwgdiFvKfB58ME/qKUDlZfMWWDjBD1us0jqb+obo=;
+ b=cNOZcfp4gLnUosw6qaEpaunrxtpsBSNsQgvh/C3StK1VPirNXCoHKVx5ZtOO/szG
+ PdOufKL9NM0bQu6OiYFBxUdp2fuZ2FGwONd2nzGBOGcMN4V2aSOuMAGcuClGe2Kf+SM
+ xfSfiY2iyxEUtlFoNU9qfvTtJ5Wutm+/EkIRuJ+g=
+DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/simple;
+ s=hsbnp7p3ensaochzwyq5wwmceodymuwv; d=amazonses.com; t=1598975192;
+ h=Subject:From:To:Date:Mime-Version:Content-Type:Content-Transfer-Encoding:References:Message-Id:Feedback-ID;
+ bh=h5tYwgdiFvKfB58ME/qKUDlZfMWWDjBD1us0jqb+obo=;
+ b=Hg/JwA02/i8iV50zfzagoO6qkIy3fWh3xaS3Ea/Mbew1Ve1B70ilQdJIrthnSRCF
+ S50Nn0YzMQlyzXXd8rCHSfwTcqDUYV14hdN+GPbGPF+O+6w/X7KG6PxWnYTE/5w/cE9
+ 5HAQ9xp3FdpTqa2RvVsLtEJVweoEtHmNR5319VRs=
+Subject: Re: The database I wish I had
+From: =?UTF-8?Q?Richard_Newman?= <rnewman@twinql.com>
+To: =?UTF-8?Q?=7Eeuandreh/public-inbox=40lists=2Esr=2Eht?=
+ <~euandreh/public-inbox@lists.sr.ht>
+Date: Tue, 1 Sep 2020 15:46:31 +0000
+Mime-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+References: <7454DA45-C5A4-4A54-8D02-1F3BD37726E9@twinql.com>
+X-Priority: 3 (Normal)
+X-Mailer: Amazon WorkMail
+Thread-Index: AQHWgHcPJ4udfXYrRaOwhoO7EcsDJQ==
+Thread-Topic: The database I wish I had
+X-Original-Mailer: Apple Mail (2.3445.104.14)
+X-Wm-Sent-Timestamp: 1598975191
+Message-ID: <010101744a592b75-1dce9281-f0b8-4226-9d50-fd2c7901fa72-000000@us-west-2.amazonses.com>
+X-SES-Outgoing: 2020.09.01-54.240.27.232
+Feedback-ID: 1.us-west-2.An468LAV0jCjQDrDLvlZjeAthld7qrhZr+vow8irkvU=:AmazonSES
+
+Hello! Original author of Mentat here.=0D=0A=0D=0AThe use of SQLite for M=
+entat was intended to be an accelerant for development: by leveraging SQL=
+ite we could ignore large parts of the tricky problem of getting bits on =
+disk across platforms, and also get things like transactionality and full=
+-text search for free.=0D=0A=0D=0AWe used the same leverage trick when we=
+ built the first proof of concept using Clojure and ClojureScript =E2=80=94=
+ we could run it in a JVM locally, compare to Datomic, and test in Node, =
+and entirely avoid having to build parsers and lexers. This allowed us to=
+ focus on the problem of representing a datom/log/index data model in SQL=
+ite.=0D=0A=0D=0AThe downside of using SQLite was a large amount of time w=
+riting code to compile queries =E2=80=94 which were written in much the s=
+ame language as Datomic=E2=80=99s =E2=80=94 into SQL for execution. That =
+Datomic compatibility also pushed us towards building an EDN parser in Ru=
+st, and we found some under-specified parts of Datomic, too.=0D=0A=0D=0AP=
+ortability to the web itself was not an initial goal, but it was somethin=
+g we thought about. With adequate resourcing, and the benefit of hindsigh=
+t, we would have instead built a chunk-based backing store abstraction, u=
+sing something like rkv/LMDB for embedded use and indexeddb on the web. W=
+e did not feel like we had the engineering capacity to take on building a=
+ truly portable, battle-tested storage layer like SQLite=E2=80=99s *and* =
+a bits-on-disk query executor =E2=80=94 I=E2=80=99ve done it in the past,=
+ and it takes a lot of time and is hard to get right.=0D=0A=0D=0AMentat a=
+ttempted to address a number of things that you didn=E2=80=99t cover in y=
+our brief post. A robust definition of identity, end-to-end encryption, a=
+nd evolution of schema (even simple schema like cardinality and uniquenes=
+s constraints on attributes, which are the foundation of identity) are th=
+ree such. Schema evolution becomes tremendously important when you have m=
+ultiple teams using the same storage, or a long-lived product =E2=80=94 t=
+he more useful the database is, the more likely you are to need this feat=
+ure!=0D=0A=0D=0AThose three things cascade into the problem of synchroniz=
+ation, which was my focus for a number of years.=0D=0A=0D=0ASynchronizati=
+on is a hard problem. Doing it without server assistance is even harder, =
+but is necessary for a portable and secure product. Synchronization of ge=
+neric data =E2=80=94 that is, data where the synchronization tool itself =
+doesn=E2=80=99t have its own model of the world =E2=80=94 is extremely tr=
+icky: everything from identifiers through to basic constraints needs to b=
+e aware of the possibility of change, and naturally it=E2=80=99s an itera=
+tive and distributed situation. Most of the approaches I found rely on a =
+server and are for systems that omit schema, and are typically incomplete=
+: e.g., detecting conflicts on individual attributes, leading to nonsensi=
+cal results, and offload to the developer the problem of separately handl=
+ing stable identifiers.=0D=0A=0D=0AI still have a persistent itch to star=
+t with a clean slate and solve this problem. No large company has the rig=
+ht incentives to solve the much harder problems faced by a truly secure s=
+tore, most hobbyists lack the understanding of the importance of managing=
+ change and concurrent collaboration over time, and the current trend is =
+towards using =E2=80=98dumb=E2=80=99 object/key-value stores or specialis=
+t systems like time-series databases, so I think it=E2=80=99s unlikely th=
+at anybody else is going to tackle all three in the same solution. Here=E2=
+=80=99s hoping!
+
+From eu@euandre.org Tue Sep 1 18:00:18 2020
+Received: from box.euandre.org (box.euandre.org [46.101.160.115])
+ by mail-b.sr.ht (Postfix) with ESMTPS id CD25BFF0C9
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 1 Sep 2020 18:00:17 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (2048-bit key) header.d=euandre.org header.i=@euandre.org header.b=q+qyYbsV
+Received: from authenticated-user (box.euandre.org [46.101.160.115])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
+ (No client certificate requested)
+ by box.euandre.org (Postfix) with ESMTPSA id F050CFCB45;
+ Tue, 1 Sep 2020 15:00:14 -0300 (-03)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=euandre.org; s=mail;
+ t=1598983216; bh=HeMLnOZIeRgOrLdGepra14BxoBWpvE7fXfdvB9MFFjw=;
+ h=From:To:Cc:Subject:In-Reply-To:References:Date:From;
+ b=q+qyYbsVLZrz3+swol7Y+Sc59kNUZkM1tSaXTcgLVIOp3CSJ+96XlP7i6oKZdeubH
+ fZIdw2fmFCMQotaXCnLJKBUNauX5Jrvt+4t2TrujIie5i5o/VSp08DxtxyWAmUWqdt
+ nPZrAand3styW0vvpWFYCZ7RjJKs4Ujek2G4H713X/eDOTooKalFNIXZ+A7t/xzq+9
+ mYojhphqHBDFQeqNKs9500YqetgTg4hsYfqRmGHzOs5ADw1dkQegQhjduodWvj2SpI
+ YeUu4OoXYDYUR9o+6VkXV+KtC4zDnoXyvgdSfHaLrahnkfiCBgg1Y6Kmcxy8iKnXjP
+ q5baz6m+cXC9Q==
+From: EuAndreh <eu@euandre.org>
+To: Richard Newman <rnewman@twinql.com>,
+ "~euandreh/public-inbox@lists.sr.ht" <~euandreh/public-inbox@lists.sr.ht>
+Cc:
+Subject: Re: The database I wish I had
+In-Reply-To: <010101744a592b75-1dce9281-f0b8-4226-9d50-fd2c7901fa72-000000@us-west-2.amazonses.com>
+References: <7454DA45-C5A4-4A54-8D02-1F3BD37726E9@twinql.com>
+ <010101744a592b75-1dce9281-f0b8-4226-9d50-fd2c7901fa72-000000@us-west-2.amazonses.com>
+Date: Tue, 01 Sep 2020 14:50:07 -0300
+Message-ID: <87sgc1xu7k.fsf@euandre.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: quoted-printable
+
+Richard Newman <rnewman@twinql.com> writes:
+
+> Hello! Original author of Mentat here.
+
+Hi there! You wouldn't be suprised if I told I recognize you by name,
+would you?
+
+Great job on Mentat, and please write more installments on "Thinking
+about Syncing" if possible =E2=98=BA=EF=B8=8F
+
+> The use of SQLite for Mentat was intended to be an accelerant for
+> development: by leveraging SQLite we could ignore large parts of the
+> tricky problem of getting bits on disk across platforms, and also get
+> things like transactionality and full-text search for free.
+
+For your goal of supporting other teams working on Firefox, SQLite is a
+perfectly reasonable choice.
+
+> We used the same leverage trick when we built the first proof of
+> concept using Clojure and ClojureScript =E2=80=94 we could run it in a JVM
+> locally, compare to Datomic, and test in Node, and entirely avoid
+> having to build parsers and lexers. This allowed us to focus on the
+> problem of representing a datom/log/index data model in SQLite.=20
+>
+> The downside of using SQLite was a large amount of time writing code
+> to compile queries =E2=80=94 which were written in much the same language=
+ as
+> Datomic=E2=80=99s =E2=80=94 into SQL for execution. That Datomic compatib=
+ility also
+> pushed us towards building an EDN parser in Rust, and we found some
+> under-specified parts of Datomic, too.=20
+
+Hmm, I'd be interested in learning more about those under-specified
+pitfalls to try to avoid them if possible.=20
+
+> Portability to the web itself was not an initial goal, but it was
+> something we thought about. With adequate resourcing, and the benefit
+> of hindsight, we would have instead built a chunk-based backing store
+> abstraction, using something like rkv/LMDB for embedded use and
+> indexeddb on the web. We did not feel like we had the engineering
+> capacity to take on building a truly portable, battle-tested storage
+> layer like SQLite=E2=80=99s *and* a bits-on-disk query executor =E2=80=94=
+ I=E2=80=99ve done it
+> in the past, and it takes a lot of time and is hard to get right.=20
+
+I don't expect to match SQLite's robustness for quite some time, and
+having plugable storage APIs makes me consider having a SQLite storage
+API to have as a correctness reference.
+
+What do you mean by "chunk-based backing store abstraction"? Could you
+elaborate further?
+
+> Mentat attempted to address a number of things that you didn=E2=80=99t co=
+ver
+> in your brief post. A robust definition of identity, end-to-end
+> encryption, and evolution of schema (even simple schema like
+> cardinality and uniqueness constraints on attributes, which are the
+> foundation of identity) are three such. Schema evolution becomes
+> tremendously important when you have multiple teams using the same
+> storage, or a long-lived product =E2=80=94 the more useful the database i=
+s,
+> the more likely you are to need this feature!=20
+
+What do you mean by "identity"? As in ":db.unique/value" and
+":db.unique/identity"? Couldn't you just use UUIDs for
+":db.unique/identity" values?
+
+For schema evolution wouldn't the "Grow your schema, and never break
+it"[0] approach be enough?
+
+On encryption, my first approach would be to see if git-remote-gcrypt[1]
+functionality can be translated, and have the local database be ignorant
+of all encryption whatsoever. However, I haven't explored it enough to
+be able to declare it to be possible, so I would welcome a more mature
+view on the subject.
+
+[0]: https://blog.datomic.com/2017/01/the-ten-rules-of-schema-growth.html=20
+[1]: https://spwhitton.name/tech/code/git-remote-gcrypt/
+
+> Those three things cascade into the problem of synchronization, which
+> was my focus for a number of years.=20
+>
+> Synchronization is a hard problem. Doing it without server assistance
+> is even harder, but is necessary for a portable and secure product.
+> Synchronization of generic data =E2=80=94 that is, data where the
+> synchronization tool itself doesn=E2=80=99t have its own model of the wor=
+ld =E2=80=94
+> is extremely tricky: everything from identifiers through to basic
+> constraints needs to be aware of the possibility of change, and
+> naturally it=E2=80=99s an iterative and distributed situation. Most of the
+> approaches I found rely on a server and are for systems that omit
+> schema, and are typically incomplete: e.g., detecting conflicts on
+> individual attributes, leading to nonsensical results, and offload to
+> the developer the problem of separately handling stable identifiers.=20
+
+If sound and robust, what's the downside of detecting conflicts on
+individual attributes?
+
+If an Datomic-like entity with 10 attributes has 4 of them change by
+different database instances, but only 1 change is on the same
+attribute, would this type of detection offer more granularity instead?
+
+Wouldn't the 5-tuple be the equivalent of a single line of code on git,
+and aggregates are built on top of it?
+
+From eu@euandre.org Tue Sep 1 18:00:22 2020
+Received: from box.euandre.org (box.euandre.org [46.101.160.115])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 097B1FF0C9
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 1 Sep 2020 18:00:22 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (2048-bit key) header.d=euandre.org header.i=@euandre.org header.b=bsr8vYSW
+Received: from authenticated-user (box.euandre.org [46.101.160.115])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
+ (No client certificate requested)
+ by box.euandre.org (Postfix) with ESMTPSA id C1FCCFD6BB;
+ Tue, 1 Sep 2020 15:00:19 -0300 (-03)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=euandre.org; s=mail;
+ t=1598983221; bh=VxPcwqhwGX+tAOR9QBGKCYzKImUIi+BqaSfmrBhsR6g=;
+ h=From:To:Cc:Subject:In-Reply-To:References:Date:From;
+ b=bsr8vYSWGIarl0IY64Ew+GLFGsQhZe971tKRr72HG9/UJru3FMjjA0VXK5LmyRGAh
+ ZjamXQpzM0biS0NNhY6mhfrvHXGKaGr19E6I5NDokNZFc/T/hXD1qMzY4fgEkDmayl
+ fteqI7T//DT06yseOXOkfrKzNCDWKmSmlsOo9PZgSsAicwEsqxb/UqehXN6ON9vpG4
+ VCXsOW8y8/aQGR7/WL2M9rkTd2cf6p/csDiUTkGf8YMFu8Qz/u5MgzY/14LCShXzfL
+ S+iz36YnA/kIu1RjrOzR22rPPcQRfZE1MyS9B+h9QeHHWR7A1SnsfE8WnlhBCCRxU7
+ ZcKgKfiihKPxQ==
+From: EuAndreh <eu@euandre.org>
+To: Seph Gentle <me@josephg.com>, ~euandreh/public-inbox@lists.sr.ht
+Cc:
+Subject: Re: Making a database
+In-Reply-To: <9e513706-7ebc-4a40-8e5e-a5f94d84bc77@www.fastmail.com>
+References: <d78064d2-5441-4275-96d9-463a08dfd7a9@www.fastmail.com>
+ <87ft81zvi0.fsf@euandre.org>
+ <9e513706-7ebc-4a40-8e5e-a5f94d84bc77@www.fastmail.com>
+Date: Tue, 01 Sep 2020 14:56:23 -0300
+Message-ID: <87pn75xtx4.fsf@euandre.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: quoted-printable
+
+> You kinda gave a good description of them in your post already. They
+> work by requiring all peers to resolve concurrent edits to an object
+> the same way. That could be last-writer-wins (based on time stamps
+> like you said in your article). Or it could store all conflicting
+> versions together to be resolved by the next reader (Eg riak). Or it
+> could do something more clever (like merge changes using automerge or
+> y.js or the like). Git is sort of a CRDT - it=E2=80=99s just it=E2=80=99s=
+ 3 way merge
+> algorithm is a bit of a dogs breakfast as far as these things go.=20
+
+My ignorace is really on the applicability of it. The "store all
+conflicting versions together or to be resolved by the next reader"
+sounds like what I'm thinking about, so maybe I'm trying to re-invent
+CRDTs after all.
+
+I've just bumped the priority of this topic to better understand those
+nuances.=20
+
+From rnewman@twinql.com Tue Sep 1 19:30:22 2020
+Received: from a27-132.smtp-out.us-west-2.amazonses.com (a27-132.smtp-out.us-west-2.amazonses.com [54.240.27.132])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 67FA4FF130
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 1 Sep 2020 19:30:21 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (1024-bit key) header.d=twinql.com header.i=@twinql.com header.b=HXFRpUpY;
+ dkim=pass (1024-bit key) header.d=amazonses.com header.i=@amazonses.com header.b=Fknn4Oqd
+DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/simple;
+ s=qvs2k2sd2ivtrflyu2nswmravuqizjml; d=twinql.com; t=1598988619;
+ h=Subject:From:To:Cc:Date:Mime-Version:Content-Type:Content-Transfer-Encoding:In-Reply-To:References:Message-Id;
+ bh=Y4wqQqAQDJ1Sa/E5OhrtvJ+Yk3Uij2laWmG/aD8wLfw=;
+ b=HXFRpUpY+Dt9MJW6Z60km/fTpPfIYak8NKLDvv6UeroaBfvWClO0loOtSYksmT88
+ dWeTwau14008g4YQwDwEY8msngXXDx/9W05ArClEsNXDygcmOYea3eHhqVMcvS/dtuA
+ QYV4fjMoIj9QsGiHAP/QKjampkgK6nHx+Qfhlti4=
+DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/simple;
+ s=hsbnp7p3ensaochzwyq5wwmceodymuwv; d=amazonses.com; t=1598988619;
+ h=Subject:From:To:Cc:Date:Mime-Version:Content-Type:Content-Transfer-Encoding:In-Reply-To:References:Message-Id:Feedback-ID;
+ bh=Y4wqQqAQDJ1Sa/E5OhrtvJ+Yk3Uij2laWmG/aD8wLfw=;
+ b=Fknn4Oqdb29M5jY4pJV5V9gX2yK3n+WkgnRWmjQl46DdAzcaL4dHoZvIbk8cQxSt
+ v6z1Rdu/S1IvHVSIHLYloebUmPBBfmLzEksz9sh5yCGO4IJhfdIEk0CPgPvNNkXC7as
+ DpyhbHSQzUMFT1EBYwmgHNK/DQMSwe+2Jy7SpfPg=
+Subject: Re: The database I wish I had
+From: =?UTF-8?Q?Richard_Newman?= <rnewman@twinql.com>
+To: =?UTF-8?Q?EuAndreh?= <eu@euandre.org>
+Cc: =?UTF-8?Q?=7Eeuandreh/public-inbox=40lists=2Esr=2Eht?=
+ <~euandreh/public-inbox@lists.sr.ht>
+Date: Tue, 1 Sep 2020 19:30:19 +0000
+Mime-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+In-Reply-To: <87sgc1xu7k.fsf@euandre.org>
+References: <7454DA45-C5A4-4A54-8D02-1F3BD37726E9@twinql.com>
+ <010101744a592b75-1dce9281-f0b8-4226-9d50-fd2c7901fa72-000000@us-west-2.amazonses.com>
+ <87sgc1xu7k.fsf@euandre.org>
+ <9652AAE0-10DF-4D13-ABE8-B30667BE27DD@twinql.com>
+X-Priority: 3 (Normal)
+X-Mailer: Amazon WorkMail
+Thread-Index: AQHWgHcPJ4udfXYrRaOwhoO7EcsDJQAErSAlAAfQ7+g=
+Thread-Topic: The database I wish I had
+X-Original-Mailer: Apple Mail (2.3445.104.14)
+X-Wm-Sent-Timestamp: 1598988618
+Message-ID: <010101744b260d62-1bb508ab-647f-4877-bdf9-f988667d56e1-000000@us-west-2.amazonses.com>
+X-SES-Outgoing: 2020.09.01-54.240.27.132
+Feedback-ID: 1.us-west-2.An468LAV0jCjQDrDLvlZjeAthld7qrhZr+vow8irkvU=:AmazonSES
+
+> Hi there! You wouldn't be suprised if I told I recognize you by name,=0D=
+=0A> would you=3F=0D=0A>=20=0D=0A> Great job on Mentat, and please write =
+more installments on "Thinking=0D=0A> about Syncing" if possible =E2=98=BA=
+=EF=B8=8F=0D=0A=0D=0AOne day I do hope to return to them! Thank you for t=
+he kind words.=0D=0A=0D=0A=0D=0A> Hmm, I'd be interested in learning more=
+ about those under-specified=0D=0A> pitfalls to try to avoid them if poss=
+ible.=20=0D=0A=0D=0AMostly the usual stuff you get when a thing is define=
+d by its implementation:=0D=0A=0D=0Ahttps://github.com/mozilla/mentat/pul=
+l/665=0D=0Ahttps://github.com/mozilla/mentat/issues/412=0D=0A=0D=0AThere =
+are probably more if you poke around the issue tracker.=0D=0A=0D=0A=0D=0A=
+> What do you mean by "chunk-based backing store abstraction"=3F Could yo=
+u=0D=0A> elaborate further=3F=0D=0A=0D=0AI'm familiar with the way Allegr=
+oGraph does storage, and fairly familiar with how Datomic does it.=0D=0A=0D=
+=0AIn short, a datom store like this consists of the log (all datoms, wit=
+h retractions, ordered by ID), and some current state (which Mentat calle=
+d `datoms`), which consists of all current asserted datoms.=0D=0A=0D=0ATh=
+e current state is typically stored in a number of redundant permutations=
+ to support querying. In AllegroGraph (subject, predicate, object, graph,=
+ id), it would maintain multiple indices: `spogi`, `posgi`, etc. Each of =
+these store the same datoms in different orders.=0D=0A=0D=0AIf we have th=
+e data=0D=0A=0D=0A```=0D=0A :bob foaf:knows :alice=0D=0A :alice foaf:na=
+me "Alice"=0D=0A :bob foaf:name "Bob"=0D=0A :bob foaf:age 26=0D=0A :al=
+ice foaf:age 30=0D=0A```=0D=0A=0D=0Ait might appear in the `spogi` index =
+as:=0D=0A=0D=0A```=0D=0A :alice foaf:age 30=0D=0A :alice foaf:name "Ali=
+ce"=0D=0A :bob foaf:age 26=0D=0A :bob foaf:knows :alice=0D=0A :bob foa=
+f:name "Bob"=0D=0A```=0D=0A=0D=0Aand in the `posgi` index as:=0D=0A=0D=0A=
+```=0D=0A foaf:age 26 :bob=0D=0A foaf:age 30 :alice=20=0D=0A foaf:name=
+ "Alice" :alice=20=0D=0A foaf:name "Bob" :bob=20=0D=0A foaf:knows :alic=
+e :bob=20=0D=0A```=0D=0A=0D=0ANaturally these would be using efficient (i=
+nterned) sortable representations of each term, with rows of fixed size, =
+memory mapped on disk.=0D=0A=0D=0AThis allows you to implement query patt=
+ern execution as an index walk.=0D=0A=0D=0A`SELECT =3Fs WHERE =3Fs foaf:n=
+ame 'Alice'`=0D=0A=0D=0Afor example turns into a walk on `posgi`: you fin=
+d the spot that corresponds to `foaf:name`, find the next place which is =
+`Alice`, and then read out the `s`.=0D=0A=0D=0AWhen you insert data, you =
+typically don't want to immediately regenerate all of these indices. Inst=
+ead you accrue new *chunks*, which are just all of the datoms asserted in=
+ some window of time, or within a particular transaction. The query engin=
+e walks all of the relevant chunks and merges the results together. One c=
+hunk might have Bob's name, and one might have Alice's.=0D=0A=0D=0ATypica=
+lly _metaindices_ are used to help the query engine navigate which chunks=
+ to use.=0D=0A=0D=0AAllegroGraph periodically merges chunks together: a f=
+ully merged store is faster to query. Datomic goes the other way: it buil=
+ds new merged chunks and keeps the old ones around, because those old chu=
+nks represent the state of the database at a point in time! This is how y=
+ou efficiently query past states in Datomic: it trades space in your Cass=
+andra cluster for the ability to grab the right chunks for any query.=0D=0A=
+=0D=0AYou can easily imagine that any system that allows you to put a nam=
+e on some region of data can be used to store these chunks, and their met=
+aindices, and the log itself. The only requirement is that the chunk stor=
+e be durable, and ideally replicated and relatively fast to read from.=0D=
+=0A=0D=0A=0D=0A> What do you mean by "identity"=3F As in ":db.unique/valu=
+e" and=0D=0A> ":db.unique/identity"=3F Couldn't you just use UUIDs for=0D=
+=0A> ":db.unique/identity" values=3F=0D=0A=0D=0AIf on Computer A I visit =
+https://aclu.org, and on Computer B I visit https://aclu.org, my two comp=
+uters will each give the same history item a different internal identifie=
+r =E2=80=94 1234 and 4567, say.=0D=0A=0D=0AWhen I sync them, I need the d=
+ata store to know that the visits I made to each page _were visits to the=
+ same thing_.=0D=0A=0D=0AThere is more value to it than that (e.g., Datom=
+ic's "lookup refs"), but it's important to recognize that identity is _do=
+main specific_, and it is often _compound_ and even contextual or conditi=
+onal.=0D=0A=0D=0AA large part of synchronization, and a large part of dat=
+a modeling in general, consists of figuring out exactly what it is you're=
+ trying to record data about, and how to identify it. It's tremendously i=
+mportant in NoSQL systems because there is no foreign key constraint to h=
+elp you, and changing an identifier later is difficult. It's just as impo=
+rtant in syncing systems because these identifiers are the only thing you=
+ have to help you merge data =E2=80=94 to zip two graphs together.=0D=0A=0D=
+=0A=0D=0A> For schema evolution wouldn't the "Grow your schema, and never=
+ break=0D=0A> it"[0] approach be enough=3F=0D=0A=0D=0AIt definitely helps=
+; this is how teams generally try to work with DynamoDB, for example, tho=
+ugh there the schema is implicit rather than explict.=0D=0A=0D=0AHowever,=
+ you inevitably have data migrations to worry about=E2=80=A6 not to menti=
+on that syncing makes this a distributed system, and so you cannot easily=
+ coordinate schema changes in more than one place, and they will race wit=
+h new data!=0D=0A=0D=0AThere's also the argument that coordinating change=
+ in consumers and engineers is sometimes harder than evolving a vocabular=
+y!=0D=0A=0D=0AIt's a complicated topic.=0D=0A=0D=0A=0D=0A> On encryption,=
+ my first approach would be to see if git-remote-gcrypt[1]=0D=0A> functio=
+nality can be translated, and have the local database be ignorant=0D=0A> =
+of all encryption whatsoever. However, I haven't explored it enough to=0D=
+=0A> be able to declare it to be possible, so I would welcome a more matu=
+re=0D=0A> view on the subject.=0D=0A=0D=0AI have been relatively happy wi=
+th SQLCipher, and I think the idea of block-level encryption is a good pl=
+ace to put the abstraction. The thing you lose with any of these mechanis=
+ms is, by definition, the ability for a central entity to help you with c=
+onflict resolution. The best you can get is ordering of opaque chunks and=
+ collision detection.=0D=0A=0D=0A=0D=0A> If sound and robust, what's the =
+downside of detecting conflicts on=0D=0A> individual attributes=3F=0D=0A=0D=
+=0AIt relies on extensive reification to achieve the property that we wan=
+t: that each semantically conflicting write results in a conflicting attr=
+ibute change.=0D=0A=0D=0AAs a contrived example example: notes have both =
+a title and a body.=0D=0A=0D=0AA user has two computers.=0D=0A=0D=0AOn Co=
+mputer A, the user decides to change their first note to something else, =
+updating both the title and body.=0D=0A=0D=0AOn Computer B, they do the s=
+ame thing to a different title and body.=0D=0A=0D=0AWith attribute-based =
+conflict resolution it is possible to end up with a note that has a combi=
+nation of A's title and B's body =E2=80=94 a combination that never exist=
+ed on either computer.=0D=0A=0D=0AThis gets even worse when you treat del=
+etion specially, when new assertions don't conflict but do alter domain s=
+emantics, or when some of the conflicts affect the way the rest of the da=
+ta is interpreted (e.g., when you have an identity property or a lookup r=
+ef).=0D=0A=0D=0AThe only data modeling solution to this case is to introd=
+uce an immutable 'note node', and have the _change_ be to have Note One p=
+oint to a different note node =E2=80=94 one attribute change!=0D=0A=0D=0A=
+In a JSON-ish system like Pouch you'd swap an entire JS object within the=
+ same attribute, rather than having two different attributes.=0D=0A=0D=0A=
+Developers =E2=80=94 at least, non-Clojure developers =E2=80=94 rarely th=
+ink in these terms, and now everywhere else you need to make really caref=
+ul decisions about whether you point to the 'note' or the 'node'. Getting=
+ it wrong means dangling pointers.=0D=0A=0D=0AUsing per-attribute automat=
+ic merging without having an expert ontologist model your domain is a rec=
+ipe for weird edge-case conflict resolution bugs that result in data soup=
+ or data loss.=0D=0A=20=0D=0A=0D=0A> If an Datomic-like entity with 10 at=
+tributes has 4 of them change by=0D=0A> different database instances, but=
+ only 1 change is on the same=0D=0A> attribute, would this type of detect=
+ion offer more granularity instead=3F=0D=0A=0D=0AMy thinking is mostly: y=
+ou need some kind of schema mechanism to be able to recognize which prope=
+rties can be reconciled safely, and you also cannot examine each tuple in=
+dependently in many cases. It's complicated. Something like Dropbox's dat=
+astore conflict resolution (if I remember right) is fine much of the time=
+, but wrong some of the time.=0D=0A=0D=0A=0D=0A> Wouldn't the 5-tuple be =
+the equivalent of a single line of code on git,=0D=0A> and aggregates are=
+ built on top of it=3F=0D=0A=0D=0AEven source code merging isn't this sim=
+ple, no=3F A conceptual operation like "add this line underneath this oth=
+er line" is the thing you're trying to represent, but that line has _cont=
+ext_ =E2=80=94 it might have moved, the file might have been renamed, or =
+someone else already put a line there.=0D=0A=0D=0AYou've probably seen th=
+is with b0rked merges in software version control, where the result of a =
+merge won't compile because of duplicate identifiers thanks to hunks movi=
+ng around=E2=80=A6=0D=0A=0D=0ABut trying to apply text-based merging to s=
+emantic data only works if there are no constraints in your data, or if y=
+ou have fast-forward merges =E2=80=94 merging can violate uniqueness prop=
+erties in all kinds of ways.=20=0D=0A
+
+From me@josephg.com Tue Sep 1 22:13:15 2020
+Received: from wout4-smtp.messagingengine.com (wout4-smtp.messagingengine.com [64.147.123.20])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 53A0EFF124
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 1 Sep 2020 22:13:14 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (1024-bit key) header.d=josephg.com header.i=@josephg.com header.b=2Kj6nHib;
+ dkim=fail reason="key not found in DNS" (0-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=eH2pUNJ6
+Received: from compute1.internal (compute1.nyi.internal [10.202.2.41])
+ by mailout.west.internal (Postfix) with ESMTP id CB8F377E;
+ Tue, 1 Sep 2020 18:13:12 -0400 (EDT)
+Received: from imap35 ([10.202.2.85])
+ by compute1.internal (MEProxy); Tue, 01 Sep 2020 18:13:12 -0400
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=josephg.com; h=
+ mime-version:message-id:in-reply-to:references:date:from:to
+ :subject:content-type:content-transfer-encoding; s=mesmtp; bh=Q9
+ PUfqkL153ICDLSRfraWMnWRXOclTHibxQiOyAV3lA=; b=2Kj6nHibSzEldHCTUN
+ lgQhb0piuopatnU3wV94ozxZ95sUW7zyCnSE/s9gqFgTgYg0o2ZeqMffp92mRZis
+ frDqWjWNhxv4WN78vO8dcwA8U/a+Uwp4cfxUyjuyY/zw7NHHM8LORDQCfembOUFj
+ O6a5SW/fQRgU9XJIemyvxSDlM=
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=
+ messagingengine.com; h=content-transfer-encoding:content-type
+ :date:from:in-reply-to:message-id:mime-version:references
+ :subject:to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender
+ :x-sasl-enc; s=fm3; bh=Q9PUfqkL153ICDLSRfraWMnWRXOclTHibxQiOyAV3
+ lA=; b=eH2pUNJ6n7/Ng/S9WTH+X0QePTCyzr5SjXdLgG3eN4a4yktWOtaMr44AU
+ nW7SE782cX3xP2TEyvVSJ58tnx6r+6M5Jzauja+b2OJiTtnrJyWfIZ6iK/URp/y2
+ v++7NoAfWmyXOfUQAiLbK92FCRrRkR8nGx0EksjMInRiDUozhHcg1mRYRipvULUx
+ UsaQ0gtdGNDm76c6VwZU7YwBxssaeVQNP8ROlvm/y/KC3gpLYILLdNJPcI/r1846
+ FrhxPQLgAQIe+4UV6NYtjNIRbFNPjdIWHZvWZ4LdMRPQ4yFdbpD6aHhKa+wLBPyB
+ sIgGBCGwzMhfTDSRZQw8DrWpcx21w==
+X-ME-Sender: <xms:dsdOX2zlGD9BWCFDAMf7fpxK8tc8KYm5XDsDnDguZM2Sy8xflOH9kA>
+ <xme:dsdOXyTTPqu0-Pa3UYN2l_7sXVE04FX8AClSMYgSkAoeLeTspezWpQUlQYsja54Jc
+ wrAfUl58HrBhQRp6w>
+X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeduiedrudefkedgtdeiucetufdoteggodetrfdotf
+ fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen
+ uceurghilhhouhhtmecufedttdenucenucfjughrpefofgggkfgjfhffhffvufgtgfesth
+ hqredtreerjeenucfhrhhomhepfdfuvghphhcuifgvnhhtlhgvfdcuoehmvgesjhhoshgv
+ phhhghdrtghomheqnecuggftrfgrthhtvghrnhepteeugfeufeeludekieduiefggfehhf
+ eiffelfeetteejheehveelteffhefffeeunecuffhomhgrihhnpehklhgvphhpmhgrnhhn
+ rdgtohhmnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomh
+ epmhgvsehjohhsvghphhhgrdgtohhm
+X-ME-Proxy: <xmx:dsdOX4WWAjDIjt91xpvOW8wGgg-eiGlTvG2yYFGPVBe2qPkuU169eg>
+ <xmx:dsdOX8gA1SFXeEhDY-io8js7YHP8z7rKmyRtM81cBA0MFgLBRHhtow>
+ <xmx:dsdOX4DJCCv4jHxex2u1y3w6cvLGKhAfSjHmtdhvaRTHlJcfSu4J5w>
+ <xmx:eMdOX7M12Nj2n8pxrtLuGPyovCEq1wtzixBdJWACKU6e8qYcMiQKKg>
+Received: by mailuser.nyi.internal (Postfix, from userid 501)
+ id 762F414C00B3; Tue, 1 Sep 2020 18:13:10 -0400 (EDT)
+X-Mailer: MessagingEngine.com Webmail Interface
+User-Agent: Cyrus-JMAP/3.3.0-248-gcd102cb-fm-20200901.001-gcd102cb9
+Mime-Version: 1.0
+Message-Id: <bab08df6-be8e-47bd-a254-7b86c41a9b47@www.fastmail.com>
+In-Reply-To: <87pn75xtx4.fsf@euandre.org>
+References: <d78064d2-5441-4275-96d9-463a08dfd7a9@www.fastmail.com>
+ <87ft81zvi0.fsf@euandre.org>
+ <9e513706-7ebc-4a40-8e5e-a5f94d84bc77@www.fastmail.com>
+ <87pn75xtx4.fsf@euandre.org>
+Date: Wed, 02 Sep 2020 08:12:45 +1000
+From: "Seph Gentle" <me@josephg.com>
+To: EuAndreh <eu@euandre.org>, ~euandreh/public-inbox@lists.sr.ht
+Subject: Re: Making a database
+Content-Type: text/plain;charset=utf-8
+Content-Transfer-Encoding: quoted-printable
+
+On Wed, Sep 2, 2020, at 3:56 AM, EuAndreh wrote:
+> > You kinda gave a good description of them in your post already. They=
+
+> > work by requiring all peers to resolve concurrent edits to an object=
+
+> > the same way. That could be last-writer-wins (based on time stamps
+> > like you said in your article). Or it could store all conflicting
+> > versions together to be resolved by the next reader (Eg riak). Or it=
+
+> > could do something more clever (like merge changes using automerge o=
+r
+> > y.js or the like). Git is sort of a CRDT - it=E2=80=99s just it=E2=80=
+=99s 3 way merge
+> > algorithm is a bit of a dogs breakfast as far as these things go.=20=
+
+>=20
+> My ignorace is really on the applicability of it. The "store all
+> conflicting versions together or to be resolved by the next reader"
+> sounds like what I'm thinking about, so maybe I'm trying to re-invent
+> CRDTs after all.
+>=20
+> I've just bumped the priority of this topic to better understand those=
+
+> nuances.=20
+
+This is worth watching if you haven=E2=80=99t seen it. Martin is at the =
+forefront of the field - but this gives you a sense of where the researc=
+h tech is at:
+
+https://martin.kleppmann.com/2020/07/06/crdt-hard-parts-hydra.html
+
+From eu@euandre.org Wed Sep 2 00:00:16 2020
+Received: from box.euandre.org (box.euandre.org [46.101.160.115])
+ by mail-b.sr.ht (Postfix) with ESMTPS id E53C0FF14B
+ for <~euandreh/public-inbox@lists.sr.ht>; Wed, 2 Sep 2020 00:00:15 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (2048-bit key) header.d=euandre.org header.i=@euandre.org header.b=Ts/gQOWS
+Received: from authenticated-user (box.euandre.org [46.101.160.115])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
+ (No client certificate requested)
+ by box.euandre.org (Postfix) with ESMTPSA id E5F6DFC052;
+ Tue, 1 Sep 2020 21:00:11 -0300 (-03)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=euandre.org; s=mail;
+ t=1599004813; bh=GS02iBK+iDwoGCfVqsjbqMenu+Wpn9OO6GpSXGHcpsY=;
+ h=From:To:Cc:Subject:In-Reply-To:References:Date:From;
+ b=Ts/gQOWSiM5Vq/pHxaNuXKM4mXoy3VW9rD5mv/uLMGHYQcslItj1TNcvxINLzU/yj
+ ZaBmqtENnSd9AXap4PHxsFz48JlDI5oYcn98HE9nLsAZrD7/9GkhJMNjRDsf0ODAl9
+ W7do6QS8LRAZo0RhTguAzgoTJXf/IxuRqykG9Nm8imNuLDN4T04EbGvRw4ibN4UhlT
+ DED1G5O4Ul96BT6mY/im9tBQPGGNURLLcNcUkqG4fpddJrg58/yH9JOPMhnNhatEiV
+ 39+m3xyL2rM8HSBfKun87TXjE6CD4/kQ0U6OpSB9ej/4zdNJ7Ei9K5NSuF78KRvkWJ
+ qHKRHqP/84rQw==
+From: EuAndreh <eu@euandre.org>
+To: Seph Gentle <me@josephg.com>, ~euandreh/public-inbox@lists.sr.ht
+Cc:
+Subject: Re: Making a database
+In-Reply-To: <bab08df6-be8e-47bd-a254-7b86c41a9b47@www.fastmail.com>
+References: <d78064d2-5441-4275-96d9-463a08dfd7a9@www.fastmail.com>
+ <87ft81zvi0.fsf@euandre.org>
+ <9e513706-7ebc-4a40-8e5e-a5f94d84bc77@www.fastmail.com>
+ <87pn75xtx4.fsf@euandre.org>
+ <bab08df6-be8e-47bd-a254-7b86c41a9b47@www.fastmail.com>
+Date: Tue, 01 Sep 2020 20:18:08 -0300
+Message-ID: <87eenlxf0v.fsf@euandre.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: quoted-printable
+
+"Seph Gentle" <me@josephg.com> writes:
+
+> This is worth watching if you haven=E2=80=99t seen it. Martin is at the f=
+orefront of the field - but this gives you a sense of where the research te=
+ch is at:
+>
+> https://martin.kleppmann.com/2020/07/06/crdt-hard-parts-hydra.html
+
+Coincidentally, that's the third item on my list =F0=9F=98=AC
+
+From jeff@rhyason.org Wed Sep 2 01:35:17 2020
+Received: from mx3.rhyason.org (mx3.rhyason.org [104.245.39.21])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 6C175FF0E7
+ for <~euandreh/public-inbox@lists.sr.ht>; Wed, 2 Sep 2020 01:35:17 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (1024-bit key) header.d=rhyason.org header.i=@rhyason.org header.b=weEVa7Zv
+Received: from rhyason.org (d173-183-32-226.bchsia.telus.net [173.183.32.226])
+ by mx3.rhyason.org (8.15.2/8.15.2) with ESMTP id 0821ZI7G073010
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 1 Sep 2020 21:35:19 -0400 (EDT)
+ (envelope-from jeff@rhyason.org)
+Received: from rhyason.org (localhost [127.0.0.1]);
+ by rhyason.org (OpenSMTPD) with ESMTP id 4aa4e076;
+ for <~euandreh/public-inbox@lists.sr.ht>;
+ Tue, 1 Sep 2020 19:35:08 -0600 (MDT)
+DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=rhyason.org; h=from
+ :content-type:content-transfer-encoding:mime-version:subject
+ :message-id:date:to; s=selector1; bh=61RPMANfo/dmUC5BfkkO8NNOMrg
+ =; b=weEVa7ZvWqlxTjSrAxMCoiPy3GBkyNONviKR6lPNlGM3/eV5+0g3KhjKIKs
+ g5+xXXB1G69y1xvNyo5hpOYY4Gk51OeGCQ/QxtE3tD2IgTxYjWlsB5SuNTrWI6Kd
+ Onz8i5Fdan7ZigJ1ambirCGyI/IWPb8Q8gG7+i76Wb2vj3W8=
+DomainKey-Signature: a=rsa-sha1; c=nofws; d=rhyason.org; h=from
+ :content-type:content-transfer-encoding:mime-version:subject
+ :message-id:date:to; q=dns; s=selector1; b=mpt0BK56C+LRHqEw8Bx8M
+ y7nJWXZqD5kxPw4J7wh3mh8VcMYiBuA/2BpbrT7wYH9IxpMGFjlSP/lIZIHUPFTt
+ cpbgqiL6CrW1zvP4CJ+6ygSQkfnsEGekcmEgLGa/E49lcKxA/y7Ep1G7maJJIZxN
+ 3OqgdtRLK1MiajxwW/MvBA=
+Received: from [10.0.88.117] (10.0.88.117 [10.0.88.117]);
+ by rhyason.org (OpenSMTPD) with ESMTPSA id d7674b26;
+ TLS version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO;
+ for <~euandreh/public-inbox@lists.sr.ht>;
+ Tue, 1 Sep 2020 19:35:08 -0600 (MDT)
+From: Jeff Rhyason <jeff@rhyason.org>
+Content-Type: text/plain;
+ charset=us-ascii
+Content-Transfer-Encoding: quoted-printable
+Mime-Version: 1.0 (Mac OS X Mail 13.4 \(3608.120.23.2.1\))
+Subject: building MSTs->CRDTs-> Re: The database I wish I had
+Message-Id: <99C92E06-E0BC-4DB0-BF63-20FD44DC4062@rhyason.org>
+Date: Tue, 1 Sep 2020 18:35:08 -0700
+To: ~euandreh/public-inbox@lists.sr.ht
+X-Mailer: Apple Mail (2.3608.120.23.2.1)
+
+It's amazing to see this discussion. Let's keep it going!
+
+My friend and I started Indelible[1] (now defunct) for a lot of the same =
+reasons you want to build a datastore -- there aren't enough options for =
+a datastore that's immutable, relational, highly-vaailable, and =
+private.. We're still super-passionate about the ideas, love Datomic but =
+want something a bit different, decided not to commercialize it and =
+start more simply with just the data structure. We don't want a huge =
+thing, just a library, to keep coordination down; the complexity doesn't =
+spread out of the client.
+
+Where we are at now is we have a great versioned immutable B-Tree-like =
+structure that is the foundation for the verioned and diffable database =
+we want. FWIW The Go implementation of the tree is at =
+https://github.com/jrhy/mast . We have plans to do a CRDT based on it, =
+possibly add an SQL layer, but frankly, having a strongly-consistent, =
+verioned + diffable tree, is pretty good for now. We'd love to =
+collaborate!
+
+
+
+[1] Indelible API Reference =
+http://htmlpreview.github.io/?https://github.com/indeliblesystems/indelibl=
+e/blob/master/doc/000-API-ref.html
+
+
+From eu@euandre.org Wed Sep 2 08:00:14 2020
+Received: from box.euandre.org (box.euandre.org [46.101.160.115])
+ by mail-b.sr.ht (Postfix) with ESMTPS id E2229FF148
+ for <~euandreh/public-inbox@lists.sr.ht>; Wed, 2 Sep 2020 08:00:13 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (2048-bit key) header.d=euandre.org header.i=@euandre.org header.b=Gv++l+b4
+Received: from authenticated-user (box.euandre.org [46.101.160.115])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
+ (No client certificate requested)
+ by box.euandre.org (Postfix) with ESMTPSA id BBE37FC052;
+ Wed, 2 Sep 2020 05:00:10 -0300 (-03)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=euandre.org; s=mail;
+ t=1599033612; bh=L0rF3hNESOLacI6plS3F0G5McLpB7MuPNAKgItTuBzg=;
+ h=From:To:Cc:Cc:Subject:In-Reply-To:References:Date:From;
+ b=Gv++l+b4mbVug6D7ywconYDZ/EzLKxn27zY5F1FhWm2A97mClGgf20SVzjqrxZNOE
+ LHVxQX1qwB7I/MzwWDf0ACHFQsOu0Y2VgQyffVE0BHInz/TPbXlv9Qbfe1L9yRFvD7
+ Z+3BoX5//m/1Na1ZKeuP1KT5neo8dF75wyyUhG0Zh/hFEDa1ILdR8wrnovtouqmwxA
+ JoMH2EzjLJ8ad2pox2VU0BskunOUvIX/CRXsrnXDCxybseFECE7h2jghegme7MlLae
+ /kvzmmg6Ow1z/lzvftKWGJRPb47MvQYSFIbnLb2kkkhTb6evJ5eXPO2nPY6/vJ8t6c
+ jhgzwfN/DzLeA==
+From: EuAndreh <eu@euandre.org>
+To: Richard Newman <rnewman@twinql.com>
+Cc: "~euandreh/public-inbox@lists.sr.ht" <~euandreh/public-inbox@lists.sr.ht>
+Cc:
+Subject: Re: The database I wish I had
+In-Reply-To: <010101744b260d62-1bb508ab-647f-4877-bdf9-f988667d56e1-000000@us-west-2.amazonses.com>
+References: <7454DA45-C5A4-4A54-8D02-1F3BD37726E9@twinql.com>
+ <010101744a592b75-1dce9281-f0b8-4226-9d50-fd2c7901fa72-000000@us-west-2.amazonses.com>
+ <87sgc1xu7k.fsf@euandre.org>
+ <9652AAE0-10DF-4D13-ABE8-B30667BE27DD@twinql.com>
+ <010101744b260d62-1bb508ab-647f-4877-bdf9-f988667d56e1-000000@us-west-2.amazonses.com>
+Date: Wed, 02 Sep 2020 04:30:43 -0300
+Message-ID: <878sdsy6sc.fsf@euandre.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: quoted-printable
+
+This is some rich discussion, thanks for engaging!
+
+Richard Newman <rnewman@twinql.com> writes:
+
+> Mostly the usual stuff you get when a thing is defined by its
+> implementation:
+>
+> https://github.com/mozilla/mentat/pull/665
+> https://github.com/mozilla/mentat/issues/412
+>
+> There are probably more if you poke around the issue tracker.
+
+Thanks for the references.
+
+> I'm familiar with the way AllegroGraph does storage, and fairly
+> familiar with how Datomic does it.
+
+I've used Common Lisp a lot before, and I heard a lot about
+AllegroGraph, but I've never worked with it. Would you recommed it as a
+place to take inspirations from?
+
+> In short, a datom store like this consists of the log (all datoms,
+> with retractions, ordered by ID), and some current state (which Mentat
+> called `datoms`), which consists of all current asserted datoms.
+>
+> The current state is typically stored in a number of redundant
+> permutations to support querying. In AllegroGraph (subject, predicate,
+> object, graph, id), it would maintain multiple indices: `spogi`,
+> `posgi`, etc. Each of these store the same datoms in different orders.
+>
+> If we have the data
+>
+> ```
+> :bob foaf:knows :alice
+> :alice foaf:name "Alice"
+> :bob foaf:name "Bob"
+> :bob foaf:age 26
+> :alice foaf:age 30
+> ```
+>
+> it might appear in the `spogi` index as:
+>
+> ```
+> :alice foaf:age 30
+> :alice foaf:name "Alice"
+> :bob foaf:age 26
+> :bob foaf:knows :alice
+> :bob foaf:name "Bob"
+> ```
+>
+> and in the `posgi` index as:
+>
+> ```
+> foaf:age 26 :bob
+> foaf:age 30 :alice
+> foaf:name "Alice" :alice
+> foaf:name "Bob" :bob
+> foaf:knows :alice :bob
+> ```
+
+These look just like the EAVT and AVET indexes in Datomic.
+
+> Naturally these would be using efficient (interned) sortable
+> representations of each term, with rows of fixed size, memory mapped
+> on disk.
+>
+> This allows you to implement query pattern execution as an index walk.
+>
+> `SELECT ?s WHERE ?s foaf:name 'Alice'`
+>
+> for example turns into a walk on `posgi`: you find the spot that
+> corresponds to `foaf:name`, find the next place which is `Alice`, and
+> then read out the `s`.
+>
+> When you insert data, you typically don't want to immediately
+> regenerate all of these indices. Instead you accrue new *chunks*,
+> which are just all of the datoms asserted in some window of time, or
+> within a particular transaction. The query engine walks all of the
+> relevant chunks and merges the results together. One chunk might have
+> Bob's name, and one might have Alice's.
+>
+> Typically _metaindices_ are used to help the query engine navigate
+> which chunks to use.
+>
+> AllegroGraph periodically merges chunks together: a fully merged store
+> is faster to query. Datomic goes the other way: it builds new merged
+> chunks and keeps the old ones around, because those old chunks
+> represent the state of the database at a point in time! This is how
+> you efficiently query past states in Datomic: it trades space in your
+> Cassandra cluster for the ability to grab the right chunks for any
+> query.
+>
+> You can easily imagine that any system that allows you to put a name
+> on some region of data can be used to store these chunks, and their
+> metaindices, and the log itself. The only requirement is that the
+> chunk store be durable, and ideally replicated and relatively fast to
+> read from.
+
+The most intriguing part of this for me is trying to mimic Datomic's
+implementation of those merged chunks, specifically how Rich Hickey
+describes it as an equivalent of persistent data structures used for
+storage, with sharing of previous versions, etc. I believe he calls it
+"durable persistent" datastructure, like making things on disk to be
+a bit similar on how they are on memory.
+
+In fact, that's exactly where I'm focused at right now: deepening my
+grasp on persistent data structures to enable me to navigate this
+subject with more fluidity, and try to implement such durable persistent
+data structures.
+
+The merging of the AllegroGraph that you describe sounds like the flat
+files merge of indexes that RH describes, and contrasts with his
+proposal. I'll try to find this quote, and re-read the paper he mentions
+on the talk (IIRC it's the BigTable one).
+
+I was considering having all indexes be derived from the raw data
+transported between instances, but it might get computationally
+expensive, and making those indexes replicable sounds like a good idea.
+
+> If on Computer A I visit https://aclu.org, and on Computer B I visit
+> https://aclu.org, my two computers will each give the same history
+> item a different internal identifier =E2=80=94 1234 and 4567, say.
+>
+> When I sync them, I need the data store to know that the visits I made
+> to each page _were visits to the same thing_.
+
+Isn't that a domain-specifc problem of identity?
+
+I would solve this by allowing those two entries to cooexist without any
+conflict, and do aggregation queries to answers those questions,
+something like (pseudo-SQL):
+
+SELECT DISTINCT(url) FROM urls;
+
+...to show the all the visited URLs, and:
+
+SELECT SUM(1) from URLS GROUP BY url
+
+...to show the visit count for each URL.
+
+WDYT?
+
+> It definitely helps; this is how teams generally try to work with
+> DynamoDB, for example, though there the schema is implicit rather than
+> explict.
+>
+> However, you inevitably have data migrations to worry about=E2=80=A6 not =
+to
+> mention that syncing makes this a distributed system, and so you
+> cannot easily coordinate schema changes in more than one place, and
+> they will race with new data!
+>
+> There's also the argument that coordinating change in consumers and
+> engineers is sometimes harder than evolving a vocabulary!
+>
+> It's a complicated topic.
+
+On migrations:
+
+I used to share your view on needing a migration scape hatch, but I
+don't anymore. On the company I work those kind of database migrations
+never really occur. People either create a new attribute, copy data from
+a database to another, process it using ETL, compute things on the fly
+with functions, and many other strategies. We've already had to comply
+with excisions in production, but never on schema, only on data itself.
+
+So I don't think that's an actual issue, and I would insist that the
+"Grow your schema" approach actually does work at scale, on the long
+run, for multiple scenarios.=20
+
+> I have been relatively happy with SQLCipher, and I think the idea of
+> block-level encryption is a good place to put the abstraction. The
+> thing you lose with any of these mechanisms is, by definition, the
+> ability for a central entity to help you with conflict resolution. The
+> best you can get is ordering of opaque chunks and collision detection.
+
+I believe that's not what I meant by comparing it to git-remote-gcrypt,
+but maybe I misunderstood your point.
+
+Here's a code snippet to try to highlight what I was referring to:
+
+--8<---------------cut here---------------start------------->8---
+$ pushd $(mktemp -d)
+/tmp/tmp.iapFd5iBCr ~/
+$ mkdir repo-1
+$ cd repo-1/
+$ git init
+$ echo 'a' > file
+$ git add file
+$ git commit -m commit-a
+[master (commit racine) c4112e4] commit-a
+ 1 file changed, 1 insertion(+)
+ create mode 100644 file
+$ git init --bare ../remote/
+D=C3=A9p=C3=B4t Git vide initialis=C3=A9 dans /tmp/tmp.iapFd5iBCr/remote/
+$ git remote add origin gcrypt::../remote
+$ git push origin
+gcrypt: Repository not found: ../remote
+gcrypt: Setting up new repository
+gcrypt: Remote ID is :id:KjDStP/7m18wJagDO0/o
+=C3=89num=C3=A9ration des objets: 3, fait.
+D=C3=A9compte des objets: 100% (3/3), fait.
+Total 3 (delta 0), r=C3=A9utilis=C3=A9s 0 (delta 0), r=C3=A9utilis=C3=A9s d=
+u pack 0
+gcrypt: Encrypting to: --throw-keyids --default-recipient-self
+gcrypt: Requesting manifest signature
+To gcrypt::../remote
+ * [new branch] master -> master
+$ cd ..
+$ git clone gcrypt::remote repo-2
+Clonage dans 'repo-2'...
+gcrypt: Decrypting manifest
+gpg: selecting card failed: No such device
+gpg: Signature faite le mer. 02 sept. 2020 03:43:12 -03
+gpg: avec la clef RSA 5BDAE9B8B2F6C6BCBB0D6CE581F90EC3CD3560=
+60
+gpg: Bonne signature de =C2=AB=C2=A0EuAndreh <eu@euandre.org>=C2=A0=C2=BB [=
+ultime]
+gcrypt: Remote ID is :id:KjDStP/7m18wJagDO0/o
+R=C3=A9ception d'objets: 100% (3/3), fait.
+$ cd repo-2/
+$ echo 'b' > file
+$ git add file
+$ git commit -m commit-b
+[master a5f5f0b] commit-b
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+$ git remote set-url origin gcrypt::../remote
+$ git push origin
+gcrypt: Decrypting manifest
+gpg: selecting card failed: No such device
+gpg: Signature faite le mer. 02 sept. 2020 03:43:12 -03
+gpg: avec la clef RSA 5BDAE9B8B2F6C6BCBB0D6CE581F90EC3CD3560=
+60
+gpg: Bonne signature de =C2=AB=C2=A0EuAndreh <eu@euandre.org>=C2=A0=C2=BB [=
+ultime]
+=C3=89num=C3=A9ration des objets: 3, fait.
+D=C3=A9compte des objets: 100% (3/3), fait.
+Total 3 (delta 0), r=C3=A9utilis=C3=A9s 0 (delta 0), r=C3=A9utilis=C3=A9s d=
+u pack 0
+gcrypt: Encrypting to: --throw-keyids --default-recipient-self
+gcrypt: Requesting manifest signature
+To gcrypt::../remote
+ c4112e4..a5f5f0b master -> master
+$ cd ../repo-1/
+$ echo 'c' > file
+$ git add file
+$ git commit -m commit-c
+[master 3d71137] commit-c
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+--8<---------------cut here---------------end--------------->8---
+
+At this point, repo-1 has changes that conflict with repo-2, and remote
+doesn't know about it, but we can resolve it locally:
+
+--8<---------------cut here---------------start------------->8---
+$ git pull origin master
+gcrypt: Decrypting manifest
+gpg: selecting card failed: No such device
+gpg: Signature faite le mer. 02 sept. 2020 03:44:34 -03
+gpg: avec la clef RSA 5BDAE9B8B2F6C6BCBB0D6CE581F90EC3CD3560=
+60
+gpg: Bonne signature de =C2=AB=C2=A0EuAndreh <eu@euandre.org>=C2=A0=C2=BB [=
+ultime]
+Depuis gcrypt::../remote
+ * branch master -> FETCH_HEAD
+Fusion automatique de file
+CONFLIT (contenu) : Conflit de fusion dans file
+La fusion automatique a =C3=A9chou=C3=A9 ; r=C3=A9glez les conflits et vali=
+dez le r=C3=A9sultat.
+$ cat file
+<<<<<<< HEAD
+c
+=3D=3D=3D=3D=3D=3D=3D
+b
+>>>>>>> a5f5f0b6c780a7ab3f7e28f373b06fa9a10c443f
+--8<---------------cut here---------------end--------------->8---
+
+This is just the general behaviour that I'm aiming towards, but that is
+really just a long shot, I still need to reasearch more on it.
+
+At a glance I can't really tell if that is what you mean when citing
+SQLCipher.=20
+
+>> If sound and robust, what's the downside of detecting conflicts on
+>> individual attributes?
+>
+> It relies on extensive reification to achieve the property that we
+> want: that each semantically conflicting write results in a
+> conflicting attribute change.=20
+>
+> As a contrived example example: notes have both a title and a body.=20
+>
+> A user has two computers.
+>
+> On Computer A, the user decides to change their first note to
+> something else, updating both the title and body.=20
+>
+> On Computer B, they do the same thing to a different title and body.=20
+>
+> With attribute-based conflict resolution it is possible to end up with
+> a note that has a combination of A's title and B's body =E2=80=94 a
+> combination that never existed on either computer.=20
+>
+> This gets even worse when you treat deletion specially, when new
+> assertions don't conflict but do alter domain semantics, or when some
+> of the conflicts affect the way the rest of the data is interpreted
+> (e.g., when you have an identity property or a lookup ref).=20
+>=20
+> The only data modeling solution to this case is to introduce an
+> immutable 'note node', and have the _change_ be to have Note One point
+> to a different note node =E2=80=94 one attribute change!=20
+>=20
+> In a JSON-ish system like Pouch you'd swap an entire JS object within
+> the same attribute, rather than having two different attributes.=20
+>
+> Developers =E2=80=94 at least, non-Clojure developers =E2=80=94 rarely th=
+ink in these
+> terms, and now everywhere else you need to make really careful
+> decisions about whether you point to the 'note' or the 'node'. Getting
+> it wrong means dangling pointers.=20
+>
+> Using per-attribute automatic merging without having an expert
+> ontologist model your domain is a recipe for weird edge-case conflict
+> resolution bugs that result in data soup or data loss.=20
+
+TBH I'm not aiming for automatic conflict resolution.
+
+Why not use the granularity of attributes to detect conflict, but not to
+resolve them?
+
+Let me add to the scenario you presented: notes have a title, a body and
+an author.
+
+A user has two computers.
+
+On Computer A, the user decides to change their first note to
+something else, updating both the title and body.=20
+
+On Computer B, they do the same thing to a different title and body.=20
+
+On both cases, the author stays the same.
+
+When the DB instance on Computer A gets data from Computer B, the query:
+
+SELECT * FROM notes WHERE id =3D 1234;
+
+would return both, and some metadata saying that it has a conflict. Now
+the programmer needs to decide how to deal with these: use timestamps,
+pick a winning device, sort by revision ID and pick the first, show them
+on a UI to the user to resolve, etc.
+
+Either way, the DB knows that the note has the title and body attributes
+in conflict, but the author is fine. The aggregate entity itself isn't
+in conflict, but some of it's attributes.
+
+The situation that you describe where one would end up with a note that
+is a combination of A's title and B's body would only happen if the DB
+tried to solve things "automagically".
+
+However, I agree on the costly constant reification being required, and
+for long disconnected histories to be merged together this might be
+expensive.=20
+
+>> If an Datomic-like entity with 10 attributes has 4 of them change by
+>> different database instances, but only 1 change is on the same
+>> attribute, would this type of detection offer more granularity instead?
+>
+> My thinking is mostly: you need some kind of schema mechanism to be
+> able to recognize which properties can be reconciled safely, and you
+> also cannot examine each tuple independently in many cases. It's
+> complicated. Something like Dropbox's datastore conflict resolution
+> (if I remember right) is fine much of the time, but wrong some of the
+> time.=20
+
+Couldn't you leverage the schema for that? If the said conflicting
+attribute has:
+
+- cardinality one and both DBs changed it, we have a conflict;
+- cardinality many and both changed the same attribute, we have a
+ conflict;
+- cardinality may and Computer A changed an entry and Computer B changed
+ another one, we don't have a conflict.
+
+and so on.
+
+I wouldn't build it with an exaustive search, but just leverage the
+schema to make those decisions.
+
+I don't know Dropbox's datastore, is this[0] the one you're refferring
+to?=20
+
+[0]: https://dropbox.tech/developers/how-the-datastore-api-handles-conflict=
+s-part-1-basics-of-offline-conflict-handling
+
+> Even source code merging isn't this simple, no? A conceptual operation
+> like "add this line underneath this other line" is the thing you're
+> trying to represent, but that line has _context_ =E2=80=94 it might have
+> moved, the file might have been renamed, or someone else already put a
+> line there.=20
+>
+> You've probably seen this with b0rked merges in software version
+> control, where the result of a merge won't compile because of
+> duplicate identifiers thanks to hunks moving around=E2=80=A6=20
+>
+> But trying to apply text-based merging to semantic data only works if
+> there are no constraints in your data, or if you have fast-forward
+> merges =E2=80=94 merging can violate uniqueness properties in all kinds of
+> ways.=20
+
+Yep, that was an oversimplification.
+
+<hr />
+
+Again, thanks for engaging, this discussion feels like a golden nugget.
+
+From me@jonas-schuermann.name Wed Sep 2 12:48:41 2020
+Received: from mout-p-201.mailbox.org (mout-p-201.mailbox.org [80.241.56.171])
+ by mail-b.sr.ht (Postfix) with ESMTPS id E843EFF14B
+ for <~euandreh/public-inbox@lists.sr.ht>; Wed, 2 Sep 2020 12:48:40 +0000 (UTC)
+Received: from smtp1.mailbox.org (smtp1.mailbox.org [80.241.60.240])
+ (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits))
+ (No client certificate requested)
+ by mout-p-201.mailbox.org (Postfix) with ESMTPS id 4BhNzC5xW2zQlX2;
+ Wed, 2 Sep 2020 14:48:39 +0200 (CEST)
+X-Virus-Scanned: amavisd-new at heinlein-support.de
+Received: from smtp1.mailbox.org ([80.241.60.240])
+ by spamfilter03.heinlein-hosting.de (spamfilter03.heinlein-hosting.de [80.241.56.117]) (amavisd-new, port 10030)
+ with ESMTP id 3fpZQfKdp0ux; Wed, 2 Sep 2020 14:48:36 +0200 (CEST)
+Subject: Re: The database I wish I had
+To: EuAndreh <eu@euandre.org>, ~euandreh/public-inbox@lists.sr.ht
+References: <e0b3b042-5eda-bcde-59a5-a5b61fecd2e5@jonas-schuermann.name>
+ <87y2ltyeqg.fsf@euandre.org>
+From: =?UTF-8?Q?Jonas_Sch=c3=bcrmann?= <me@jonas-schuermann.name>
+Message-ID: <acd49073-c46e-2bae-da55-de1d8cc42533@jonas-schuermann.name>
+Date: Wed, 2 Sep 2020 14:48:35 +0200
+MIME-Version: 1.0
+In-Reply-To: <87y2ltyeqg.fsf@euandre.org>
+Content-Type: text/plain; charset=utf-8; format=flowed
+Content-Language: en-US
+Content-Transfer-Encoding: 7bit
+X-MBO-SPAM-Probability:
+X-Rspamd-Score: -0.85 / 15.00 / 15.00
+X-Rspamd-Queue-Id: 6F42037F
+X-Rspamd-UID: dfaa19
+
+> Hmmm, I haven't heard this term before. Are these [0] the complete
+> categories that you mean?
+>
+> [0]: https://en.wikipedia.org/wiki/Complete_category
+
+Yes, exactly. The idea is pretty abstract and while I know a little bit
+about category theory I'm by no means an expert on the subject. This
+blog post [1] is a very down-to-earth, practical explanation how the
+concept of (co)completed categories can be applied to conflict
+representation in version control systems.
+
+[1] https://jneem.github.io/merging/
+
+From rnewman@twinql.com Wed Sep 2 16:14:41 2020
+Received: from a58-46.smtp-out.us-west-2.amazonses.com (a58-46.smtp-out.us-west-2.amazonses.com [54.240.58.46])
+ by mail-b.sr.ht (Postfix) with ESMTPS id AF4DCFF12F
+ for <~euandreh/public-inbox@lists.sr.ht>; Wed, 2 Sep 2020 16:14:40 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (1024-bit key) header.d=twinql.com header.i=@twinql.com header.b=grgVRwHH;
+ dkim=pass (1024-bit key) header.d=amazonses.com header.i=@amazonses.com header.b=SGue23U5
+DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/simple;
+ s=qvs2k2sd2ivtrflyu2nswmravuqizjml; d=twinql.com; t=1599063278;
+ h=Subject:From:To:Cc:Date:Mime-Version:Content-Type:Content-Transfer-Encoding:In-Reply-To:References:Message-Id;
+ bh=UZ3b7SzbsIcKi0Si4ZISwM274bq+ODQFNXqhUN8KRVo=;
+ b=grgVRwHHOiZAo7asJgQxtXN5anM8LDtht/oVf7XVjh3IJNFPui4FSOPJK+Eh4Xdf
+ MniQ9lndbRWU+3IR3fr3G6NKqYNdfpJb74/j/LvjnnrwZqxQQJFt99pQmwAKADGJAyG
+ ZIwzp6kjG9pz3Uk1Bh+kRa2mHBIMmgfenRoqjiAI=
+DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/simple;
+ s=hsbnp7p3ensaochzwyq5wwmceodymuwv; d=amazonses.com; t=1599063278;
+ h=Subject:From:To:Cc:Date:Mime-Version:Content-Type:Content-Transfer-Encoding:In-Reply-To:References:Message-Id:Feedback-ID;
+ bh=UZ3b7SzbsIcKi0Si4ZISwM274bq+ODQFNXqhUN8KRVo=;
+ b=SGue23U5gqkhyLMKyKwkBGrqzxh9Ds7ua97mgDIB1BcWDmf58EDy2KhBrlkOtn0y
+ IP8gxNfgYZDYdJFvPt8GmlkDg8nQNYW29iWNcf8ppMHCCEchNB85KrXAOtyBXYiR2oa
+ nKriOwTP4/GeXifOl7+T8eIEkFuZI6DVL5Gas5SA=
+Subject: Re: The database I wish I had
+From: =?UTF-8?Q?Richard_Newman?= <rnewman@twinql.com>
+To: =?UTF-8?Q?EuAndreh?= <eu@euandre.org>
+Cc: =?UTF-8?Q?=7Eeuandreh/public-inbox=40lists=2Esr=2Eht?=
+ <~euandreh/public-inbox@lists.sr.ht>
+Date: Wed, 2 Sep 2020 16:14:38 +0000
+Mime-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+In-Reply-To: <878sdsy6sc.fsf@euandre.org>
+References: <7454DA45-C5A4-4A54-8D02-1F3BD37726E9@twinql.com>
+ <010101744a592b75-1dce9281-f0b8-4226-9d50-fd2c7901fa72-000000@us-west-2.amazonses.com>
+ <87sgc1xu7k.fsf@euandre.org>
+ <9652AAE0-10DF-4D13-ABE8-B30667BE27DD@twinql.com>
+ <010101744b260d62-1bb508ab-647f-4877-bdf9-f988667d56e1-000000@us-west-2.amazonses.com>
+ <878sdsy6sc.fsf@euandre.org>
+ <7027FD8B-1B30-435F-A840-7DC61A722E0B@twinql.com>
+X-Priority: 3 (Normal)
+X-Mailer: Amazon WorkMail
+Thread-Index: AQHWgHcPJ4udfXYrRaOwhoO7EcsDJQAErSAlAAfQ7+gAIgKd3wAzRhy+
+Thread-Topic: The database I wish I had
+X-Original-Mailer: Apple Mail (2.3445.104.14)
+X-Wm-Sent-Timestamp: 1599063277
+Message-ID: <010101744f99448f-fa8dd88e-0daf-41f8-b3ca-6ea551c300e4-000000@us-west-2.amazonses.com>
+X-SES-Outgoing: 2020.09.02-54.240.58.46
+Feedback-ID: 1.us-west-2.An468LAV0jCjQDrDLvlZjeAthld7qrhZr+vow8irkvU=:AmazonSES
+
+> I've used Common Lisp a lot before, and I heard a lot about=0D=0A> Alle=
+groGraph, but I've never worked with it. Would you recommed it as a=0D=0A=
+> place to take inspirations from=3F=0D=0A=0D=0AHelping to build it was a=
+ very formative experience for me. twinql was the name of my first SPARQL=
+ query engine, which became the genesis of AllegroGraph's, and it stuck a=
+s a domain name!=0D=0A=0D=0ASome of AllegroGraph's performance is the res=
+ult of decades of experience (Steve Haflich comes to mind) and having som=
+e of the same exceptional developers work on it as worked on the compiler=
+, so its internals are excellent inspiration. The UPI (how EAV are repres=
+ented) is a good idea executed well. AllegroGraph is also a good example =
+of how important tooling is for exploring this kind of semi-structured da=
+ta.=0D=0A=0D=0A=0D=0A> These look just like the EAVT and AVET indexes in =
+Datomic.=0D=0A=0D=0AYes, exactly! It's a very natural way to model a fixe=
+d-width tuple store.=0D=0A=0D=0A=0D=0A> I was considering having all inde=
+xes be derived from the raw data=0D=0A> transported between instances, bu=
+t it might get computationally=0D=0A> expensive, and making those indexes=
+ replicable sounds like a good idea.=0D=0A=0D=0AIt depends where you're r=
+eplicating. In Datomic's case each of the query nodes gets a replicated c=
+opy of the index chunks it needs (and you can thus control cache warmth b=
+y routing queries to different nodes), because those nodes are meant to b=
+e fungible, and reads from S3 (or wherever) are cheap, so there's no sens=
+e doing the work twice.=0D=0A=0D=0AIf you were to stand up a hot spare, y=
+ou would probably just stream the log and have the far end create its own=
+ indices. That's particularly true if the other store needs different ind=
+ices, or is materializing views.=0D=0A=0D=0ASidenote: it surprises me tha=
+t Datomic Cloud stores its log in DynamoDB. It's hard to imagine a good p=
+rimary key scheme that wouldn't at some point exhaust write capacity, and=
+ DDB makes different availability and durability tradeoffs than S3. I'm c=
+urious how that decision was made.=0D=0A=0D=0AObligatory disclaimer: I wo=
+rk at AWS, but the above is not a statement on behalf of AWS!=0D=0A=0D=0A=
+=0D=0A>> When I sync them, I need the data store to know that the visits =
+I made=0D=0A>> to each page _were visits to the same thing_.=0D=0A>=20=0D=
+=0A> Isn't that a domain-specifc problem of identity=3F=0D=0A=0D=0AYes, a=
+bsolutely. I take the position that the goal of a database is to help me =
+efficiently, accurately, and ergonomically represent my domain. If it doe=
+sn't, every developer has to solve that problem at the application level,=
+ and that limits interoperability, correctness, performance, and expressi=
+veness.=0D=0A=0D=0AThe ability for this data layer to be able to help you=
+ with problems like syncing also relies on its ability to leverage some i=
+nformation about your domain =E2=80=94 compare to when you check in an im=
+age file or a tarball to Git, and it can't help you detect or resolve con=
+flicts at anything more granular than the containing file, even if that f=
+ile has its own internal structure.=0D=0A=0D=0A=0D=0A> I would solve this=
+ by allowing those two entries to cooexist without any=0D=0A> conflict, a=
+nd do aggregation queries to answers those questions,=0D=0A> something li=
+ke (pseudo-SQL):=0D=0A>=20=0D=0A> SELECT DISTINCT(url) FROM urls;=0D=0A>=20=
+=0D=0A> ...to show the all the visited URLs, and:=0D=0A>=20=0D=0A> SELECT=
+ SUM(1) from URLS GROUP BY url=0D=0A>=20=0D=0A> ...to show the visit coun=
+t for each URL.=0D=0A>=20=0D=0A> WDYT=3F=0D=0A=0D=0AThe advantage is inde=
+ed that you have removed the ability for data to conflict.=0D=0A=0D=0AThe=
+ disadvantage is that you have pushed a domain identity and merging into =
+application code, where:=0D=0A=0D=0A* Every query needs to implement this=
+ kind of unification in multiple places, which is more expensive and horr=
+endously difficult to get right, not to mention very hard for developers =
+to read.=0D=0A=0D=0A* Application developers can get it wrong (e.g., dele=
+ting from History needs to retract every entry with a given URL), and you=
+ make domain operations more complex as a result. Sometimes this adds val=
+ue =E2=80=94 e.g., this kind of accumulative storage gives you magic prop=
+erties like lifetime undo! =E2=80=94 but I suspect that putting that in t=
+he log is better.=0D=0A=0D=0A* You lose some conveniences like lookup ref=
+s, because you've removed the concept of a 1:1 lookup altogether.=0D=0A=0D=
+=0A* You lose the trivial ability to detect and model _changes_, which I =
+find is natural for developers: this approach essentially makes the log p=
+art of the datom state. You can still do retract/assert locally, for a pa=
+rticular entity, but if you want to e.g., get the latest title for a page=
+, you need to reduce over the log with some ordering.=0D=0A=0D=0A=0D=0AI =
+agree with your position in principle: the idea of figuring out the curre=
+nt state dynamically by rolling up history from different machines is ver=
+y event-sourcey and is theoretically pleasant. Every device would simply =
+append new non-conflicting _observations_ to the log. I do advocate for t=
+his in some circumstances.=0D=0A=0D=0AHowever, in practice we usually hav=
+e some stable idea of what we want current state to be (particularly of s=
+chema itself!), and some domain concepts around identity and cardinality =
+that we want to enforce and use to build concepts like change, conflict, =
+and merging, and we need that maintained state to be fast and obvious bec=
+ause we use it when making new assertions.=0D=0A=0D=0AIn essence, the pro=
+blem I was trying to solve with syncing in Mentat was: how do I allow dev=
+elopers to talk about their schema in such a way that they could get sync=
+ing for nearly free=3F That a sync engine could, in most cases, automatic=
+ally figure out which entities were the same, decide on a stable ordering=
+ of operations, and rewrite history to produced the expected merged state=
+, such that in the above example all the visits stitch together, we know =
+the latest title, and the log has all the historical titles=3F=0D=0A=0D=0A=
+Using observation-structured storage to remove conflict is definitely a v=
+aluable tool in the toolbox for doing that: the more you can structure yo=
+ur data as new observations about stably identified things, the easier ti=
+me you have with syncing. I believe, but have not proved, that a system w=
+ith only identity properties and cardinality-many properties, perhaps wit=
+h some additional restrictions on retraction and uniqueness, will never h=
+ave conflicts when merging.=0D=0A=0D=0ABut I also think that the best dev=
+eloper ergonomics are actually slightly _more_ expressive than even Datom=
+ic or Mentat =E2=80=94 developers told me that they wanted cardinality-N,=
+ and to be able to express types or domain restrictions on properties, an=
+d nesting, and so on =E2=80=94 and delivering those ergonomics means taki=
+ng care of turning the log into a simple, concrete current state for them=
+ to query.=0D=0A=0D=0APhrased differently: in the above, your developers =
+want some way to write=0D=0A=0D=0A SELECT url, title FROM pages=0D=0A=0D=
+=0Aand=0D=0A=0D=0A SET title FOR page WITH url 'foo.com' TO 'whatever'=0D=
+=0A=0D=0AWhether you do that by figuring out at sync time the current tit=
+le for pages with a given URL, or whether you give them some affordance t=
+o dynamically roll that up from the log, gets to be the same thing if you=
+ think of Mentat's 'datoms' table and schema as a way for developers to d=
+escribe how Mentat should roll up current state from the log!=0D=0A=0D=0A=
+=0D=0A> I used to share your view on needing a migration scape hatch, but=
+ I=0D=0A> don't anymore. On the company I work those kind of database mig=
+rations=0D=0A> never really occur. People either create a new attribute, =
+copy data from=0D=0A> a database to another, process it using ETL, comput=
+e things on the fly=0D=0A> with functions, and many other strategies. We'=
+ve already had to comply=0D=0A> with excisions in production, but never o=
+n schema, only on data itself.=0D=0A=0D=0AI actually view what you descri=
+be as migration: the point at which you're defining a new attribute and s=
+toring things twice, or doing a batch job to move things over, is exactly=
+ a data migration, if perhaps smeared over space and time!=0D=0A=0D=0AThe=
+se still have all the problems that traditional schema migrations have: d=
+ealing with data that doesn't conform, and dealing with clients that don'=
+t know about the new format.=0D=0A=0D=0A=0D=0A> So I don't think that's a=
+n actual issue, and I would insist that the=0D=0A> "Grow your schema" app=
+roach actually does work at scale, on the long=0D=0A> run, for multiple s=
+cenarios.=20=0D=0A=0D=0AI agree. I also think that one of the ways it doe=
+s so is by pushing the work of tracking versions and vocabulary into appl=
+ication code, either formally or informally (e.g., checking for the exist=
+ence of a property, coordinating the upgrade of client code, etc.) =E2=80=
+=94 the clients need to know how to work with the schema as it grows!=0D=0A=
+=0D=0AThis was the motivation for me spending time on vocabulary manageme=
+nt, which we did end up building:=0D=0A=0D=0Ahttps://github.com/mozilla/m=
+entat/wiki/Proposal:-application-schema-coordination-and-versioning=0D=0A=
+=0D=0A=0D=0A> At a glance I can't really tell if that is what you mean wh=
+en citing=0D=0A> SQLCipher.=20=0D=0A=0D=0AAh, we were talking about two d=
+ifferent kinds of encryption!=0D=0A=0D=0AWe were using SQLCipher for loca=
+l block-level encryption.=0D=0A=0D=0AAt Mozilla we would additionally use=
+ Firefox Account encryption to end-to-end encrypt data as it passed throu=
+gh Sync. The two are complementary. Sync acts exactly as you demonstrate =
+with git-remote-gcrypt: two local devices can access cleartext and are re=
+sponsible for resolving conflicts, and the service itself sees only ciphe=
+rtext and does not involve itself in how conflicts are resolved.=0D=0A=0D=
+=0AMy point in bringing this up is that many systems have a central entit=
+y that can help in resolving conflicts; if the central entity can only ev=
+er see ciphertext, it's limited to whatever ordering or collision propert=
+ies you enable via the envelope. For that Git system I imagine it tracks =
+refs, and the Git object store is opaque.=0D=0A=0D=0A=0D=0A> TBH I'm not =
+aiming for automatic conflict resolution.=0D=0A>=20=0D=0A> Why not use th=
+e granularity of attributes to detect conflict, but not to=0D=0A> resolve=
+ them=3F=0D=0A=0D=0AIn short:=0D=0A=0D=0A* End user software is no longer=
+ built (as iSync was) to present data conflicts to end users for resoluti=
+on. The goal is conflict resolution.=0D=0A* You cannot leave a store in a=
+n inconsistent state until conflicts have been addressed, because you sti=
+ll need to handle local writes. You cannot disconnect a device until conf=
+licts have been addressed; users expect continuity of service. Whenever y=
+ou are able, you have to do _something_.=0D=0A* _Someone_ has to write th=
+e code to resolve conflicts. Eventually you're going to have some dev com=
+e up with the bright idea of annotating "data objects" with some kind of =
+conflict resolution attributes, and then have an automated "conflict reso=
+lver"=E2=80=A6 at which point you've Greenspun yourself a sync engine and=
+ schema language. This works much better when it's part of the storage an=
+d sync later, and can participate in transaction boundaries and annotate =
+writes.=0D=0A=0D=0AThis is the doc I wrote to outline the motivations for=
+ Mentat at Mozilla:=0D=0A=0D=0Ahttps://mozilla.github.io/firefox-browser-=
+architecture/text/0008-sync-and-storage-review-packet.html=0D=0A=0D=0AThe=
+ Lay Summary in particular is worth a read.=0D=0A=0D=0AWhen we built Fire=
+fox for iOS we managed to put storage and syncing inside the same compone=
+nt, so finally we were in a position to track changes during writes, and =
+could get it right. Mentat was the next logical step: how do we let other=
+ teams use a storage system that will still do a good job of syncing thei=
+r data without them having to get elbow deep in how a sync engine works, =
+and without them having to put all the syncing gunk =E2=80=94 timestamps/=
+versions, UUIDs, etc. =E2=80=94 in their data model=3F=0D=0A=0D=0AWhat do=
+es Mentat need to know, schema-wise, to be able to do that kind of declar=
+ative conflict resolution=3F We didn't finish answering that question, bu=
+t I think the intended outcome is still valid.=0D=0A=0D=0A=0D=0A> Either =
+way, the DB knows that the note has the title and body attributes=0D=0A> =
+in conflict, but the author is fine. The aggregate entity itself isn't=0D=
+=0A> in conflict, but some of it's attributes.=0D=0A=0D=0AThis is what Co=
+uch/Pouch do, I think. Well, it actually resolves the conflict and leaves=
+ a conflict marker=E2=80=A6 no idea whether anyone actually writes code t=
+o check that!=0D=0A=0D=0A=0D=0A> Couldn't you leverage the schema for tha=
+t=3F If the said conflicting=0D=0A> attribute has:=0D=0A>=20=0D=0A> - car=
+dinality one and both DBs changed it, we have a conflict;=0D=0A> - cardin=
+ality many and both changed the same attribute, we have a=0D=0A> conflic=
+t;=0D=0A> - cardinality may and Computer A changed an entry and Computer =
+B changed=0D=0A> another one, we don't have a conflict.=0D=0A>=20=0D=0A>=
+ and so on.=0D=0A=0D=0AYes, that's exactly the approach we were taking. T=
+he important difference is that you also need to zip together identities =
+during this process =E2=80=94 for example, two assertions of a cardinalit=
+y-one attribute are not a conflict if the two entities at the other end t=
+urn out to be the same. Couch etc. don't have that concept.=0D=0A=0D=0A(I=
+n an open-world system like OWL-DL, this cardinality conflict actually be=
+comes implication of equality!)=0D=0A=0D=0A> I don't know Dropbox's datas=
+tore, is this[0] the one you're refferring=0D=0A> to=3F=20=0D=0A=0D=0AYep=
+! It's a really great bit of thinking.=0D=0A=0D=0A
+
+From eu@euandre.org Thu Sep 3 01:00:21 2020
+Received: from box.euandre.org (box.euandre.org [46.101.160.115])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 71E69FF0FA
+ for <~euandreh/public-inbox@lists.sr.ht>; Thu, 3 Sep 2020 01:00:20 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (2048-bit key) header.d=euandre.org header.i=@euandre.org header.b=rMNL9qSC
+Received: from authenticated-user (box.euandre.org [46.101.160.115])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
+ (No client certificate requested)
+ by box.euandre.org (Postfix) with ESMTPSA id E3DD6102122;
+ Wed, 2 Sep 2020 22:00:16 -0300 (-03)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=euandre.org; s=mail;
+ t=1599094818; bh=VQ0pHLJALciNNh1+kpFy1DVLAgL5kn8a36k3GAV6u7E=;
+ h=From:To:Cc:Subject:In-Reply-To:References:Date:From;
+ b=rMNL9qSC0p+9DH1RAD5wEPqn9/v63VAiwAiMKQuTvccTrH5EnO2qlB7mKU594brWM
+ P+5qCkBJymi/N2hK+BvoROdWT24A6NPyG0rORpg00rmuJsrpyn6HkpbHzOcx3CwYdH
+ WUVzXoZQndMFNa4nN4P/KE4Mh/jA/4TvVO/4QvfrJqpnpfKdPJeIgFEdrCEQL+3ZIP
+ b/DjqLRRSkV3fljHnB2MhsCMe/od5BD+wDEza/x+HvOwTxFHvalbWT1QVj3saKxiTG
+ VbD8xe43tls9agJsSh/z8MErjmNI4Ngd0ygfIu8QbbLNswkpQe3RMru5lYi2o49N55
+ 1bnUqfFRkHs/w==
+From: EuAndreh <eu@euandre.org>
+To: Jonas =?utf-8?Q?Sch=C3=BCrmann?= <me@jonas-schuermann.name>,
+ ~euandreh/public-inbox@lists.sr.ht
+Cc:
+Subject: Re: The database I wish I had
+In-Reply-To: <acd49073-c46e-2bae-da55-de1d8cc42533@jonas-schuermann.name>
+References: <e0b3b042-5eda-bcde-59a5-a5b61fecd2e5@jonas-schuermann.name>
+ <87y2ltyeqg.fsf@euandre.org>
+ <acd49073-c46e-2bae-da55-de1d8cc42533@jonas-schuermann.name>
+Date: Wed, 02 Sep 2020 21:34:51 -0300
+Message-ID: <87d033g0k4.fsf@euandre.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: quoted-printable
+
+Jonas Sch=C3=BCrmann <me@jonas-schuermann.name> writes:
+
+> Yes, exactly. The idea is pretty abstract and while I know a little bit
+> about category theory I'm by no means an expert on the subject. This
+> blog post [1] is a very down-to-earth, practical explanation how the
+> concept of (co)completed categories can be applied to conflict
+> representation in version control systems.
+>
+> [1] https://jneem.github.io/merging/
+
+This sounds interesting, thanks for the link.
+
+From eu@euandre.org Thu Sep 3 01:07:15 2020
+Received: from box.euandre.org (box.euandre.org [46.101.160.115])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 20958FF0FA
+ for <~euandreh/public-inbox@lists.sr.ht>; Thu, 3 Sep 2020 01:07:15 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (2048-bit key) header.d=euandre.org header.i=@euandre.org header.b=Y4O+t3yg
+Received: from authenticated-user (box.euandre.org [46.101.160.115])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
+ (No client certificate requested)
+ by box.euandre.org (Postfix) with ESMTPSA id B209DFC81E;
+ Wed, 2 Sep 2020 22:07:12 -0300 (-03)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=euandre.org; s=mail;
+ t=1599095234; bh=Y2H9I+SgM0rzOycDHDHlfDDvDGVB79YoDxvEzTudos4=;
+ h=From:To:Cc:Subject:In-Reply-To:References:Date:From;
+ b=Y4O+t3yg4rBzXtK6CL570EiO+7TXdlHZ1OAjSDRUjr71MbaIt+HonABTUommz90+U
+ +FkpruCMrLNQIyuveS07REukjQVp/zKHrH41FLFDaRUoBIg1OuxvIp4I+Ug1M+HQAC
+ tS1tPA0oqbqmrI7l2ByFEP58VyYhRNpYAbrpSMJUI4gt/Busfq5TFLQfnuTZSaGbNy
+ 9AsJYnD04/B+VRQHcrST55I8sVyaHkxQM0xck0J7iEnJesbTxGU6adg89UL7fTd4Jb
+ LP4PognYY5bvYrsRP1eTQVkm8Iefevp0X+fgNdQsvBkee8WLAykSq4jkMpPaBEyMl3
+ WIq9JcFaLACew==
+From: EuAndreh <eu@euandre.org>
+To: Jeff Rhyason <jeff@rhyason.org>, ~euandreh/public-inbox@lists.sr.ht
+Cc:
+Subject: Re: building MSTs->CRDTs-> Re: The database I wish I had
+In-Reply-To: <99C92E06-E0BC-4DB0-BF63-20FD44DC4062@rhyason.org>
+References: <99C92E06-E0BC-4DB0-BF63-20FD44DC4062@rhyason.org>
+Date: Wed, 02 Sep 2020 22:06:59 -0300
+Message-ID: <87v9gveki4.fsf@euandre.org>
+MIME-Version: 1.0
+Content-Type: text/plain
+
+Jeff Rhyason <jeff@rhyason.org> writes:
+
+> It's amazing to see this discussion. Let's keep it going!
+>
+> My friend and I started Indelible[1] (now defunct) for a lot of the
+> same reasons you want to build a datastore -- there aren't enough
+> options for a datastore that's immutable, relational,
+> highly-vaailable, and private.. We're still super-passionate about the
+> ideas, love Datomic but want something a bit different, decided not to
+> commercialize it and start more simply with just the data structure.
+> We don't want a huge thing, just a library, to keep coordination down;
+> the complexity doesn't spread out of the client.
+>
+> Where we are at now is we have a great versioned immutable B-Tree-like
+> structure that is the foundation for the verioned and diffable
+> database we want. FWIW The Go implementation of the tree is at
+> https://github.com/jrhy/mast . We have plans to do a CRDT based on
+> it, possibly add an SQL layer, but frankly, having a
+> strongly-consistent, verioned + diffable tree, is pretty good for now.
+> We'd love to collaborate!
+
+This looks interesting! I'll take a deeper look later.
+
+I'll be particularly interested in seeing the implementation of
+persistent B-Trees that are durable.
+
+From eu@euandre.org Thu Sep 3 23:39:44 2020
+Received: from box.euandre.org (box.euandre.org [46.101.160.115])
+ by mail-b.sr.ht (Postfix) with ESMTPS id B8167FF1D6
+ for <~euandreh/public-inbox@lists.sr.ht>; Thu, 3 Sep 2020 23:39:43 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (2048-bit key) header.d=euandre.org header.i=@euandre.org header.b=oQR0LJk6
+Received: from authenticated-user (box.euandre.org [46.101.160.115])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
+ (No client certificate requested)
+ by box.euandre.org (Postfix) with ESMTPSA id 2F2B2FCB45;
+ Thu, 3 Sep 2020 20:39:39 -0300 (-03)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=euandre.org; s=mail;
+ t=1599176381; bh=0OTcSYr5H5tf2Nj44ZROMSLLvq65wMQATwFWuwPcGcA=;
+ h=From:To:Cc:Cc:Subject:In-Reply-To:References:Date:From;
+ b=oQR0LJk66tmm7FUnmKKEUg3GVM6KyB0ybBX7bvke8hAkcTr/UOADsGEa64ZOgq47F
+ 5+6d43VfXXhxXQie9IypcAHdqe5eYTXL0Yh6ymknjM9GIPHEhGRgzbK+jeTe6EK2KH
+ y2bC4HqyQ0Xn9Ijj+m+OEQCACknw1vsIvVH3qQveSft3mRmIKIibpDL5uxMaGbNRHK
+ V0ts3LFy07Knz4QI5fsPRqBwsKNQV9t+F5eL5ITa6OSC3JY+QclEbcYheBW80V42IT
+ 8+hb6HzscpFlxvBtdi1fG7PbnUtWAp/7j6sK8AtVYRCK5OybOAy0VpJhGGbFCZdale
+ s3u7XVTPdzmeQ==
+From: EuAndreh <eu@euandre.org>
+To: Richard Newman <rnewman@twinql.com>
+Cc: "~euandreh/public-inbox@lists.sr.ht" <~euandreh/public-inbox@lists.sr.ht>
+Cc:
+Subject: Re: The database I wish I had
+In-Reply-To: <010101744f99448f-fa8dd88e-0daf-41f8-b3ca-6ea551c300e4-000000@us-west-2.amazonses.com>
+References: <7454DA45-C5A4-4A54-8D02-1F3BD37726E9@twinql.com>
+ <010101744a592b75-1dce9281-f0b8-4226-9d50-fd2c7901fa72-000000@us-west-2.amazonses.com>
+ <87sgc1xu7k.fsf@euandre.org>
+ <9652AAE0-10DF-4D13-ABE8-B30667BE27DD@twinql.com>
+ <010101744b260d62-1bb508ab-647f-4877-bdf9-f988667d56e1-000000@us-west-2.amazonses.com>
+ <878sdsy6sc.fsf@euandre.org>
+ <7027FD8B-1B30-435F-A840-7DC61A722E0B@twinql.com>
+ <010101744f99448f-fa8dd88e-0daf-41f8-b3ca-6ea551c300e4-000000@us-west-2.amazonses.com>
+Date: Thu, 03 Sep 2020 20:39:23 -0300
+Message-ID: <87h7se5t1w.fsf@euandre.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: quoted-printable
+
+I started quoting many parts of your email, and things started to get
+confusing. Each answer that I gave was referring to each other, and I
+almost made a loop.
+
+So I've rewritten everything as a single piece of prose. I added some
+snippets of your text to better contextualize what I was talking about,
+but I tried to address what you said directly. Here it goes.
+
+# On migrations
+
+I still don't think that I've insisted enough on *strict* schema growth.
+
+The wiki page you pointed to reinforces that: it talks a lot about how
+to coordinate attributes that changed their names, change in vocabulary,
+shared ownership of changes in schema and even schema versioning!
+
+All of them are the opposite of what I want, and they're "schema
+breakage", not "schema growth".
+
+What I mean by that is the same thing that Rich Hickey proposes as a
+what growth and breakage means in his talk "Spec-ulation"[0].
+
+All of those forms of schema change are instances of breakage, which
+should have no place here. They're all sources of undesired
+incompatibility. I'm not sure if Datomic allows you to change the type
+of an attribute, and if it does this can't be copied, since we don't
+have a transactor to coordinate such change.
+
+If you want to rename an attribute, don't! Create a new one! Maybe you
+could retract schema and assert back the same schema, but you shouldn't
+overwrite it with an incompatible name, or attribute type. This is a
+trap that you shoudln't put yourself in, and maybe even this could be
+enforced.
+
+This model of an ever-growing vocabulary is how HTML works. At some
+point people realised that using <b> to mark something as bold was not
+such a good idea, since it coupled semantics with presentation. So a new
+tag was created, <strong>, which only represents the semantic part of
+<b>. And <b> still continues to exist, and that's fine.
+
+The same can be said about JavaScript. I've written a fair share of it
+in the past 2 years, and I don't think I ever used the 'var' keyword,
+only 'let' and 'const'. And yet, every JavaScript runtime still
+recognizes it, and even transpiler emit it as their output, and that's
+also fine.
+
+Maybe a linter or the browser tooling would warn you against the usage
+of those old features, but they continue to work. If an equivalent of a
+"vocabulary change" happened, web pages would be brittle by design more
+brittle, and old pages would stop working as time passes. That isn't a
+principle I would like to adopt.
+
+When more than one database instance can add data, the programmer needs
+to think of it as an open world, where new things are accrued, but old
+things don't disapper. Changing the vocabulary mid-air only makes this
+open world brittle so "migrations" can be performed.
+
+I see no reason for having this type of migration you describe, and
+insist on *strict* schema growth, it being a saner, more robust and
+completly fine alternative.
+
+[0]: https://www.youtube.com/watch?v=3DoyLBGkS5ICk
+
+# On conflicts and ergonomics
+
+You mentioned ergonomics thrice on your message, and I believe that your
+insistence of it is a sign of the coupling of two different layers of
+the database, and makes ease of use drive the fundamentals. I also think
+that your statement on conflict resolution being a requirement is not a
+good idea. Let me expand on that.
+
+I'd argue that conflict resolution is inherit complexity [1] of creating
+a decentralized client-side application that can sync data.
+
+That means that the responsability to solve conflicts is not the
+database's, but the application's responsability, and making decisions
+of conflict resolution on the database limits its application, and I
+wouldn't consider it to be general purpose.
+
+When two offline database instances change the same field, there's no
+escaping from the bottom reality, which is: there is a conflict, and it
+needs to be resolved somewhere. There's no layer of ergonomics that can
+change this fact, and picking a strategy for conflict resolution is
+undoubtly domain-specific.
+
+You mentioned that I proposed a solution on URL history tracking that
+fowards all of the responsability to the programmer, but I was only
+giving a simple example, and that same problem could be tackled in many
+different ways, and the person creating the application has a decision
+to make on it.
+
+Let's expand on the URL history on the browser example from earlier, and
+explore how to handle identity.
+
+The application developer now has to decide what they want to consider a
+URL being "the same" as another, and that's what I think you meant when
+you talked about pushing domain identity to the application.
+
+What does the programmer wants the identity of a URL to be? Is it a
+UUID attributed to it when asserting the entitiy on the database? If so,
+you would need to do things like the SELECT DISTINCT and SELECT SUM I
+proposed, inevitably. The programmer may otherwise try to avoid this by
+always starting from an existing database, so the history could be
+common whenever a new database instance starts. One would just not
+simply 'git init', they would 'git clone' in order to work with shared
+identities that are created in a distributed way.
+
+Is the identity of a URL its string? If so, the URL would have to be
+treated as an URI, and that string would be effectively global, and
+updates would have to be handled with lots of care.
+
+The database *can't* make those decision. What it *can* do is provide
+tools that allow the programmer to work with this inherent complexity.
+And I see no way around it. No amout of ergonomics can solve this, and
+removing the ability to have 1:1 lookups is the nature of decentralized
+client-side applications, it's not me trying to forward a problem that
+the database should solve to the programmer.=20
+
+A simple comparison is: whenever we talk about code, we use SHAs to
+pinpoint which piece of code we mean. What does a SHA say about the
+existence of other branches that disagree? Nothing! You can't ever
+assume that the commit you're on is the latest version of a file, or
+even is the only version of a file. While you were not looking, any
+number of new versions could have been created, which means that when
+you fetch, there isn't a single README. There are *at least* two of
+them: README from master, and README from origin/master. They may be the
+same, and they may not be the same, but saying just "the README file" is
+an incomplete depiction of it: you either say "my README file" or talk
+about both of them, which do coexist.
+
+There's no denial that this model is much more complex than the usual
+1:1 lookup we're used to, but trying to hide this in the database
+implementation somehow would be a bad idea. The programmer brought this
+to themself when they choose to build a decentralized client-side
+application.=20
+
+However, with a solid foundation, there are still lots of room for work
+on ergonomics, like:=20
+
+1. diffing algorithms for text like you mention would totally
+make sense to be in a "contrib/" directory, so that people could pick
+different diffing algorithms for different string fields.
+
+2. whenever a 'dev comes up with the bright idea of annotating "data
+objects" with some kind of conflict resolution', I could point them to a
+"IConflictResolutionStrategy" interface for they to implement. Their
+implementation could even end-up under said "contrib/" directory. Even
+better if "IConflictResolutionStrategy" could be composed! A programmer
+would then pick different strategies for different entities in their
+domain, like in a buffet.
+
+3. offer some low-level building blocks that can be applied to different
+domains, like tombstones, max(a.last-modified, b.last-modified), etc.
+
+...and so on.
+
+The point is: the database is not in a position to make this type of
+decision. Only the programmer is, and they must be aware of it.
+
+You said that:
+
+> the goal of a database is to help me efficiently, accurately, and
+> ergonomically represent my domain=20
+
+For me, the crucial part is the accurately comes first, and ergonomics
+comes second. No accuracy should be sacrificed in name of ergonomics,
+since the inherent complexity is already too big to be dealt with.
+
+The syncing part is not free.
+
+[1]: As defined in http://curtclifton.net/papers/MoseleyMarks06a.pdf
+
+# On history rewriting
+
+You talked about how Mentat was trying to reconcile changes by rewriting
+the history in a stable and consistent order.
+
+I'd argue that instead of a linear log where things are rebased, a DAG
+log where things are merged is a more flexible approach. Let me show you
+where I'm coming from first.
+
+You said that:
+
+> Phrased differently: in the above, your developers want some way to
+> write
+>
+> SELECT url, title FROM pages
+>
+> and
+>
+> SET title FOR page WITH url 'foo.com' TO 'whatever'
+
+This may still be true, but with a caveat: you'd be asserting facts to
+the local "timeline", in a somewhat similar way that the Mentat wiki
+describes[2] (I feel funny giving you a link to Mentat's wiki).=20
+
+What I'd do differently is to don't try so hard to make everyone
+converge on the same thing always, and represent timelines in a more
+flexible way.
+
+Instead of having two types of timelines, =CE=B1 and =CE=B2, there would be=
+ a
+multitude of timelines, one for each instance of the database. Let's say
+we have 3 of them for the purpose of explanation.
+
+Instead of adding an extra 'timeline_id' identifier, I'd add a
+':db/txParent' to the transaction entity, with cardinality many, and the
+database instance would always know it's latest local parent. This is
+effectivelly like a branch, but not a shared one, and transations with
+more than one ':db/txParent' represent merges. Those would only happen
+when explicitly solving conflicts, and any given database instances
+could know about a multitude of timelines.
+
+This way, how can one know the current state of the database?=20
+
+>From the first root datom, the database would rollup every datom taking
+the timelines and merges in consideration, and it would be the sum of
+data from all timelines. When a conflict exists, the local timeline is
+always picked so the database can still serve writes, and the conflict
+would be presented when serving reads.=20
+
+The obvious problem of always favouring the local timeline is that each
+timeline would show different "primary" answers, but that's sounds
+somewhat reasonable to me to enable the database to keep working while
+offline, and while the conflict isn't resolved.
+
+This also means that "sync" are two distinct things: exchanging timeline
+data, and reconciling that data locally. The only part that's online is
+the "exchange", and instances resolve conflict locally. This way, the
+database is always available, even when offline, and even when conflict
+is present.
+
+But how would queries over history behave? No clue yet =F0=9F=98=AC.
+
+I tried not to sound too hand wavy, and I've only prototyped a small
+part of this in the past, so there are probably many more problems with
+this approach. I don't claim this is a sound solution yet, but it does
+handle some of the problems that Mentat faced in a different way. I'd be
+interested to see you poking holes on it.=20
+
+When combined with strict schema growth, new data always comes after
+it's schema declaration, so the application doesn't have to coordinate
+around distributed schema evolution.
+
+[2]: https://github.com/mozilla/mentat/wiki/Multiple-timeline-support
+
+# On CouchDB/PouchDB's entity conflicts
+
+CouchDB/PouchDB's most fundamental value is the document. This means
+that a single document write is atomic (not true for clustered CouchDB),
+but you end up rewritting the full document ([3], on time 3:35). If you
+represent an entity as a document, the whole entity is in conflict, not
+the attribute that changed.
+
+So it doesn't know the difference between two changes on unrelated
+attributes and full rewrites of the full document: everything is a
+conflict in CouchDB/PouchDB's view.
+
+On another talk ([4] on time 5:30, also by Joan Touzet), she mentions
+the desire to add partial updates to CouchDB, but I don't know if it was
+pursued.
+
+Checking for conflicts on revisions is considered good practice ([1], on
+time 10:21) on the community.
+
+[3]: https://www.youtube.com/watch?v=3DBKQ9kXKoHS8
+[4]: https://www.youtube.com/watch?v=3D0gAZHIYBh-g
+
+# On zipping identities together
+
+=F0=9F=91=8D
+
+# Misc
+
+TIL: I didn't know you worked on AllegroGraph.
+
+P.S.: I haven't reviewed this text too much, and it might have gotten a
+bit confusing. I'm happy to clarify any points, and I'll consider
+writing a more formal version of this braindump later. I chose to send
+this like this over taking more time and letting the discussion cool
+down.=20
+
+From eu@euandre.org Fri Sep 4 00:00:13 2020
+Received: from box.euandre.org (box.euandre.org [46.101.160.115])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 7A675FF1B9
+ for <~euandreh/public-inbox@lists.sr.ht>; Fri, 4 Sep 2020 00:00:12 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (2048-bit key) header.d=euandre.org header.i=@euandre.org header.b=teyCS1z1
+Received: from authenticated-user (box.euandre.org [46.101.160.115])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
+ (No client certificate requested)
+ by box.euandre.org (Postfix) with ESMTPSA id 3507CFCB45;
+ Thu, 3 Sep 2020 21:00:09 -0300 (-03)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=euandre.org; s=mail;
+ t=1599177611; bh=mYcY4i3Y9/GUAx3/nY2Lu91JMvyDWaWOxTADQnXB+MM=;
+ h=From:To:Cc:Cc:Subject:In-Reply-To:References:Date:From;
+ b=teyCS1z1iKrOm7S9ivB92sQQ5uq6Kb9TRtkrIvyFYqPWIP9zFVq4sYuuU5NQZwDSi
+ GHHeO9otqioa+dWLVcXYITaxTvgD3uAaiI/Xa9Nzj3swN1DWuzGwJ7Ovqf8vQDBSQI
+ 0VROFULtzElG2iNTv3tyWy6C7Hu12whmbfteUtq6aTzonf9pFIP6Wio6wGakmS9px9
+ xwKDfuZgrdN8tUWhjAM+Joz9bwRgRg08+hJOf9Jd9JKShTgZGosTIOYDl8HnfQwR7F
+ fZbYza/n4vBwLdkpDlOpX9wVdVprXmvM3cD+zoPQ/ukRomUmjQ6KFETBOksZ4t7pY1
+ E/KZ/wEEBxRJA==
+From: EuAndreh <eu@euandre.org>
+To: Richard Newman <rnewman@twinql.com>
+Cc: "~euandreh/public-inbox@lists.sr.ht" <~euandreh/public-inbox@lists.sr.ht>
+Cc:
+Subject: Re: The database I wish I had
+In-Reply-To: <87h7se5t1w.fsf@euandre.org>
+References: <7454DA45-C5A4-4A54-8D02-1F3BD37726E9@twinql.com>
+ <010101744a592b75-1dce9281-f0b8-4226-9d50-fd2c7901fa72-000000@us-west-2.amazonses.com>
+ <87sgc1xu7k.fsf@euandre.org>
+ <9652AAE0-10DF-4D13-ABE8-B30667BE27DD@twinql.com>
+ <010101744b260d62-1bb508ab-647f-4877-bdf9-f988667d56e1-000000@us-west-2.amazonses.com>
+ <878sdsy6sc.fsf@euandre.org>
+ <7027FD8B-1B30-435F-A840-7DC61A722E0B@twinql.com>
+ <010101744f99448f-fa8dd88e-0daf-41f8-b3ca-6ea551c300e4-000000@us-west-2.amazonses.com>
+ <87h7se5t1w.fsf@euandre.org>
+Date: Thu, 03 Sep 2020 20:57:28 -0300
+Message-ID: <87eeni5s7r.fsf@euandre.org>
+MIME-Version: 1.0
+Content-Type: text/plain
+
+I better get a POC so I can stop talking in "what ifs".
+
+I was more focused on the storage implementation side of it, but I'm
+considering not implementing a storage backend for this and just using
+SQLite instead (more detailed in [0]).
+
+Building durable persistent data structures on top of SQLite backed
+storage is still a bit distant, but the model I presented could benefit
+from a POC.
+
+I'll try starting that instead of giving so much focus on storage, and
+share it later.
+
+[0]: https://euandre.org/2020/08/31/the-database-i-wish-i-had.html#fn:posix-sqlite
+
+From eu@euandre.org Fri Sep 4 02:12:34 2020
+Received: from box.euandre.org (box.euandre.org [46.101.160.115])
+ by mail-b.sr.ht (Postfix) with ESMTPS id DE901FF196
+ for <~euandreh/public-inbox@lists.sr.ht>; Fri, 4 Sep 2020 02:12:33 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (2048-bit key) header.d=euandre.org header.i=@euandre.org header.b=aOTLZVo3
+Received: from authenticated-user (box.euandre.org [46.101.160.115])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
+ (No client certificate requested)
+ by box.euandre.org (Postfix) with ESMTPSA id 13742FCB45;
+ Thu, 3 Sep 2020 23:12:30 -0300 (-03)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=euandre.org; s=mail;
+ t=1599185552; bh=0eOLRgKRFwQtp0D1PSgenzUsGttO7assNfrY8hnPqz8=;
+ h=From:To:Cc:Cc:Subject:In-Reply-To:References:Date:From;
+ b=aOTLZVo3JPMEsqZSTB2G6Y+T0rDJDcBXrDuU5+R0SjJN9lX+c01/ir+CO/+Av22VH
+ IhKMuNJU1nVjzOf4Qs0CnmJO1Agzeuq+pf41gU0qIx0kZVSaUZP8PgXzpG1DUvfQ5T
+ tEKNEHhRVFapOZ9HYeYzbMcUdO4z4haaEkuxerFOwwvIudLPkmCCeJH7xFouMvkMSN
+ vxyKaJK7RdWzBkiXZutwKzS6BEbvbalzdMf7IcLXAlmYsUdV3bMPH0Bvg5HsxvLY1I
+ YQNTiPp8yRiojv69hKqGKByUvhPveXVtbZNt3k8y4cwU5UAaAXnTQrNwSFhy3pxB+9
+ fwaRNRHiXi1mQ==
+From: EuAndreh <eu@euandre.org>
+To: Richard Newman <rnewman@twinql.com>
+Cc: "~euandreh/public-inbox@lists.sr.ht" <~euandreh/public-inbox@lists.sr.ht>
+Cc:
+Subject: Re: The database I wish I had
+In-Reply-To: <87eeni5s7r.fsf@euandre.org>
+References: <7454DA45-C5A4-4A54-8D02-1F3BD37726E9@twinql.com>
+ <010101744a592b75-1dce9281-f0b8-4226-9d50-fd2c7901fa72-000000@us-west-2.amazonses.com>
+ <87sgc1xu7k.fsf@euandre.org>
+ <9652AAE0-10DF-4D13-ABE8-B30667BE27DD@twinql.com>
+ <010101744b260d62-1bb508ab-647f-4877-bdf9-f988667d56e1-000000@us-west-2.amazonses.com>
+ <878sdsy6sc.fsf@euandre.org>
+ <7027FD8B-1B30-435F-A840-7DC61A722E0B@twinql.com>
+ <010101744f99448f-fa8dd88e-0daf-41f8-b3ca-6ea551c300e4-000000@us-west-2.amazonses.com>
+ <87h7se5t1w.fsf@euandre.org> <87eeni5s7r.fsf@euandre.org>
+Date: Thu, 03 Sep 2020 23:02:55 -0300
+Message-ID: <87a6y65meo.fsf@euandre.org>
+MIME-Version: 1.0
+Content-Type: text/plain
+
+Here's what I was referring to when talking about "durable persistent
+data structures": https://www.youtube.com/watch?v=Cym4TZwTCNU
+
+From eu@euandre.org Sat Sep 5 09:07:44 2020
+Received: from box.euandre.org (box.euandre.org [46.101.160.115])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 6A917FF1F5
+ for <~euandreh/public-inbox@lists.sr.ht>; Sat, 5 Sep 2020 09:07:43 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (2048-bit key) header.d=euandre.org header.i=@euandre.org header.b=grYKK93t
+Received: from authenticated-user (box.euandre.org [46.101.160.115])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
+ (No client certificate requested)
+ by box.euandre.org (Postfix) with ESMTPSA id 92EB0FC83F;
+ Sat, 5 Sep 2020 06:07:40 -0300 (-03)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=euandre.org; s=mail;
+ t=1599296862; bh=RjoAEI4vfrQ1VCfrySR+wMnwePv+AnUVT1ZbWcm8O1A=;
+ h=From:To:Cc:Cc:Subject:In-Reply-To:References:Date:From;
+ b=grYKK93td5WpquSSrkfBXBzUeJ25jSXwwjOE/x5c8rfR1DVAT1sDu7NfGVP+80pBW
+ oG7IM1+Kpjz7PL15Bg01iDOXTLXMD/4GeikxqSUUG2I9Pj0NEO4eNzl6hNTfNDBuMA
+ E7cz3qVVonEDHIYGlXvavHAWxWU6MXoSdDvEn0K2zNPU3qmo5PJ0vTKYNQTPkInY1N
+ DRSUHv6DgfTUUYyrXdcw0rIPhQdNvobloBT29Q7LOtvXcBtiMhWlLMG30ifV+ETmhU
+ h6AIw5myD387Nf7GRL6TfmtM1zWotf92jSop38NRnUCOJQ8iBpUrTt74qaSsuhcDbx
+ p4C1GSTXxTj2g==
+From: EuAndreh <eu@euandre.org>
+To: Richard Newman <rnewman@twinql.com>
+Cc: "~euandreh/public-inbox@lists.sr.ht" <~euandreh/public-inbox@lists.sr.ht>
+Cc:
+Subject: Re: The database I wish I had
+In-Reply-To: <87a6y65meo.fsf@euandre.org>
+References: <7454DA45-C5A4-4A54-8D02-1F3BD37726E9@twinql.com>
+ <010101744a592b75-1dce9281-f0b8-4226-9d50-fd2c7901fa72-000000@us-west-2.amazonses.com>
+ <87sgc1xu7k.fsf@euandre.org>
+ <9652AAE0-10DF-4D13-ABE8-B30667BE27DD@twinql.com>
+ <010101744b260d62-1bb508ab-647f-4877-bdf9-f988667d56e1-000000@us-west-2.amazonses.com>
+ <878sdsy6sc.fsf@euandre.org>
+ <7027FD8B-1B30-435F-A840-7DC61A722E0B@twinql.com>
+ <010101744f99448f-fa8dd88e-0daf-41f8-b3ca-6ea551c300e4-000000@us-west-2.amazonses.com>
+ <87h7se5t1w.fsf@euandre.org> <87eeni5s7r.fsf@euandre.org>
+ <87a6y65meo.fsf@euandre.org>
+Date: Sat, 05 Sep 2020 06:07:31 -0300
+Message-ID: <878sdosib0.fsf@euandre.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: quoted-printable
+
+> The obvious problem of always favouring the local timeline is that each
+> timeline would show different "primary" answers, but that's sounds
+> somewhat reasonable to me to enable the database to keep working while
+> offline, and while the conflict isn't resolved.
+
+Also a possible ergonomics layer that can be built on top of some lower lev=
+el
+primitive:
+
+Suppose we want to add a profile page to an app, but the user will be
+able to edit their profile while offline.
+
+Such profile page contains 3 parts: name, bio and links. Here's how it
+could look like:
+
+{:profile/name "Foo Bar"
+ :profile/links #{{:link/title "Personal webpage"
+ :link/position 0
+ :link/address #uri "https://example.com"}
+ {:link/title "Mastodon profile"
+ :link/position 1
+ :link/address #uri "https://example.org"}
+ {:link/title "Photo album"
+ :link/position 2
+ :link/address #uri "https://example.net"}}
+ :profile/bio "My lengthy bio (...)"}
+
+For each of those attributes, one could want to handle conflicts in a diffe=
+rent
+way:
+
+1. For the :profile/name, you could want to be fully conservative, and never
+ resolve any conflict whatsoever. If the user changes their name to "Foo =
+Bax"
+ in one device and to "Foo Baz" in another, you may choose to not solve t=
+his
+ conflict on the application. Instead you could just pick one of them to =
+show
+ to the user on their profile page, say "Foo Baz", but add an exclamation=
+ mark
+ icon =E2=9D=97=EF=B8=8F beside it. When the user touches the icon, the a=
+pp asks: "do you want
+ to keep 'Foo Baz' or would you like to change it to 'Foo Bax'?". This co=
+uld
+ be done multiple times for multiple concurrent conflicts, and when user
+ confirms their choice, that's a merge that resolves the conflict for all
+ instances when propagated.
+
+ The upside is that the app doesn't try to be smart about reconciling the
+ :profile/name, showing things like "Foo Baxz" or "Foo BazFooBax". The
+ downside is that if the app doesn't handle this explicitly, they'll be
+ perpetually inconsistent.
+
+2. For the :profile/links, specifically the :link/position attribute, you m=
+ight
+ try an intermediate approach. Say you want to handle reordering less
+ conservatively. Say the user changes the position of the "Photo album" l=
+ink
+ to 1 in a device, and to 0 on another device, and that is their actual
+ desired final position for this link. You could solve it by doing
+ something trivial, like picking the transaction UUID with the lowest
+ alphanumerical value. This gives you consistent choices across instances=
+, and
+ a potential nuisance for the user: if the winning final state is with "P=
+hoto
+ album" a 0, nothing needs to be done, and all instances agree with this.=
+ If
+ it isn't, then the user should reorder again, and all instances would st=
+ill agree.
+
+ The upside of this is that this conflict resolution strategy allows you =
+to
+ never stay conflicted for this particular attribute, and you could have =
+1:1
+ lookups back. The downside is that this assumes that the transactions th=
+at
+ are reordering links contain only :link/position attribute asserts.
+
+3. For the :profile/bio, you could be extremely optimistic, and try to comb=
+ine
+ all edits. This could be done by modeling it using commutative edits, si=
+milar
+ to how in [0], and let everything always converge. Maybe even adopt the
+ binary enconding of edits proposed in [1], and encode all of the edit hi=
+story
+ in the database itself. They talk a lot about the metadata overhead of t=
+he
+ edit history, and recording every edit as they come in would also add a
+ datom-related overhead, too. It might be more advantegeous to accumulate=
+ the
+ edit history, and when the app closes or a relevent event occurs, all ed=
+its
+ are combined into a single binary, and that would be a single datom entr=
+y.
+ This would remove the datom-related overhead while still persisting the =
+full
+ edit history.
+
+ The upside of this is that the text could always converge, you would get
+ 1:1 lookups back and you wouldn't have to deal with diffs on a huge stri=
+ng,
+ neither would the user. The downside is that you'll need a layer on top =
+of the
+ transactions to actually derive the text, and querying would get trickie=
+r.
+ The database would give you the ordered list of all binary enconding thi=
+ngs,
+ and a function would turn that into text.
+
+ Another downside is that this is very much an open research problem, and
+ probably has a lot of maturity to be gained. It would be very powerful if
+ this could be built on top of some database primitive, without the datab=
+ase
+ having to explicitly support it.
+
+All of that to handle the conflicts of *each attribute*, and they're all
+the programmer's responsability to choose.
+
+If the database was versatile enough, an ideal world the programmer could w=
+rite:
+
+;; schema definition
+[{:db/ident :profile/name
+ :db/valueType :db.type/string
+ :db/cardinality :db.cardinality/one
+ :db/conflict-strategy :db.conflict-strategy/none}
+ {:db/ident :link/position
+ :db/valueType :db.type/long
+ :db/cardinality :db.cardinality/one
+ :db/conflict-strategy :db.conflict-strategy/simple-winner}
+ {:db/ident :profile/bio
+ :db/valueType :db.type/string
+ :db/cardinality :db.cardinality/one
+ :db/conflict-strategy :db.conflict-strategy/automerge}
+ ...]
+
+And be done with it.
+
+If those :db/conflict-strategy fields could be open and extensible, the dat=
+abase
+could learn new algorithms to deal with conflict without changing it's
+internals, and people could teach how translate the :profile/bio from
+:db.type/string the actual :db.type/bytes that would be actually stored.
+
+Also, I think this is what I was trying to say with "coupling two differente
+layers of the database". However, I haven't tested any of this yet.
+
+[0]: https://doi.org/10.1016/j.jpdc.2010.12.006
+[1]: https://github.com/automerge/automerge-perf/tree/master/columnar
+
+From rnewman@twinql.com Sat Sep 5 20:04:34 2020
+Received: from a58-61.smtp-out.us-west-2.amazonses.com (a58-61.smtp-out.us-west-2.amazonses.com [54.240.58.61])
+ by mail-b.sr.ht (Postfix) with ESMTPS id D36DAFF13B
+ for <~euandreh/public-inbox@lists.sr.ht>; Sat, 5 Sep 2020 20:04:32 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (1024-bit key) header.d=twinql.com header.i=@twinql.com header.b=qiZnhSkC;
+ dkim=pass (1024-bit key) header.d=amazonses.com header.i=@amazonses.com header.b=MQAUAk7Q
+DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/simple;
+ s=qvs2k2sd2ivtrflyu2nswmravuqizjml; d=twinql.com; t=1599336271;
+ h=Subject:From:To:Cc:Date:Mime-Version:Content-Type:Content-Transfer-Encoding:In-Reply-To:References:Message-Id;
+ bh=lPgy0nUilFxkCQGCuQxBgZW5Udn+JVGxl6bDms54Sk4=;
+ b=qiZnhSkCMRBNxcfwIf1Ecic54ZZmlMpioyl5vuE02tb5tFBnHk+bAFJcpIwJInMS
+ EdJiWj0xSnX7x4dBpD6JqwFHvQfx3wvs+tW6yxyAvjIQcFYb4qoC8ufiI33atGIOW/R
+ rrhRvIaDsmSyeT53ab+mytd/6WCxBnufMhHh5rrE=
+DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/simple;
+ s=hsbnp7p3ensaochzwyq5wwmceodymuwv; d=amazonses.com; t=1599336271;
+ h=Subject:From:To:Cc:Date:Mime-Version:Content-Type:Content-Transfer-Encoding:In-Reply-To:References:Message-Id:Feedback-ID;
+ bh=lPgy0nUilFxkCQGCuQxBgZW5Udn+JVGxl6bDms54Sk4=;
+ b=MQAUAk7QsnWFnb7gUs7do0tFKJ/ZEjJ+XsIFxth+m525oY5TIo0jyYTBcFxAeQU3
+ ruo+SqNRgJBJVtzqIHoghOuG8UqNq50MkyFLgv+agjPe95SKqQPIgnHeraWBXXccyjN
+ C42JTZ/q9uSxe/Kxy2m7iHmbpGF3+O3tP1PMHXLk=
+Subject: Re: The database I wish I had
+From: =?UTF-8?Q?Richard_Newman?= <rnewman@twinql.com>
+To: =?UTF-8?Q?EuAndreh?= <eu@euandre.org>
+Cc: =?UTF-8?Q?=7Eeuandreh/public-inbox=40lists=2Esr=2Eht?=
+ <~euandreh/public-inbox@lists.sr.ht>
+Date: Sat, 5 Sep 2020 20:04:31 +0000
+Mime-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+In-Reply-To: <87h7se5t1w.fsf@euandre.org>
+References: <7454DA45-C5A4-4A54-8D02-1F3BD37726E9@twinql.com>
+ <010101744a592b75-1dce9281-f0b8-4226-9d50-fd2c7901fa72-000000@us-west-2.amazonses.com>
+ <87sgc1xu7k.fsf@euandre.org>
+ <9652AAE0-10DF-4D13-ABE8-B30667BE27DD@twinql.com>
+ <010101744b260d62-1bb508ab-647f-4877-bdf9-f988667d56e1-000000@us-west-2.amazonses.com>
+ <878sdsy6sc.fsf@euandre.org>
+ <7027FD8B-1B30-435F-A840-7DC61A722E0B@twinql.com>
+ <010101744f99448f-fa8dd88e-0daf-41f8-b3ca-6ea551c300e4-000000@us-west-2.amazonses.com>
+ <87h7se5t1w.fsf@euandre.org>
+ <B032A2E0-0679-4FF6-9A80-06450A8F36B1@twinql.com>
+X-Priority: 3 (Normal)
+X-Mailer: Amazon WorkMail
+Thread-Index: AQHWgHcPJ4udfXYrRaOwhoO7EcsDJQAErSAlAAfQ7+gAIgKd3wAzRhy+AHUc9SwA0i0RQQ==
+Thread-Topic: The database I wish I had
+X-Original-Mailer: Apple Mail (2.3445.104.14)
+X-Wm-Sent-Timestamp: 1599336269
+Message-ID: <010101745fdecd5f-52ab4d88-26f1-4a6d-879d-3234c1956260-000000@us-west-2.amazonses.com>
+X-SES-Outgoing: 2020.09.05-54.240.58.61
+Feedback-ID: 1.us-west-2.An468LAV0jCjQDrDLvlZjeAthld7qrhZr+vow8irkvU=:AmazonSES
+
+Ah, thank you for the conversation refactor!=0D=0A=0D=0A> The wiki page y=
+ou pointed to reinforces that: it talks a lot about how=0D=0A> to coordin=
+ate attributes that changed their names, change in vocabulary,=0D=0A> sha=
+red ownership of changes in schema and even schema versioning!=0D=0A>=20=0D=
+=0A> All of them are the opposite of what I want, and they're "schema=0D=0A=
+> breakage", not "schema growth".=0D=0A=0D=0AMy two observations are thes=
+e:=0D=0A=0D=0AOn the one hand, front-end developers tend to think in term=
+s of change =E2=80=94 "this thing's title was X and is now Y" =E2=80=94 a=
+nd states =E2=80=94 "give me a list of things and their current titles so=
+ I can search them or show them in a table".=0D=0A =20=0D=0AThat view of =
+change extends to schema/data, because the schema stretches most of the w=
+ay towards the front-end itself, sometimes all the way to a "view model" =
+in the MVVM sense.=0D=0A =20=0D=0AAnd it involves migration, because no f=
+ront-end developer (or back-end developer, for that matter) wants to have=
+ to write queries that accommodate all of the different ways a particular=
+ concept might have been represented in the last ten years. Doing so is m=
+entally taxing, is very difficult to test, and results in wildly variable=
+ performance. They want to fix their data once, or define how to roll it =
+up into a single state once, and make sure the old representation never g=
+ets used again. And these representations do change, often significantly;=
+ making sure they don't requires tremendous up-front deep thought (for ex=
+ample, my modeling attempt for embedded videos), and results in something=
+ that is often normalized to the point of non-viability (already a concer=
+n with tuple stores).=0D=0A=0D=0AOn the other hand, storage/syncing devel=
+opers learn to love immutable logs, because it makes a ton of sense. Sync=
+able storage systems should be based on an immutable timeline of appended=
+ observations of the world.=0D=0A=0D=0A=0D=0AThere is obvious conflict be=
+tween these two. Resolving that conflict involves some complexity, and I =
+felt it better to address that complexity once, inside the storage system=
+=2E=0D=0A=0D=0AI didn't feel that it was worthwhile to build a storage sy=
+stem that required front-end developers to adjust their mental model so f=
+ar from what they're trying to achieve on a daily basis, because they hav=
+e a choice in which tools they use.=0D=0A=0D=0AFrom my perspective, the p=
+rimary goal has to be to give them a system they're willing to use (a cle=
+ar, current-focused schema and the ability to represent change), with the=
+ additional goal of a useful log and better formalisms underneath to supp=
+ort syncing. Putting it the other way 'round would simply lead to them us=
+ing SQLite or JSON files, omitting syncing, building bad ad hoc sync solu=
+tions, and otherwise continuing in the way that Mozilla had been doing th=
+ings.=0D=0A=0D=0AMentat, like Datomic, is a stateful layer on top of log-=
+structured data, with the current schema =E2=80=94 like the current state=
+ of the world! =E2=80=94 being materialized from the log.=0D=0A=0D=0A=0D=0A=
+> When more than one database instance can add data, the programmer needs=
+=0D=0A> to think of it as an open world, where new things are accrued, bu=
+t old=0D=0A> things don't disapper. Changing the vocabulary mid-air only =
+makes this=0D=0A> open world brittle so "migrations" can be performed.=0D=
+=0A=0D=0AI am a fan of open-world systems =E2=80=94 I spent 7+ years work=
+ing on semantic web stuff! =E2=80=94 but I'm not aware of any client appl=
+ications with any degree of adoption that use the open-world model. Havin=
+g multiple components share state doesn't require an open world per se.=0D=
+=0A=0D=0ABeing able to evolve the representation of existing data, just l=
+ike being able to 'change' the state of the world, is regarded as a *feat=
+ure* by developers, and they typically would rather sacrifice interoperab=
+ility than take on the burden of dealing with a pile of data that only ge=
+ts more crufty over time. In the 'schemaless' world (e.g., DynamoDB), dev=
+elopers take a mix of painful approaches (ranging from adding version tag=
+s that drive switches in application code, to doing manual migrations bet=
+ween tables) to clean up old data when the previous representation cannot=
+ stretch far enough.=0D=0A=0D=0AI tried with Mentat to strike a balance h=
+ere: to design a system that would allow multiple teams to interact, defi=
+ning their vocabularies in an ever-growing way, with specific transformat=
+ion steps as needed, and being able to detect and handle conflicting voca=
+bulary usage within that closed world. You can absolutely use Mentat as a=
+ system with no vocabulary modification ever, complicating your queries t=
+o accommodate, but it would not have met the needs of its audience:=0D=0A=
+=0D=0AD: "So, I realized that having a page -> visit timestamp property d=
+oesn't actually do what we want; we need to be able to mark each visit wi=
+th which device visited the page. How do I change the schema and the exis=
+ting data=3F"=0D=0AR: "You can't; make a new property. Every time you wan=
+t to retrieve the visits for the page, everywhere in your app, your queri=
+es need to look at both properties."=0D=0AD: "Can I write some function t=
+hat seamlessly transforms the old data to the new format and works with s=
+yncing=3F"=0D=0AR: "I wrote a doc about that, but we decided that purity =
+was better. You can write code to manually retract the old stuff, but oth=
+er syncing devices can still mess you up. Sorry."=0D=0A=0D=0AResolving co=
+nflicts and supporting sync was a core part of Mentat's charter. The last=
+ thing I wanted was for developers to think the system was hard to use, o=
+r to build a second storage layer on top to maintain a snapshot of the cu=
+rrent data in an app-centric format.=0D=0A=0D=0A=0D=0A> You mentioned erg=
+onomics thrice on your message, and I believe that your=0D=0A> insistence=
+ of it is a sign of the coupling of two different layers of=0D=0A> the da=
+tabase, and makes ease of use drive the fundamentals.=0D=0A=0D=0AYes, exa=
+ctly. The goal of Mentat wasn't to build a pure log-structured store: it =
+was to build a store with a developer interface that wasn't too alien to =
+an engineer who might store data in JSON or SQLite, but with internals th=
+at made sharing data, syncing data, and evolving data easier and safer.=0D=
+=0A=0D=0A=0D=0A> I also think=0D=0A> that your statement on conflict reso=
+lution being a requirement is not a=0D=0A> good idea. Let me expand on th=
+at.=0D=0A>=20=0D=0A> I'd argue that conflict resolution is inherit comple=
+xity [1] of creating=0D=0A> a decentralized client-side application that =
+can sync data.=0D=0A>=20=0D=0A> That means that the responsability to sol=
+ve conflicts is not the=0D=0A> database's, but the application's responsa=
+bility, and making decisions=0D=0A> of conflict resolution on the databas=
+e limits its application, and I=0D=0A> wouldn't consider it to be general=
+ purpose.=0D=0A=0D=0AAh, here's an interesting point.=0D=0A=0D=0AThere ar=
+e two positions here: am I vending a database, or am I vending a storage =
+solution for a client app=3F=0D=0A=0D=0ABuilding Firefox, Firefox for And=
+roid, and Firefox for iOS convinced me that implementing a synchronizatio=
+n solution that is not tightly coupled with the storage layer =E2=80=94 t=
+hat is, one that is just another consumer of the same storage interface t=
+hat the rest of the app uses =E2=80=94 is difficult and error-prone. Sync=
+ needs to see writes as they happen, and synchronization has particular i=
+mplications for how data is stored.=0D=0A=0D=0AI also found that offloadi=
+ng the difficulties in syncing =E2=80=94 conflict resolution, tracking ch=
+ange, merging identities, etc. =E2=80=94 to application developers leads =
+to worse outcomes.=0D=0A=0D=0AMy feeling is that it should be possible fo=
+r the application to educate the storage layer about how to handle most o=
+f the conflicts it encounters, and to model its data in a way that makes =
+conflicts meaningful and minimal, without sacrificing the kinds of data r=
+epresentations with which developers feel comfortable.=0D=0A=0D=0A=0D=0A>=
+ When two offline database instances change the same field, there's no=0D=
+=0A> escaping from the bottom reality, which is: there is a conflict, and=
+ it=0D=0A> needs to be resolved somewhere. There's no layer of ergonomics=
+ that can=0D=0A> change this fact, and picking a strategy for conflict re=
+solution is=0D=0A> undoubtly domain-specific.=0D=0A=0D=0AThe approach I w=
+as taking for syncing in Mentat involved rewriting (or permanently associ=
+ating) identifiers based on uniqueness constraints. That is, as a develop=
+er I could transact a schema fragment:=0D=0A=0D=0A=0D=0A {:history/url :=
+db/unique :db.unique/value}=0D=0A=0D=0A=0D=0Aand thereafter say things li=
+ke=0D=0A=0D=0A=0D=0A {:history/url "https://foo.com/"=0D=0A :history/t=
+itle "Today's title"}=0D=0A=0D=0A=0D=0AIf the log already contains this, =
+with a local ID:=0D=0A=0D=0A=0D=0A [1234 :history/url "https://foo.com/"=
+]=0D=0A=0D=0A=0D=0Athe new transact would add to the log something like:=0D=
+=0A=0D=0A=0D=0A [1234 :history/title "Today's title"]=0D=0A=0D=0A=0D=0AA=
+nother device can record similar data:=0D=0A=0D=0A=0D=0A {:history/url "=
+https://foo.com/"=0D=0A :history/title "A different title"}=0D=0A=0D=0A=
+=0D=0Awhich would transact=0D=0A=0D=0A=0D=0A [4567 :history/title "A dif=
+ferent title"]=0D=0A=0D=0A=0D=0AA query on either platform can omit the I=
+D, using a lookup ref instead:=0D=0A=0D=0A=0D=0A [[:history/url "https:/=
+/foo.com/"] :history/title =3Ftitle]=0D=0A=0D=0A=0D=0AIn both cases we ge=
+t a 1:1 association between a URL and a 'page'. The use of lookup refs an=
+d the schema definition allows the developer to use the [attribute, URL] =
+pair as an external identifier in this context, while using an efficient =
+identifier internally.=0D=0A=0D=0AWhen the two transaction logs are merge=
+d, it follows from the schema definition that B(4567) =3D=3D=3D A(1234), =
+and that identity can be used in the store to zip the two graphs together=
+ in a way that is seamless to the developer: the 1:1 association is prese=
+rved. In practice we found that holding on to those numeric IDs, which ca=
+n change during syncing, in application code was rarely or never necessar=
+y.=0D=0A=0D=0AThis example is what I mean by 'teaching' the database abou=
+t domain definitions of identity. In a relational database, the schema is=
+ largely used to define structure and constraints. The way it feels to wr=
+ite a schema in Datomic/Mentat is much closer to 'explaining' how various=
+ kinds of new assertions and sync behaviors will interact with the existi=
+ng data =E2=80=94 the schema constraints are so often used to drive looku=
+p refs and updates, and of course there's no tabular structure.=0D=0A=0D=0A=
+This can get a merge all the way down to "this page's title changed from =
+X to Y at time T on one device and from X to Z on another at time Y; whic=
+h do you want=3F" which is _also_ something that you can teach the databa=
+se to automatically resolve =E2=80=94 annotate the schema to say that a c=
+onflict takes the time-latest value.=0D=0A=0D=0A=0D=0A> They may be the=0D=
+=0A> same, and they may not be the same, but saying just "the README file=
+" is=0D=0A> an incomplete depiction of it: you either say "my README file=
+" or talk=0D=0A> about both of them, which do coexist.=0D=0A=0D=0AI actua=
+lly used this as an example when explaining Mentat =E2=80=94 timelines ar=
+e branches, but the `datoms` table (the data you actually work with) is a=
+ Git "working tree". Developers (and end users) need a working tree and a=
+ single state of the world.=0D=0A=0D=0A=0D=0A> The point is: the database=
+ is not in a position to make this type of=0D=0A> decision. Only the prog=
+rammer is, and they must be aware of it.=0D=0A=0D=0AWhich kinds of progra=
+mmers are your audience=3F=0D=0A=0D=0AIn my experience, most developers a=
+re (a) not aware of the difficulties here, (b) do not have the time to di=
+g in to the details, and (c) would rather take reasonable defaults (e.g.,=
+ latest wins) or even data duplication or corruption than sacrifice simpl=
+icity to get it right. That is: if you presented them with the option of =
+a distributed sync system where they had to handle multiple timelines, us=
+e pluggable conflict-resolution modules, and write complicated queries to=
+ deal with that complexity, they are instead going to use JSON files.=0D=0A=
+=0D=0AIt would not be inaccurate to say that Mentat is/was _already_ way =
+too much of a shift for most developers; a tuple store with a log is expe=
+nsive and confusing in ways that a key-value store is not.=0D=0A=0D=0A=0D=
+=0A> You said that:=0D=0A>=20=0D=0A>> the goal of a database is to help m=
+e efficiently, accurately, and=0D=0A>> ergonomically represent my domain=20=
+=0D=0A>=20=0D=0A> For me, the crucial part is the accurately comes first,=
+ and ergonomics=0D=0A> comes second. No accuracy should be sacrificed in =
+name of ergonomics,=0D=0A> since the inherent complexity is already too b=
+ig to be dealt with.=0D=0A=0D=0ATo broaden your point a little bit, my po=
+sition on that has shifted over the years, after working on systems that =
+prioritized correctness over adoption or user experience (e.g., Firefox S=
+ync's perfect crypto, which caused _so many users_ to lose data because t=
+hey weren't prepared to manage long-lived secure keys, and couldn't under=
+stand the setup flow=E2=80=A6).=0D=0A=0D=0AA system that is perfect but t=
+hat people =E2=80=94 developers or end users =E2=80=94 don't use it is _w=
+orse_ than a system that makes compromises and gets some adoption. As Guy=
+ Steele put it about Java:=20=0D=0A=0D=0A "We were not out to win over t=
+he Lisp programmers; we were after the C++ programmers. We managed to dra=
+g a lot of them about half-way to Lisp."=0D=0A=0D=0AI think a brief tange=
+nt into ontology would be interesting. I know your point here is about ac=
+curately modeling change, but a similar set of positions exists when mode=
+ling information itself.=0D=0A=0D=0AThe 'correct' way to model the world =
+is as a time-series sequence of independent observations, with everything=
+ else being materialized from it through a carefully designed ontological=
+ lens, such that one doesn't have to accidentally make false statements i=
+n order to say something true.=0D=0A=0D=0AAt one end is the kind of ontol=
+ogy I drew on a whiteboard once, nearly twenty years ago, in which the co=
+ncept of a music "album" is broken down into issuances, releases, track o=
+rders, renditions, recordings, performers, names, identities, collectives=
+, etc. =E2=80=94 it's incredibly complex how one recording of a group of =
+people in a room might be recorded multiple times, remastered over the ye=
+ars, edited for various releases, and appear on multiple formats in diffe=
+rent versions, and how different versions of songs might have different b=
+ands or people playing different parts.=0D=0A=0D=0AMusic nerds really car=
+e about this, though perhaps not down to the level of detail to which I m=
+odeled it.=0D=0A=0D=0AAt the other end is how this kind of metadata is re=
+ally represented in the real world.=0D=0A=0D=0ATitle: "Landslide"=0D=0AAr=
+tist: "Fleetwood Mac"=0D=0APlay Count: 15=0D=0ATrack Number: 2=0D=0A=0D=0A=
+The theoretically pure way to model all this stuff makes it possible to s=
+ay "this recording of Landslide is of a performance by a band named Fleet=
+wood Mac =E2=80=94 the particular instance of Fleetwood Mac that included=
+ Stevie Nicks, not the one that included Bob Welch". Let's not even get i=
+nto the fact that there are two albums recorded by Fleetwood Mac titled "=
+Fleetwood Mac" =E2=80=94 (title, artist) isn't compound-unique!=0D=0A=0D=0A=
+But no user wants to say "Alexa, play Fleetwood Mac" and have her reply "=
+do you mean the band 'Fleetwood Mac' that recorded songs released by Warn=
+er Bros, or the band 'Fleetwood Mac' that recorded songs released by Blue=
+ Horizon"=3F They will exchange a certain amount of precision to cast a w=
+ider and more usable net.=0D=0A=0D=0APut bluntly: apps have trouble getti=
+ng the darn play counts on two devices to add up to the same number, let =
+alone this degree of precision. I would rather make it _possible_ for the=
+m to model data at a reasonable degree of accuracy, with no more difficul=
+ty than they would have writing a CREATE TABLE statement, and help them g=
+et their play counts right when they sync two devices, rather than force =
+them to take an extreme position on accuracy and leave them to figure out=
+ how to list how many times the user played a song.=0D=0A=0D=0A=0D=0A> Th=
+is way, how can one know the current state of the database=3F=20=0D=0A>=20=
+=0D=0A> From the first root datom, the database would rollup every datom =
+taking=0D=0A> the timelines and merges in consideration, and it would be =
+the sum of=0D=0A> data from all timelines. When a conflict exists, the lo=
+cal timeline is=0D=0A> always picked so the database can still serve writ=
+es, and the conflict=0D=0A> would be presented when serving reads.=20=0D=0A=
+>=20=0D=0A> The obvious problem of always favouring the local timeline is=
+ that each=0D=0A> timeline would show different "primary" answers, but th=
+at's sounds=0D=0A> somewhat reasonable to me to enable the database to ke=
+ep working while=0D=0A> offline, and while the conflict isn't resolved.=0D=
+=0A=0D=0AMy view is that end users expect all of their devices to present=
+ the same canonical view of their data, and manual conflict resolution sh=
+ould be rare/unnecessary/modeled as an explicit change or an explicit sta=
+te (a write like any other).=0D=0A=0D=0AFurther, developers expect querie=
+s to be fast and writes and queries to behave the same when devices are s=
+ynced, regardless of which device performs the write or runs the query.=0D=
+=0A=0D=0AThat makes it difficult to either diverge between devices for lo=
+ng periods, or to expect routine manual involvement in conflicts.=0D=0A=0D=
+=0AIt seems to me that the longer these distinct timelines exist, the mor=
+e conflicts will breed, as writes reflect different local states, and so =
+the difficulty of presenting a coherent view increases over time.=0D=0A=0D=
+=0AI expected probably one place to do manual conflict resolution in Fire=
+fox: password management. Even there it might be better to turn every con=
+flict into an explicit "previous passwords" entry, or to expose the log a=
+s a dynamic list of previous passwords =E2=80=94 that data is valuable fo=
+r detecting website breaches. That is, it's better to surface the alterna=
+tive data as a normal change and reach agreement with other devices, rath=
+er than preserving some kind of pending conflict.=0D=0A=0D=0ABy modeling =
+rebases or merges as a coherent set of retractions of conflicting data, i=
+t's still possible to implement log-oriented features like querying *ever=
+y* title a page has ever had, but eagerly reach a shared timeline. ("Show=
+ me that news article I remember reading".)=0D=0A=0D=0A=0D=0A> This also =
+means that "sync" are two distinct things: exchanging timeline=0D=0A> dat=
+a, and reconciling that data locally.=0D=0A=0D=0AAgreed.=0D=0A=0D=0A=0D=0A=
+> The only part that's online is=0D=0A> the "exchange", and instances res=
+olve conflict locally. This way, the=0D=0A> database is always available,=
+ even when offline, and even when conflict=0D=0A> is present.=0D=0A=0D=0A=
+The difference IMO is that the goal of conflict resolution is that every =
+device converges on the same canonical timeline, and does so as quickly a=
+s possible.=0D=0A=0D=0A=0D=0A> But how would queries over history behave=3F=
+ No clue yet =F0=9F=98=AC.=0D=0A>=20=0D=0A> I tried not to sound too hand=
+ wavy, and I've only prototyped a small=0D=0A> part of this in the past, =
+so there are probably many more problems with=0D=0A> this approach. I don=
+'t claim this is a sound solution yet, but it does=0D=0A> handle some of =
+the problems that Mentat faced in a different way. I'd be=0D=0A> interest=
+ed to see you poking holes on it.=0D=0A=0D=0AIt's a really difficult prob=
+lem! My suspicion is that querying history is either broadly a union ("th=
+ese pages all had this title at some point") or very much an expert featu=
+re, analogous to browsing your own 'undo' tree. But note that the union a=
+pproach can benefit from identifier rewriting to represent derived equali=
+ty at a particular point in time, even in the presence of conflicts.=0D=0A=
+=0D=0A=0D=0A> When combined with strict schema growth, new data always co=
+mes after=0D=0A> it's schema declaration, so the application doesn't have=
+ to coordinate=0D=0A> around distributed schema evolution.=0D=0A=0D=0AYes=
+, that's broadly the position I took with Mentat: by definition, data on =
+each timeline comes after its schema declaration, so we have an ordered m=
+echanism for merging and also for migrating data as the schema evolves.=0D=
+=0A=0D=0A=0D=0A> TIL: I didn't know you worked on AllegroGraph.=0D=0A=0D=0A=
+Seems like every few years I end up back on data modeling and graph stora=
+ge :D=0D=0A=0D=0A=0D=0A> If the database was versatile enough, an ideal w=
+orld the programmer could write:=0D=0A>=20=0D=0A> ;; schema definition=0D=
+=0A> [{:db/ident :profile/name=0D=0A> :db/valueType :db.type/string=0D=0A=
+> :db/cardinality :db.cardinality/one=0D=0A> :db/conflict-strategy :db.=
+conflict-strategy/none}=0D=0A=0D=0AYes, that's pretty much where we were =
+headed with Mentat. My key observations were that (a) you need really goo=
+d tree-zipping, and frequent syncs, to minimize conflicts; (b) tree-zippi=
+ng and merges practically require that internal identifiers be rewritten =
+when identity is determined; (c) it is undesirable to stop the world, and=
+ so when a conflict is detected you end up with continuing divergence whi=
+ch will be even harder to merge later; (d) because this is a single graph=
+, a conflict _must_ be resolved in some way before you can merge two head=
+s; (e) lossy merges (squash then rebase or squash then merge) can help.=0D=
+=0A=0D=0A
+
+From eu@euandre.org Sat Sep 5 22:51:10 2020
+Received: from box.euandre.org (box.euandre.org [46.101.160.115])
+ by mail-b.sr.ht (Postfix) with ESMTPS id D9930FF12D
+ for <~euandreh/public-inbox@lists.sr.ht>; Sat, 5 Sep 2020 22:51:09 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (2048-bit key) header.d=euandre.org header.i=@euandre.org header.b=w2kZ+s6p
+Received: from authenticated-user (box.euandre.org [46.101.160.115])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
+ (No client certificate requested)
+ by box.euandre.org (Postfix) with ESMTPSA id 2130DFC822;
+ Sat, 5 Sep 2020 19:51:06 -0300 (-03)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=euandre.org; s=mail;
+ t=1599346268; bh=PWBfaRn3jOle7Z3Lc1INXcX50+v5RZ5HNwANJeQm8Js=;
+ h=From:To:Cc:Cc:Subject:In-Reply-To:References:Date:From;
+ b=w2kZ+s6pUTUZJSwLwi3NmcFMqy3a8mBj7PuNX5oJD9O3R4MZM4Td6AZVsjHQQLRVG
+ IRQHyT9t8jngQXu0mkRtU2F4IZoKEPMQAVrRBOBq+f4+J1OR5zynUcT7UKE+hmWDtr
+ lJqDHIbUEzNoS8yLpWXZqib6kwHFqF8B/TLtbl4scllrpf+Q0eq7ZbDpjYHfotNt7C
+ xZ8bFSDeeSP6Ec93nh82bOpXqAEhaTA/vf7qh7T1qWKdjmFvDV3WldUtxjmMcCDvgS
+ PYZ5Laxlrvoq8li4XF55rArXMOb8gftJMy3QY0rPRmyvpFLKFFB423i+NnIctnu93o
+ ewcl54UnP6Iaw==
+From: EuAndreh <eu@euandre.org>
+To: Richard Newman <rnewman@twinql.com>
+Cc: "~euandreh/public-inbox@lists.sr.ht" <~euandreh/public-inbox@lists.sr.ht>
+Cc:
+Subject: Re: The database I wish I had
+In-Reply-To: <010101745fdecd5f-52ab4d88-26f1-4a6d-879d-3234c1956260-000000@us-west-2.amazonses.com>
+References: <7454DA45-C5A4-4A54-8D02-1F3BD37726E9@twinql.com>
+ <010101744a592b75-1dce9281-f0b8-4226-9d50-fd2c7901fa72-000000@us-west-2.amazonses.com>
+ <87sgc1xu7k.fsf@euandre.org>
+ <9652AAE0-10DF-4D13-ABE8-B30667BE27DD@twinql.com>
+ <010101744b260d62-1bb508ab-647f-4877-bdf9-f988667d56e1-000000@us-west-2.amazonses.com>
+ <878sdsy6sc.fsf@euandre.org>
+ <7027FD8B-1B30-435F-A840-7DC61A722E0B@twinql.com>
+ <010101744f99448f-fa8dd88e-0daf-41f8-b3ca-6ea551c300e4-000000@us-west-2.amazonses.com>
+ <87h7se5t1w.fsf@euandre.org>
+ <B032A2E0-0679-4FF6-9A80-06450A8F36B1@twinql.com>
+ <010101745fdecd5f-52ab4d88-26f1-4a6d-879d-3234c1956260-000000@us-west-2.amazonses.com>
+Date: Sat, 05 Sep 2020 19:50:56 -0300
+Message-ID: <87r1rfsur3.fsf@euandre.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: quoted-printable
+
+I see what to mean by being accessible, and it does make sense.
+
+If done properly this might be the reality, where nobody chooses to
+handle conflicts manually because they'd rather have a consistent view
+by picking one of the possible algorithms that are available for them to
+pick for each attribute. I'll experiment with this a bit, and if=20
+feasible, the "perpetual inconsistency" might be an internal view, and
+something available for those who choose to customize their conflict
+resolution strategy. This would be ideal, and perfectly aligned with
+what you said on "the application educating the storage layer about how
+to handle most of the conflicts it encouters". The unique URL you showed
+is a tempting challenge to reach, and the domain identity being
+teachable sounds indeed really powerful.
+
+This also means that with those defaults the desired 1:1 lookups would
+be restored. I don't think that doing manual resolution for each
+attribute is desired, and I wouldn't impose this on the programmer. In
+fact, I'd want to use it myself most of the time.=20
+
+That would also mean an intersection between the two conflicting forces
+you mentioned, where both sides would get what they wanted: a developer
+could use it to quickly prototype something by picking the suggested
+defaults and not finding many surprises, while allowing another
+developer to fine tune their strategy when desired.
+
+It's a database, with a robust storage solution inside it, but primarily
+a database.
+
+If not possible, I tend to err on the side of less ease. Not because I
+want to be a purist, but because I want to do this for myself =F0=9F=98=AC.=
+ The
+title of the article was actually honest on this, and I've actually had
+this exact though a few times: "I'm struggling with this database, but
+there must be more people like me with the same struggles. I bet someone
+has already solved, or will solve soon enough". I'm the target audience,
+and if I do something that solves my problem, than I would consider it a
+success :). It may not be widespread but I'd feel better knowing that
+I've built something that works over something that works most of the
+time. Again, I don't want to be a purist, I just want to create a
+database that I'd want for myself, and whoever sees themselves on the
+same position could benefit. If ease can be added on top of it with some
+choices made for you, great! From your comment, I think you'd agree with
+that last part.=20=20=20=20=20
+
+I'm not trying to convert anyone on this, though. I'd be happy to share
+the code and learning, and welcome those who are interested and
+attracted to this, but broad adoption and world domination are not
+goals.
+
+The applications that I tried to write using the existing solutions were
+just good enough to get me started, but I always felt abandoned when I
+wanted to evolve it somehow, or add some specific functionality without
+losing data, or some similar trade-off. Most of those times I'd rather
+deal with that complexity than choose an easier path.
+
+Today whenever I see a client-side database that syncs, I go to the
+documentation and search for "conflict". I'll either get a page with
+some in-depth explanation, or there will be a list of choices they made
+for me with no way out of it, like last write win, and permanent
+deletion tombstones. Sure those are valuable, but I'd rather be able to
+choose them myself. I already know by now that I will need some tweaked
+conflict resolution someday, and they won't be able to provide it for
+me without an ad-hoc approach.=20
+
+If someone would value something more familiar and more
+fragile/opinionated over something very different and more sound, then
+maybe they're correct to pick SQLite or some JSON files, and having data
+loss in some edge cases would be just fine in the problem they're
+solving. I'd encourage them to stick to SQLite, even, its a great piece
+of software.=20
+
+Also, there's something more profound on this, I believe:=20
+
+Having a solid foundation could potentially help people reason about the
+problem with good tools, instead of looking like a solid foundation that
+will crack if you step on the wrong place. Instead of helping, it could
+be hurting the application, and the superficial ease that many solutions
+offer sometimes feel more like a trap for me to figure out myself later.
+To me it's like someone saying: "have this relational fully ACID
+performant database!", and only later I realize that it isn't actually
+ACID by default, and if all change all the knobs to make it ACID, it
+becomes unusuably slow (or maybe even those knobs aren't there at all).=20
+
+I'd argue that such approach may look weirder and more foreign to any
+developer, but it would actually be "easier". This reminds me of
+"Simple Made Easy" [0]
+
+[0]: https://www.infoq.com/presentations/Simple-Made-Easy/
+
+Back on schema growth: one doesn't have to make a perfect plan of the
+data models so no mental tax gets to them later.
+
+The solution you mentioned sounds just fine: write a function that
+transforms the old data and feed it to the new attribute, and retract
+everything else. From that point on, you've effectively migrated the
+data, and you'll only need to consider the historical schema when doing
+historical queries. Nothing broke on the process, and people might just
+forget about how it used to be in the past. No mental tax, unless you
+want to dig history.
+
+The existence of the new attribute serves the same purpose that the
+version tag does.
+
+You still have an immutable log, and you've evolved your schema to
+better represent the domain. I don't see how those conflict here. The
+biggest objection would probably be the "wasted storage" of the old
+data.=20
+
+But changing schema types and versioning them is something else. It
+might even be possible to implement, doing rollups of the schema just
+like what's done with the rest of the data like you mentioned, but I
+just don't like this idea overall, I think it has brittleness written
+all over it. Maybe I'm just being grumpy.=20=20=20
+
+About your album ontology: I like it! Coincidence or not, I did 5 years
+of music major before dropping out of college, so I'd consider myself a
+"music nerd"! But I agree that one needs the database to help them model
+the domain in a useful way, and I wouldn't force everybody to have a
+particular view of it. Ultimately the database should be a useful tool
+to solve a problem.=20
+
+From andi@notmuch.email Tue Oct 6 07:53:02 2020
+Received: from mx.h4ck.space (mx.h4ck.space [159.69.146.50])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 57F5AFF0FF
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 6 Oct 2020 07:53:02 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (1024-bit key) header.d=notmuch.email header.i=@notmuch.email header.b=iFvGMOSq
+Date: Tue, 6 Oct 2020 09:52:58 +0200
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=notmuch.email;
+ s=mail; t=1601970781;
+ bh=RfiC8xpAHKtbuatmn10k3tqQKuSGYPCep+XBVi1qDe0=;
+ h=Date:From:To:Subject;
+ b=iFvGMOSqx3JpTHGUue5rMzrKPzZiiAFpjJBgA/1P8lgnk/7gBNl51mU5M7m42XXS2
+ cGLwhRQAF3y/x9IoyNGAAUy/hRg+u1VGeRPoV+wFWlitZNgH6dWMuos8XNxLv7Z+dr
+ X1MbaF5gSIQaRehQyg7oyDhd9TO2WcKj6jRCia60=
+From: andi@notmuch.email
+To: ~euandreh/public-inbox@lists.sr.ht
+Subject: crate2nix
+Message-ID: <20201006075258.52tz5u5a36fpbfwy@wrt>
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+Content-Disposition: inline
+
+Hi,
+
+Just saw your crate2nix code and it looks very nice and clean!
+
+Just a heads-up, since you might not aware of all the alternatives out
+there, there is a version from Eelco that basically does the same as
+yours but also handles git dependencies. I did blog about that briefly
+last month [1].
+
+I am curious if you were aware of that and/or naersk [2] as both of them
+also come with the promise of no generated files and no *2nix commands
+to execute.
+
+
+Regards,
+
+Andi
+
+
+
+[1]: https://andreas.rammhold.de/posts/nix-rust/#import-cargohttpsgithubcomedolstraimport-cargo
+[2]: https://github.com/nmattia/naersk
+
+From eu@euandre.org Tue Oct 6 09:09:49 2020
+Received: from box.euandre.org (box.euandre.org [46.101.160.115])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 9ED40FF107
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 6 Oct 2020 09:09:48 +0000 (UTC)
+Authentication-Results: mail-b.sr.ht;
+ dkim=pass (2048-bit key) header.d=euandre.org header.i=@euandre.org header.b=sXxvO0HC
+Received: from authenticated-user (box.euandre.org [46.101.160.115])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
+ (No client certificate requested)
+ by box.euandre.org (Postfix) with ESMTPSA id DF0A2FC991;
+ Tue, 6 Oct 2020 06:09:45 -0300 (-03)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=euandre.org; s=mail;
+ t=1601975387; bh=R37p/WxSUhw6s3+NsQXWU1P6/NnfwRrO7khrmNnifPs=;
+ h=From:To:Cc:Subject:In-Reply-To:References:Date:From;
+ b=sXxvO0HCnYXM8ovt5uOk5bZ/3ZTzOR9HVWXgURy/MicF6fwxTJJfOMCC4+b/ZanpX
+ hcQc+ZSsiOgevD6oR8MZE630bHDpXNs/FskZFM0gRAwG68dfJ/lpj2ogo5GQ9fEbKy
+ oH0MD7Yd2suVGevV9oULcadlt5g8Y75Yuazzwx0HOoF12A4BQWtRrBsBV/Qp7yNoZm
+ xjNIf9MwbD2MsEWlF/K9JeDEdsHazcpQh2RiVd1/G497B9F4yEbE5HmkOP5aGP5jUv
+ yudSn0cBCWQ7S/FXeHXUyhZeebiQnFWkpJeIfUSEhGSOoim1GE/gMi1U7rA1qsWaoy
+ jVVWomlh//r5w==
+From: EuAndreh <eu@euandre.org>
+To: andi@notmuch.email, ~euandreh/public-inbox@lists.sr.ht
+Cc:
+Subject: Re: crate2nix
+In-Reply-To: <20201006075258.52tz5u5a36fpbfwy@wrt>
+References: <20201006075258.52tz5u5a36fpbfwy@wrt>
+Date: Tue, 06 Oct 2020 06:09:27 -0300
+Message-ID: <87r1qbafg8.fsf@euandre.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: quoted-printable
+
+andi@notmuch.email writes:
+
+> Hi,
+
+Hi there!
+
+> Just saw your crate2nix code and it looks very nice and clean!
+
+=E2=98=BA=EF=B8=8F
+
+> Just a heads-up, since you might not aware of all the alternatives out
+> there, there is a version from Eelco that basically does the same as
+> yours but also handles git dependencies. I did blog about that briefly
+> last month [1].
+
+I wasn't aware of it. At a first glance, it looks remarkably similar!
+Maybe I'm on to something =F0=9F=98=AC. I'll try it later, thanks for the l=
+inks.
+
+> I am curious if you were aware of that and/or naersk [2] as both of them
+> also come with the promise of no generated files and no *2nix commands
+> to execute.
+
+I did try naersk. It was the best one I found, in fact. It does use
+Cargo under the hood to drive things, but it doesn't expose the
+environment it builds. With it you can parameterize the Cargo commands,
+but not run it by yourself.
+
+In fact, there's a snippet that I copied directly from naersk[0]. I
+didn't mention it on the README, only on the commit log.
+
+[0]: https://git.euandreh.xyz/cargo2nix/commit/?id=3Dc75e7e2d3ee7f7e074ea9f=
+8e5b959d6395338acc
+
+From m@kluv.in Tue Dec 22 16:29:11 2020
+Authentication-Results: mail-b.sr.ht; dkim=none
+Received: from se15-1.privateemail.com (se15-1.privateemail.com [198.54.127.72])
+ by mail-b.sr.ht (Postfix) with ESMTPS id B579CFF0D7
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 22 Dec 2020 16:29:11 +0000 (UTC)
+Received: from new-01.privateemail.com ([68.65.122.22])
+ by se15.registrar-servers.com with esmtpsa (TLSv1.2:AES128-GCM-SHA256:128)
+ (Exim 4.92)
+ (envelope-from <m@kluv.in>)
+ id 1krkX5-0004vZ-Jo
+ for ~euandreh/public-inbox@lists.sr.ht; Tue, 22 Dec 2020 08:29:10 -0800
+Received: from MTA-08-1.privateemail.com (unknown [10.20.147.18])
+ (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
+ (No client certificate requested)
+ by NEW-01.privateemail.com (Postfix) with ESMTPS id B5E4C60041
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 22 Dec 2020 16:29:05 +0000 (UTC)
+Received: from MTA-08.privateemail.com (localhost [127.0.0.1])
+ by MTA-08.privateemail.com (Postfix) with ESMTP id 91A866006F
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 22 Dec 2020 11:29:05 -0500 (EST)
+Received: from DESKTOP1997JCK (unknown [10.20.151.208])
+ by MTA-08.privateemail.com (Postfix) with ESMTPA id F339F6004A
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 22 Dec 2020 16:29:04 +0000 (UTC)
+From: <m@kluv.in>
+To: <~euandreh/public-inbox@lists.sr.ht>
+Subject: Progress on 'The database I wish I had'
+Date: Tue, 22 Dec 2020 17:29:03 +0100
+Message-ID: <000001d6d87f$90db0f90$b2912eb0$@kluv.in>
+MIME-Version: 1.0
+Content-Type: text/plain;
+ charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+X-Mailer: Microsoft Outlook 16.0
+Thread-Index: AdbYfzkizJpIDb+oQYeLozSx36pHnw==
+Content-Language: no
+X-Virus-Scanned: ClamAV using ClamSMTP
+X-Originating-IP: 68.65.122.22
+X-SpamExperts-Domain: o1.privateemail.com
+X-SpamExperts-Username: out-01
+Authentication-Results: registrar-servers.com; auth=pass (plain) smtp.auth=out-01@o1.privateemail.com
+X-SpamExperts-Outgoing-Class: unsure
+X-SpamExperts-Outgoing-Evidence: Combined (0.57)
+X-Recommended-Action: accept
+X-Filter-ID: Pt3MvcO5N4iKaDQ5O6lkdGlMVN6RH8bjRMzItlySaT8/DiuTODf6wrobfALkT2HqPUtbdvnXkggZ
+ 3YnVId/Y5jcf0yeVQAvfjHznO7+bT5wWykU6VVuoVZYG6cNLn3c4E9P7Wdo8qPSPEXiwPI9MYyaH
+ o/9wB5YLuaEkARV33qkqPXaGse7C78HuChWU138c4pWx7WxWFZpsjT/483ewgsz4HwYGTNBY7kFu
+ mOeXVIneVHayp+oaTkTmqro7//B+gpZK9MmGPsPG1IFhoAOccOwmVIoqJOmt2f1p/FeBBYf0It0W
+ sNcsjnTm+4g485SS8OTf6I2qJgqFlok7aJfKCMNBaM6YjzsuHtjxewJtlZX1S4orbC27wIbqb1J8
+ wyDQ43DG7JDHGkUFlLvaUAJ9EsZg/9mPb0OnPglkYW72F/Odw+XGlIW1bb6iLQaqIs5BLfTttFI5
+ MCNL/izpcNORuAUvossjam0/HVDFzCeLVAjI+ht+2XwDC3Hj+WjRz7duYp7tG8ix8mThYIoihNfI
+ Pg3O+76Hxe6qkGjOqN7KXlq8vamDuW8qjcPFOC9xtigG+MEYMo0NDeyXVO3tNSgVgckOnz0mswVX
+ ZkpFVe5ZgGA0MLjqgL337Aba0izaNK+kAlchHs+Xxee6P8LI+rvlGOeqKR5Od1/Dbcw8eAyTq1JN
+ 7i5WVjtE146jFUwt8Q10AQGCef4uNE610hEkYogGmsvnLjiRqfPZZiHIniXn+ZdiHE9X9GlwwRvN
+ 2KIObvd055RG8/vjrz5/4Qa1aRJkvZZIZN1+KRx7WOWIKK5eEM2Qqv1jJWtJXZNrnBE/zutvXj2E
+ w+ZVKNlbNM488ODolw/r6z7OkCVgacEoYdDR+UFRXxKF5tPxTxfD0dMN+t5ZjqKOEsXEdN1Fhcn9
+ wF7BNjheMPxghFsjIwINNqkbXC/wv1jB3a9oGPa4BHVKDIPK97/T4LRRVYxF+VXiiOfHJN40eTXl
+ WiUAYdLmsJdAoPKCpWwKtkkGG+bEnfOEkWTNI3SjTCvjMfNBc9ze9o81pXKSQ+GI7QB7PH97h6/L
+ 6Wa2uSQw0nxsyGH4ASfmmhbNFFwa8AyQYqjO7qYtiXb+9Q==
+X-Report-Abuse-To: spam@se16.registrar-servers.com
+
+Hi EuAndreh.
+
+I am curious if you found any existing solution for the proposed database? I
+have been looking at irmin (which was mentioned in all forums) a lot lately,
+largely as substitute to libgit2, but recently also as a database.
+
+It looks like the conversation have tapered off, or moved elsewhere? In any
+case it was interesting stumbling across your post, seeing as I have been
+having very similar ideas.
+
+Regards,
+Martin
+
+
+
+From eu@euandre.org Tue Dec 22 21:37:15 2020
+Authentication-Results: mail-b.sr.ht; dkim=fail header.d=euandre.org header.i=@euandre.org
+Received: from box.euandre.org (box.euandre.org [46.101.160.115])
+ by mail-b.sr.ht (Postfix) with ESMTPS id 40985FF0DB
+ for <~euandreh/public-inbox@lists.sr.ht>; Tue, 22 Dec 2020 21:37:15 +0000 (UTC)
+Received: from authenticated-user (box.euandre.org [46.101.160.115])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
+ (No client certificate requested)
+ by box.euandre.org (Postfix) with ESMTPSA id 4B3B9FC4BF;
+ Tue, 22 Dec 2020 18:37:11 -0300 (-03)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=euandre.org; s=mail;
+ t=1608673033; bh=9xQtiA8BJBLUgJEdjCq1SNS1YmrPaYDMrBvQ2oYoGpY=;
+ h=From:To:Cc:Subject:In-Reply-To:References:Date:From;
+ b=P++6DaNXwhc5G/perUnlDlWN9zHzoyzmASEDPcZ0nqbNUpKJy9sV5OmC7etQTyPMO
+ bAAwkUw3Wl/tn9CaLxzYJo+IClWYQEVJDmAMwlcCIJa3Ol+fSQsceBxK1a5za5EA8W
+ SzBrXwvpbYG9ISVwBUY+fNkqwAoRP2lQ8Sy3/pvrA0Kq894eXg08tbvlTJh3H6rh3Z
+ mla6VfP5qftYM6oWWhGSs6EJkGtmMDA+1UkAn+ASnSROejuxcPm1Cp2qmT2VoEPapP
+ kmHV3bjCM9v4nN/1bPy1rBvhwhx1O9UfNWvUYGWnhb24Tu0zK68FA20ZXlQc1bRnGA
+ yBrW5x0bzEZxg==
+From: EuAndreh <eu@euandre.org>
+To: m@kluv.in, ~euandreh/public-inbox@lists.sr.ht
+Cc:
+Subject: Re: Progress on 'The database I wish I had'
+In-Reply-To: <000001d6d87f$90db0f90$b2912eb0$@kluv.in>
+References: <000001d6d87f$90db0f90$b2912eb0$@kluv.in>
+Date: Tue, 22 Dec 2020 18:36:56 -0300
+Message-ID: <87h7oda5fr.fsf@euandre.org>
+MIME-Version: 1.0
+Content-Type: text/plain
+
+<m@kluv.in> writes:
+
+> Hi EuAndreh.
+
+Hi there!
+
+> I am curious if you found any existing solution for the proposed database? I
+> have been looking at irmin (which was mentioned in all forums) a lot lately,
+> largely as substitute to libgit2, but recently also as a database.
+
+I have written it down as a place to steal ideas from, which I plan to
+do in the future as I mature more on the field, but irmin isn't a
+solution, being written in OCaml.
+
+> It looks like the conversation have tapered off, or moved elsewhere? In any
+> case it was interesting stumbling across your post, seeing as I have been
+> having very similar ideas.
+
+I'm working on it, slowly but surely.
+
+The previous "comment" link used to point to my email address first, and
+the this public-inbox second. Because of that I had many conversations
+done in private messages, where I imagine that people just got the first
+link. I responded those in private, but many had similar enough
+comments, and I wrote an article on that [0]. FYI: I recently added a
+"articles by category" Atom feed, if you prefer a filtered list [1] of
+articles.
+
+On the ParsecC project mentioned on the article:
+I'm already much more comfortable with creating a C API, but I got to a
+point that I needed a proper error handling solution. I stumbled upon an
+existing implementation of a parser combinator in C [2].
+
+The point I'm at right now is to read and understand that source code.
+In fact, it is 1 of 4 open tabs on my browser right now, before writing
+this message.
+
+If I find its error handling good enough, I'll replicate it in ParsecC,
+and then in libedn.
+
+I've also started reading a good book on the subject [3]. I can't speak
+yet to all of it, but I like what I've seen so far.
+
+I happy to discuss more about it, or answer questions you may have. And
+also, I'm happy to accept contributions!
+
+[0]: https://euandre.org/2020/11/12/durable-persistent-trees-and-parser-combinators-building-a-database.html
+[1]: https://euandre.org/articles-by-category.html#mediator
+[2]: https://github.com/wbhart/Cesium3/tree/combinators
+[3]: https://www.immutablearchitecture.com/
+
+
diff --git a/src/content/de/blog/index.adoc b/src/content/de/blog/index.adoc
new file mode 100644
index 0000000..ec9c676
--- /dev/null
+++ b/src/content/de/blog/index.adoc
@@ -0,0 +1,5 @@
+= Blog
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/de/blog/kategorien.adoc b/src/content/de/blog/kategorien.adoc
new file mode 100644
index 0000000..55c86a1
--- /dev/null
+++ b/src/content/de/blog/kategorien.adoc
@@ -0,0 +1,6 @@
+= Artikel nach Kategorie
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/de/folien/index.adoc b/src/content/de/folien/index.adoc
new file mode 100644
index 0000000..62433a7
--- /dev/null
+++ b/src/content/de/folien/index.adoc
@@ -0,0 +1,7 @@
+= Folien
+
+Foliensätze als PDF gesetzt: kein Browser nötig, um sie zu lesen, und kein JavaScript-Präsentationsframework im Schlepptau.
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/de/folien/kategorien.adoc b/src/content/de/folien/kategorien.adoc
new file mode 100644
index 0000000..55c86a1
--- /dev/null
+++ b/src/content/de/folien/kategorien.adoc
@@ -0,0 +1,6 @@
+= Artikel nach Kategorie
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/de/hhg/index.adoc b/src/content/de/hhg/index.adoc
new file mode 100644
index 0000000..8ecde39
--- /dev/null
+++ b/src/content/de/hhg/index.adoc
@@ -0,0 +1,11 @@
+= Heute Habe ich Gelernt
+
+:anna-e-so: https://til.flourishing.stream/
+
+**H**eute **H**abe ich **G**elernt (aus dem Englischen, _**T**oday **I** **L**earned_): kleine Einträge nützlichen Wissens.
+
+Schamlose Kopie von {anna-e-so}[Anna e só].
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/de/hhg/kategorien.adoc b/src/content/de/hhg/kategorien.adoc
new file mode 100644
index 0000000..55c86a1
--- /dev/null
+++ b/src/content/de/hhg/kategorien.adoc
@@ -0,0 +1,6 @@
+= Artikel nach Kategorie
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/de/index.adoc b/src/content/de/index.adoc
new file mode 100644
index 0000000..c586951
--- /dev/null
+++ b/src/content/de/index.adoc
@@ -0,0 +1,7 @@
+= Startseite
+
+Das Neueste aus jeder Sammlung steht unten.
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/de/musik.adoc b/src/content/de/musik.adoc
new file mode 100644
index 0000000..f2881a3
--- /dev/null
+++ b/src/content/de/musik.adoc
@@ -0,0 +1,36 @@
+= Musik
+:dengoso: ../music/dengoso
+:marcha: ../music/marcha-dos-marinheiros
+:choro: ../music/choro-da-saudade
+
+Von mir gestochene Notenblätter, jeweils mit einer synthetisierten Wiedergabe. Die LilyPond-Quellen liegen im Repository; die PDFs werden daraus gebaut.
+
+== Notenblätter
+
+João Pernambuco, link:{dengoso}.pdf[Dengoso]
+
+++++
+<audio controls>
+ <source src="../music/dengoso.ogg" type="audio/ogg" />
+</audio>
+++++
+
+Canhoto, link:{marcha}.pdf[Marcha dos Marinheiros]
+
+++++
+<audio controls>
+ <source src="../music/marcha-dos-marinheiros.ogg" type="audio/ogg" />
+</audio>
+++++
+
+Agustín Barrios, link:{choro}.pdf[Choro da Saudade]
+
+++++
+<audio controls>
+ <source src="../music/choro-da-saudade.ogg" type="audio/ogg" />
+</audio>
+++++
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/de/pastebin/index.adoc b/src/content/de/pastebin/index.adoc
new file mode 100644
index 0000000..59cb93e
--- /dev/null
+++ b/src/content/de/pastebin/index.adoc
@@ -0,0 +1,5 @@
+= Pastebins
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/de/pastebin/kategorien.adoc b/src/content/de/pastebin/kategorien.adoc
new file mode 100644
index 0000000..55c86a1
--- /dev/null
+++ b/src/content/de/pastebin/kategorien.adoc
@@ -0,0 +1,6 @@
+= Artikel nach Kategorie
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/de/podcast/index.adoc b/src/content/de/podcast/index.adoc
new file mode 100644
index 0000000..6e84602
--- /dev/null
+++ b/src/content/de/podcast/index.adoc
@@ -0,0 +1,5 @@
+= Podcast
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/de/podcast/kategorien.adoc b/src/content/de/podcast/kategorien.adoc
new file mode 100644
index 0000000..55c86a1
--- /dev/null
+++ b/src/content/de/podcast/kategorien.adoc
@@ -0,0 +1,6 @@
+= Artikel nach Kategorie
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/de/screencast/index.adoc b/src/content/de/screencast/index.adoc
new file mode 100644
index 0000000..930c244
--- /dev/null
+++ b/src/content/de/screencast/index.adoc
@@ -0,0 +1,5 @@
+= Screencasts
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/de/screencast/kategorien.adoc b/src/content/de/screencast/kategorien.adoc
new file mode 100644
index 0000000..55c86a1
--- /dev/null
+++ b/src/content/de/screencast/kategorien.adoc
@@ -0,0 +1,6 @@
+= Artikel nach Kategorie
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/de/ueber.adoc b/src/content/de/ueber.adoc
new file mode 100644
index 0000000..1c08270
--- /dev/null
+++ b/src/content/de/ueber.adoc
@@ -0,0 +1,26 @@
+= Über
+
+:mailto: mailto:~euandreh/public-inbox@lists.sr.ht
+:archive: https://lists.sr.ht/~euandreh/public-inbox
+:cargo2nix: ../en/blog/2020/10/05/cargo2nix.html
+:swift2nix: ../en/blog/2020/10/05/swift2nix.html
+:database: ../en/blog/2020/08/31/database-i-wish-i-had.html
+:fallible: ../en/blog/2021/02/17/fallible.html
+:flags: ../en/blog/2020/10/19/feature-flags.html
+:interviewing: ../en/blog/2020/10/20/wrong-interviewing.html
+:stateless: ../en/blog/2019/06/02/nixos-stateless-workstation.html
+:bugtracker: ../en/blog/2020/11/07/diy-bugs.html
+
+Hallo, ich bin EuAndreh -- André Miranda Moreira. Ich schreibe Software und gelegentlich Musik. Meine Kontaktdaten stehen im Fußbereich dieser Seite, oder schreib an meinen {mailto}[öffentlichen Posteingang] ({archive}[Archiv]).
+
+Dies ist meine persönliche Website, auf der ich Artikel schreibe, Software veröffentliche und verwandte Arbeiten zeige.
+
+I write about robust software: the kind that still works after the network drops, after the vendor leaves, and after you stop paying attention.
+
+That shows up in a few places. I care about reproducible systems, so I run a link:{stateless}[stateless NixOS workstation] and have written bridges like link:{cargo2nix}[cargo2nix] and link:{swift2nix}[swift2nix] to make Rust and Swift first-class citizens inside Nix builds. I care about durable data, which is why I keep returning to local-first software, persistent data structures, and link:{database}[the database I wish existed]: embedded, immutable, and syncable. And I care about tools that don't need a company behind them, so I reach for Git, email and plain text before I reach for a service -- far enough that link:{bugtracker}[my bug tracker is a directory].
+
+I also think about how teams ship -- fault injection (link:{fallible}[fallible]), link:{flags}[feature flags], and link:{interviewing}[how we interview engineers] -- because robustness is a team habit before it is a technology.
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/en/about.adoc b/src/content/en/about.adoc
index 14d5e22..5e8b699 100644
--- a/src/content/en/about.adoc
+++ b/src/content/en/about.adoc
@@ -2,10 +2,36 @@
:mailto: mailto:~euandreh/public-inbox@lists.sr.ht
:archive: https://lists.sr.ht/~euandreh/public-inbox
+:cargo2nix: ../en/blog/2020/10/05/cargo2nix.html
+:swift2nix: ../en/blog/2020/10/05/swift2nix.html
+:database: ../en/blog/2020/08/31/database-i-wish-i-had.html
+:fallible: ../en/blog/2021/02/17/fallible.html
+:flags: ../en/blog/2020/10/19/feature-flags.html
+:interviewing: ../en/blog/2020/10/20/wrong-interviewing.html
+:stateless: ../en/blog/2019/06/02/nixos-stateless-workstation.html
+:bugtracker: ../en/blog/2020/11/07/diy-bugs.html
-Hi, I'm EuAndreh. I write software and, occasionally, music. You can find my
+Hi, I'm EuAndreh -- André Miranda Moreira. I write software and, occasionally, music. You can find my
contact information in the footer of this page, or mail my {mailto}[public
inbox] ({archive}[archive]).
This is my personal website where I write articles, publish software and more
related work.
+
+I write about robust software: the kind that still works after the network
+drops, after the vendor leaves, and after you stop paying attention.
+
+That shows up in a few places. I care about reproducible systems, so I run a
+link:{stateless}[stateless NixOS workstation] and have written bridges like
+link:{cargo2nix}[cargo2nix] and link:{swift2nix}[swift2nix] to make Rust and
+Swift first-class citizens inside Nix builds. I care about durable data, which
+is why I keep returning to local-first software, persistent data structures,
+and link:{database}[the database I wish existed]: embedded, immutable, and
+syncable. And I care about tools that don't need a company behind them, so I
+reach for Git, email and plain text before I reach for a service --
+far enough that link:{bugtracker}[my bug tracker is a directory].
+
+I also think about how teams ship -- fault injection
+(link:{fallible}[fallible]), link:{flags}[feature flags], and
+link:{interviewing}[how we interview engineers] -- because robustness is a
+team habit before it is a technology.
diff --git a/src/content/en/blog/2018/08/01/npm-ci-reproducibility.adoc b/src/content/en/blog/2018/08/01/npm-ci-reproducibility.adoc
index 76bd8e6..7c1e837 100644
--- a/src/content/en/blog/2018/08/01/npm-ci-reproducibility.adoc
+++ b/src/content/en/blog/2018/08/01/npm-ci-reproducibility.adoc
@@ -115,7 +115,8 @@ https://lernajs.io/[Lerna]footnote:lerna-package-lock[
was harder than I expected.
]:
-```bash
+[source,sh]
+----
cd /tmp/
git clone https://github.com/lerna/lerna.git
cd lerna/
@@ -127,7 +128,7 @@ npm ci
merkle-tree node_modules/ # outputs 11e218c4ac32fac8a9607a8da644fe870a25c99821167d21b607af45699afafa
npm ci # test if it also works with an existing node_modules/ folder
merkle-tree node_modules/ # outputs 11e218c4ac32fac8a9607a8da644fe870a25c99821167d21b607af45699afafa
-```
+----
Good job `npm ci` :)
diff --git a/src/content/en/blog/2020/11/14/local-first-review.adoc b/src/content/en/blog/2020/11/14/local-first-review.adoc
index 2036069..4effc43 100644
--- a/src/content/en/blog/2020/11/14/local-first-review.adoc
+++ b/src/content/en/blog/2020/11/14/local-first-review.adoc
@@ -1,4 +1,4 @@
-= Local-First Software: article review
+= Local-First Software: You Own Your Data, in spite of the Cloud - article review
:categories: presentation article-review
:empty:
diff --git a/src/content/en/index.adoc b/src/content/en/index.adoc
index 275f7c4..2510fef 100644
--- a/src/content/en/index.adoc
+++ b/src/content/en/index.adoc
@@ -1 +1,3 @@
-= index
+= Home
+
+The newest of each collection is below.
diff --git a/src/content/en/music.adoc b/src/content/en/music.adoc
new file mode 100644
index 0000000..6828baa
--- /dev/null
+++ b/src/content/en/music.adoc
@@ -0,0 +1,33 @@
+= Music
+:dengoso: ../music/dengoso
+:marcha: ../music/marcha-dos-marinheiros
+:choro: ../music/choro-da-saudade
+
+Scores I have engraved, with a synthesised rendering of each. The
+LilyPond sources are in the repository; the PDFs are built from them.
+
+== Scores
+
+João Pernambuco, link:{dengoso}.pdf[Dengoso]
+
+++++
+<audio controls>
+ <source src="../music/dengoso.ogg" type="audio/ogg" />
+</audio>
+++++
+
+Canhoto, link:{marcha}.pdf[Marcha dos Marinheiros]
+
+++++
+<audio controls>
+ <source src="../music/marcha-dos-marinheiros.ogg" type="audio/ogg" />
+</audio>
+++++
+
+Agustín Barrios, link:{choro}.pdf[Choro da Saudade]
+
+++++
+<audio controls>
+ <source src="../music/choro-da-saudade.ogg" type="audio/ogg" />
+</audio>
+++++
diff --git a/src/content/en/pastebin/2021/06/22/curl-wget.adoc b/src/content/en/pastebin/2021/06/22/curl-wget.adoc
index 97f55c7..02a9744 100644
--- a/src/content/en/pastebin/2021/06/22/curl-wget.adoc
+++ b/src/content/en/pastebin/2021/06/22/curl-wget.adoc
@@ -1,4 +1,4 @@
-= "cloc: curl and wget"
+= cloc: curl and wget
`curl`:
diff --git a/src/content/en/screencast/2021/02/07/autoqemu.adoc b/src/content/en/screencast/2021/02/07/autoqemu.adoc
index b20b092..15bac9a 100644
--- a/src/content/en/screencast/2021/02/07/autoqemu.adoc
+++ b/src/content/en/screencast/2021/02/07/autoqemu.adoc
@@ -10,7 +10,6 @@
:script: https://www.man7.org/linux/man-pages/man1/script.1.html
:klaatu: https://gnuworldorder.info/
-video: true
After reading begriffs "{begriffs-tips}[Tips for stable and portable software]",
the "Begriffs Buildfarm?" section caught my attention, as this is something I
diff --git a/src/content/en/slide/2020/10/19/feature-flags.adoc b/src/content/en/slide/2020/10/19/feature-flags.adoc
index 553cf4c..360c00f 100644
--- a/src/content/en/slide/2020/10/19/feature-flags.adoc
+++ b/src/content/en/slide/2020/10/19/feature-flags.adoc
@@ -1,230 +1,8 @@
----
-# Rollout, feature flag, experiment, operational toggle
+= Rollout, feature flag, experiment, operational toggle
+:categories: presentation
+:deck: feature-flags.pdf
-@Different use cases for backend, frontend and mobile
+Different use cases for backend, frontend and mobile, and why the
+four things people call "feature flags" are not the same thing.
----
-.
-.
-.
-# "Feature flags" tend to come up when talking about continuous deployment
-
----
-.
-.
-.
-.
-@CI: continuous integration
-.
-@CD: continuous delivery
-.
-@CD: continuous deployment
-
----
-## Types
-.
-.
-.
-1. rollout
-2. feature flag
-3. experiment
-4. operational toggle
-
-% {favicon.svg}
----
-## Rollout
-
-# For *rolling out* a new version of software
-
-Short-lived using percentages
-
-% FIXME: links
-- a new deployment of kubernetes
-- new APK released to the Play Store
-
----
-## Feature flag
-
-# For turning a feature *on* or *off*
-
-Medium-lived using allow list, A/B test, percentage, app version, etc.
-
-- :new-chargeback-flow
-- :new-debit-card-activation-screen
-
----
-## Experiment
-
-# For analysing behaviour
-
-Medium-lived using allow list and A/B test
-
-- :debit-withdrawal-test
-
----
-## Operational toggle
-
-# For disabling features in #crash-like situations
-
-Long-lived using percentage
-
-- :bank-barcode-payment
-- :savings-bank-barcode-query-provider
-
----
-.
-.
-@We know know about the types
-# But they have different relevance for backend, frontend and mobile
-
----
-## backend
-.
-.
-1. rollout: k8s blue/green, canary and ~common-rollout~ common-xp
-2. feature flag: ~common-rollout~ common-xp and datasets
-3. experiment: common-xp
-4. operational toggle: ~common-rollout~ common-xp
-
----
-## frontend
-.
-.
-1. rollout: CDN and page refreshes
-2. feature flag: percentages and maybe IPs (no :customer/id on the website)
-3. experiment: via dynamic backend control
-4. operational toggle: via dynamic backend control
-
----
-## backend
-.
-.
-1. rollout: app stores
-2. feature flag: via dynamic backend control
-3. experiment: via dynamic backend control
-4. operational toggle: via dynamic backend control
-
----
-.
-.
-@Key differentiator is
-# how much *control* we have over the environment
-
----
-## backend
-# full control
-% FIXME: emoji
-% 🎉
-
----
-## frontend
-# partial control
-We choose when to make a new version available
-
----
-## mobile
-# very limited control
-- app stores can restrict updates (worse for iOS)
-- customers still have to download new versions
-
----
-# Costs
-- more complex code
-- compatibility with old app versions
-- nesting is exponential
-
----
-# Benefits
-- dynamicity
-
----
-## weighting costs × benefits
-# The less control we have, the more we value dynamicity
-
----
-## weighting costs × benefits
-.
-.
-.
-- backend: sometimes worth the cost
-- frontend: almost always worth the cost
-- mobile: *always* worth the cost
-
----
-.
-.
-.
-# Best practices
-
----
-# dynamic content > feature flag
-Always true for mobile, almost always for frontend
-
----
-# Use :include-list for named groups
-Always true for backend, frontend and mobile
-
- {:rules
- #{{:types :include-list
- :content {:filename "debit-team-members.txt"}}}}
-
----
-# Always use :app-version
-Only for mobile
-
- {:rules
- #{{:types :app-version
- :content {:min-version #{{:platform :android
- :code 1000000}
- {:platform :ios
- :code 2000000}}}}}}
-
----
-# Extend ~common-rollout~ common-xp if required
-
-That's how :include-list, :app-version, etc. were born
-
----
-# Beware of many nested feature flags
-True for backend, frontend and mobile
-
----
-# Don't delete app-facing feature flags
-True for mobile
-
----
-.
-.
-.
-# Include a feature flag on the whiteboarding phase
-
----
-.
-.
-.
-# Include deleting/retiring the feature flag at the end
-
----
-# Avoid renaming a feature flag
-Use :app-version with :min-version instead
-
----
-.
-.
-.
-# And most importantly...
-
----
-# *Always* rely on a feature flag on the app
-Never do a hotfix, avoid expedited releases at all costs
-
----
-## References
-.
-% FIXME: links
-1. "Feature Toggles (aka Feature Flags)", by Pete Hodgson
-2. "Continuous integration vs. delivery vs. deployment", by Sten Pittet
-3. Accelerate, by N. Forsgren, J. Humble and G. Kim
-4. these slides: euandre.org/slide/
-5. prose version of this presentation
-6. view source
+link:{deck}[Read the deck (PDF)].
diff --git a/src/content/en/slide/2020/10/19/feature-flags.eslaides b/src/content/en/slide/2020/10/19/feature-flags.eslaides
new file mode 100644
index 0000000..553cf4c
--- /dev/null
+++ b/src/content/en/slide/2020/10/19/feature-flags.eslaides
@@ -0,0 +1,230 @@
+---
+# Rollout, feature flag, experiment, operational toggle
+
+@Different use cases for backend, frontend and mobile
+
+---
+.
+.
+.
+# "Feature flags" tend to come up when talking about continuous deployment
+
+---
+.
+.
+.
+.
+@CI: continuous integration
+.
+@CD: continuous delivery
+.
+@CD: continuous deployment
+
+---
+## Types
+.
+.
+.
+1. rollout
+2. feature flag
+3. experiment
+4. operational toggle
+
+% {favicon.svg}
+---
+## Rollout
+
+# For *rolling out* a new version of software
+
+Short-lived using percentages
+
+% FIXME: links
+- a new deployment of kubernetes
+- new APK released to the Play Store
+
+---
+## Feature flag
+
+# For turning a feature *on* or *off*
+
+Medium-lived using allow list, A/B test, percentage, app version, etc.
+
+- :new-chargeback-flow
+- :new-debit-card-activation-screen
+
+---
+## Experiment
+
+# For analysing behaviour
+
+Medium-lived using allow list and A/B test
+
+- :debit-withdrawal-test
+
+---
+## Operational toggle
+
+# For disabling features in #crash-like situations
+
+Long-lived using percentage
+
+- :bank-barcode-payment
+- :savings-bank-barcode-query-provider
+
+---
+.
+.
+@We know know about the types
+# But they have different relevance for backend, frontend and mobile
+
+---
+## backend
+.
+.
+1. rollout: k8s blue/green, canary and ~common-rollout~ common-xp
+2. feature flag: ~common-rollout~ common-xp and datasets
+3. experiment: common-xp
+4. operational toggle: ~common-rollout~ common-xp
+
+---
+## frontend
+.
+.
+1. rollout: CDN and page refreshes
+2. feature flag: percentages and maybe IPs (no :customer/id on the website)
+3. experiment: via dynamic backend control
+4. operational toggle: via dynamic backend control
+
+---
+## backend
+.
+.
+1. rollout: app stores
+2. feature flag: via dynamic backend control
+3. experiment: via dynamic backend control
+4. operational toggle: via dynamic backend control
+
+---
+.
+.
+@Key differentiator is
+# how much *control* we have over the environment
+
+---
+## backend
+# full control
+% FIXME: emoji
+% 🎉
+
+---
+## frontend
+# partial control
+We choose when to make a new version available
+
+---
+## mobile
+# very limited control
+- app stores can restrict updates (worse for iOS)
+- customers still have to download new versions
+
+---
+# Costs
+- more complex code
+- compatibility with old app versions
+- nesting is exponential
+
+---
+# Benefits
+- dynamicity
+
+---
+## weighting costs × benefits
+# The less control we have, the more we value dynamicity
+
+---
+## weighting costs × benefits
+.
+.
+.
+- backend: sometimes worth the cost
+- frontend: almost always worth the cost
+- mobile: *always* worth the cost
+
+---
+.
+.
+.
+# Best practices
+
+---
+# dynamic content > feature flag
+Always true for mobile, almost always for frontend
+
+---
+# Use :include-list for named groups
+Always true for backend, frontend and mobile
+
+ {:rules
+ #{{:types :include-list
+ :content {:filename "debit-team-members.txt"}}}}
+
+---
+# Always use :app-version
+Only for mobile
+
+ {:rules
+ #{{:types :app-version
+ :content {:min-version #{{:platform :android
+ :code 1000000}
+ {:platform :ios
+ :code 2000000}}}}}}
+
+---
+# Extend ~common-rollout~ common-xp if required
+
+That's how :include-list, :app-version, etc. were born
+
+---
+# Beware of many nested feature flags
+True for backend, frontend and mobile
+
+---
+# Don't delete app-facing feature flags
+True for mobile
+
+---
+.
+.
+.
+# Include a feature flag on the whiteboarding phase
+
+---
+.
+.
+.
+# Include deleting/retiring the feature flag at the end
+
+---
+# Avoid renaming a feature flag
+Use :app-version with :min-version instead
+
+---
+.
+.
+.
+# And most importantly...
+
+---
+# *Always* rely on a feature flag on the app
+Never do a hotfix, avoid expedited releases at all costs
+
+---
+## References
+.
+% FIXME: links
+1. "Feature Toggles (aka Feature Flags)", by Pete Hodgson
+2. "Continuous integration vs. delivery vs. deployment", by Sten Pittet
+3. Accelerate, by N. Forsgren, J. Humble and G. Kim
+4. these slides: euandre.org/slide/
+5. prose version of this presentation
+6. view source
diff --git a/src/content/en/slide/2020/11/14/local-first-hype.adoc b/src/content/en/slide/2020/11/14/local-first-hype.adoc
index fd83115..6083dca 100644
--- a/src/content/en/slide/2020/11/14/local-first-hype.adoc
+++ b/src/content/en/slide/2020/11/14/local-first-hype.adoc
@@ -1,204 +1,8 @@
-# On "local-first"
+= On "local-first": beyond the CRDT silver bullet
+:categories: presentation
+:deck: local-first-hype.pdf
-@Beyond the CRDT silver bullet
+What local-first software asks of a system, and why conflict-free
+replicated data types answer only part of it.
----
-## Part 1
-# Exposition
-
----
-## Target
-...
-- documents
-- files
-- personal data repositories
-..
-Not: banking services, e-commerce, social networking, ride-sharing, etc.
-
----
-...
-# 7 ideals for local-first software
-
----
-...
-# 1 - no spinners: your work at your fingertips
-
----
-...
-# 2 - your work is not trapped on one device
-
----
-...
-# 3 - the network is optional
-
----
-...
-# 4 - seamless collaboration with your colleagues
-
----
-...
-# 5 - the long now
-
----
-...
-# 6 - security and privacy by default
-
----
-...
-# 7 - you retain ultimate ownership and control
-
----
-## Towards a better future
-....
-@ CRDTs (Conflict-free Replicated Data Types) as a Foundational Technology
-
----
-## Use case
-.
- # in node A and node B
- s = "Hello, World"
-
- # in node A
- s = "Hello, Alice"
-
- # in node B
- s = "Hello, Bob"
-
-How to reconcile those?
-- Hello, ABloibce
-- Hello, AliceBob
-- Hello, BobAlice
-- Hello, Alice
-\...
-
----
-# Existing CRDTs differ
-- performance
-- storage
-- compression
-- metadata overhead
-
----
-...
-# Hint towards the "automerge" CRDT
-
----
-......
-@@show comparison table, page 9
-
----
-## Part 2
-# Critique
-
----
-## Software license
-...
-@@"In our opinion, maintaining control and ownership of data does not mean that the software must necessarily be open source."
-
----
-## Example 1 - intentional restriction
-.
- #!/bin/sh
- TODAY="$(date +%s)"
- LICENSE_EXPIRATION="$(date -d 2020-10-27 +%s)"
- if [ "$TODAY" -ge "$LICENSE_EXPIRATION" ]; then
- echo 'License expired!'
- exit 1
- fi
- echo $((2 + 2))
-.
- # today
- $ ./useful-adder.sh
- 4
-
- # tomorrow
- $ ./useful-adder.sh
- License expired!
----
-## Example 2 - unintentional restriction
-.
- # today
- $ useful-program
- # ... useful output ...
-
- # tomorrow, with more data
- $ useful-program
- ERROR: Panic! Stack overflow!
-
----
-..
-# local-first *requires* free software
-
-Otherwise "The Long Now" (ideal nº5) is lost
-
----
-## Denial of existing solutions
-..
-@@"In principle it is possible to collaborate without a repository service, e.g. by sending patch files by email, but the majority of Git users rely on GitHub."
-.
-Solution: either GitHub+CRDTs or git send-email
-
----
-## Plain-text formats
-.
-@@"Git is highly optimized for code and similar line-based text file"
-.
-It even pulls software to the plain text directtion, e.g.:
-- delivery-templates
-- common-core.protocols.config
-.
-Why not exploit that more?
-
----
-## Ditching of web applications
-..
-@@"The architecture of web apps remains fundamentally server-centric
-.
-% FIXME: links
-Disagree. Contrast PouchDB with Android Instant Apps
-
----
-## Costs are underrated
-.
-- storage
-- backups
-- maintenance
-.
-Example: blog vs vlog
-
----
-## Real-time collaboration a bit overrated
-.
-It is only possible on the presence of reliable, medium-quality network connection
-..
-@@"X also works when inside an elevator, subway or plane!"
-
----
-## On CRDTs and developer experience
-.
-@@"For an app developer, how does the use of a CRDT-based data layer compare to existing storage layers like a SQL database, a filesystem, or CoreData? Is a distributed system harder to write software for?
-.
-@YES.
-.
-% FIXME: link
-See "A Note on Distributed Computing"
-
----
-## Conclusion
-.
-Why this is a "paper I love": it took offline-first and ran with it.
-.
-But a pinch of CRDT won't make the world local-first.
-.
-The tricky part is the end of the sentence: "in spite of the Cloud".
-
----
-## References
-.
-% FIXME: links
-1. "Local-First Software: You Own Your Data, in spite of the Cloud", by M. Kleppmann, A. Wiggins, P. Van Hardenberg and M. F. McGranaghan
-2. The Morning Paper article
-3. "A Note on Distributed Compiting", by J. Waldo, G. Wyant, A. Wollrath and S. Kendall
-4. these slides: euandre.org/slide/
-5. prose version of this presentation
-6. view source
+link:{deck}[Read the deck (PDF)].
diff --git a/src/content/en/slide/2020/11/14/local-first-hype.eslaides b/src/content/en/slide/2020/11/14/local-first-hype.eslaides
new file mode 100644
index 0000000..fd83115
--- /dev/null
+++ b/src/content/en/slide/2020/11/14/local-first-hype.eslaides
@@ -0,0 +1,204 @@
+# On "local-first"
+
+@Beyond the CRDT silver bullet
+
+---
+## Part 1
+# Exposition
+
+---
+## Target
+...
+- documents
+- files
+- personal data repositories
+..
+Not: banking services, e-commerce, social networking, ride-sharing, etc.
+
+---
+...
+# 7 ideals for local-first software
+
+---
+...
+# 1 - no spinners: your work at your fingertips
+
+---
+...
+# 2 - your work is not trapped on one device
+
+---
+...
+# 3 - the network is optional
+
+---
+...
+# 4 - seamless collaboration with your colleagues
+
+---
+...
+# 5 - the long now
+
+---
+...
+# 6 - security and privacy by default
+
+---
+...
+# 7 - you retain ultimate ownership and control
+
+---
+## Towards a better future
+....
+@ CRDTs (Conflict-free Replicated Data Types) as a Foundational Technology
+
+---
+## Use case
+.
+ # in node A and node B
+ s = "Hello, World"
+
+ # in node A
+ s = "Hello, Alice"
+
+ # in node B
+ s = "Hello, Bob"
+
+How to reconcile those?
+- Hello, ABloibce
+- Hello, AliceBob
+- Hello, BobAlice
+- Hello, Alice
+\...
+
+---
+# Existing CRDTs differ
+- performance
+- storage
+- compression
+- metadata overhead
+
+---
+...
+# Hint towards the "automerge" CRDT
+
+---
+......
+@@show comparison table, page 9
+
+---
+## Part 2
+# Critique
+
+---
+## Software license
+...
+@@"In our opinion, maintaining control and ownership of data does not mean that the software must necessarily be open source."
+
+---
+## Example 1 - intentional restriction
+.
+ #!/bin/sh
+ TODAY="$(date +%s)"
+ LICENSE_EXPIRATION="$(date -d 2020-10-27 +%s)"
+ if [ "$TODAY" -ge "$LICENSE_EXPIRATION" ]; then
+ echo 'License expired!'
+ exit 1
+ fi
+ echo $((2 + 2))
+.
+ # today
+ $ ./useful-adder.sh
+ 4
+
+ # tomorrow
+ $ ./useful-adder.sh
+ License expired!
+---
+## Example 2 - unintentional restriction
+.
+ # today
+ $ useful-program
+ # ... useful output ...
+
+ # tomorrow, with more data
+ $ useful-program
+ ERROR: Panic! Stack overflow!
+
+---
+..
+# local-first *requires* free software
+
+Otherwise "The Long Now" (ideal nº5) is lost
+
+---
+## Denial of existing solutions
+..
+@@"In principle it is possible to collaborate without a repository service, e.g. by sending patch files by email, but the majority of Git users rely on GitHub."
+.
+Solution: either GitHub+CRDTs or git send-email
+
+---
+## Plain-text formats
+.
+@@"Git is highly optimized for code and similar line-based text file"
+.
+It even pulls software to the plain text directtion, e.g.:
+- delivery-templates
+- common-core.protocols.config
+.
+Why not exploit that more?
+
+---
+## Ditching of web applications
+..
+@@"The architecture of web apps remains fundamentally server-centric
+.
+% FIXME: links
+Disagree. Contrast PouchDB with Android Instant Apps
+
+---
+## Costs are underrated
+.
+- storage
+- backups
+- maintenance
+.
+Example: blog vs vlog
+
+---
+## Real-time collaboration a bit overrated
+.
+It is only possible on the presence of reliable, medium-quality network connection
+..
+@@"X also works when inside an elevator, subway or plane!"
+
+---
+## On CRDTs and developer experience
+.
+@@"For an app developer, how does the use of a CRDT-based data layer compare to existing storage layers like a SQL database, a filesystem, or CoreData? Is a distributed system harder to write software for?
+.
+@YES.
+.
+% FIXME: link
+See "A Note on Distributed Computing"
+
+---
+## Conclusion
+.
+Why this is a "paper I love": it took offline-first and ran with it.
+.
+But a pinch of CRDT won't make the world local-first.
+.
+The tricky part is the end of the sentence: "in spite of the Cloud".
+
+---
+## References
+.
+% FIXME: links
+1. "Local-First Software: You Own Your Data, in spite of the Cloud", by M. Kleppmann, A. Wiggins, P. Van Hardenberg and M. F. McGranaghan
+2. The Morning Paper article
+3. "A Note on Distributed Compiting", by J. Waldo, G. Wyant, A. Wollrath and S. Kendall
+4. these slides: euandre.org/slide/
+5. prose version of this presentation
+6. view source
diff --git a/src/content/en/slide/categories.adoc b/src/content/en/slide/categories.adoc
new file mode 100644
index 0000000..feb64ff
--- /dev/null
+++ b/src/content/en/slide/categories.adoc
@@ -0,0 +1,2 @@
+= Articles by category
+:type: categories
diff --git a/src/content/en/slide/index.adoc b/src/content/en/slide/index.adoc
new file mode 100644
index 0000000..4e5b53d
--- /dev/null
+++ b/src/content/en/slide/index.adoc
@@ -0,0 +1,4 @@
+= Slides
+
+Slide decks, rendered to PDF: no browser needed to read them, and no
+JavaScript presentation framework to carry along.
diff --git a/src/content/eo/blog/index.adoc b/src/content/eo/blog/index.adoc
new file mode 100644
index 0000000..eae4bf9
--- /dev/null
+++ b/src/content/eo/blog/index.adoc
@@ -0,0 +1,5 @@
+= Blogo
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/eo/blog/kategorioj.adoc b/src/content/eo/blog/kategorioj.adoc
new file mode 100644
index 0000000..a67c911
--- /dev/null
+++ b/src/content/eo/blog/kategorioj.adoc
@@ -0,0 +1,6 @@
+= Artikoloj laŭ kategorio
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/eo/hml/index.adoc b/src/content/eo/hml/index.adoc
new file mode 100644
index 0000000..27af611
--- /dev/null
+++ b/src/content/eo/hml/index.adoc
@@ -0,0 +1,11 @@
+= Hodiaŭ Mi Lernis
+
+:anna-e-so: https://til.flourishing.stream/
+
+**H**odiaŭ **M**i **L**ernis (du angla, _**T**oday **I** **L**earned_): malgrandaj afiŝoj de utilaj scioj.
+
+Senhonta kopio de {anna-e-so}[Anna e só].
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/eo/hml/kategorioj.adoc b/src/content/eo/hml/kategorioj.adoc
new file mode 100644
index 0000000..a67c911
--- /dev/null
+++ b/src/content/eo/hml/kategorioj.adoc
@@ -0,0 +1,6 @@
+= Artikoloj laŭ kategorio
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/eo/index.adoc b/src/content/eo/index.adoc
new file mode 100644
index 0000000..60b761e
--- /dev/null
+++ b/src/content/eo/index.adoc
@@ -0,0 +1,7 @@
+= Hejmo
+
+La plej novaj el ĉiu kolekto estas sube.
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/eo/lumbildoj/index.adoc b/src/content/eo/lumbildoj/index.adoc
new file mode 100644
index 0000000..7945c5f
--- /dev/null
+++ b/src/content/eo/lumbildoj/index.adoc
@@ -0,0 +1,7 @@
+= Lumbildoj
+
+Prezentaĵoj bildigitaj al PDF: neniu retumilo necesas por legi ilin, kaj neniu JavaScript-prezenta kadro por kunporti.
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/eo/lumbildoj/kategorioj.adoc b/src/content/eo/lumbildoj/kategorioj.adoc
new file mode 100644
index 0000000..a67c911
--- /dev/null
+++ b/src/content/eo/lumbildoj/kategorioj.adoc
@@ -0,0 +1,6 @@
+= Artikoloj laŭ kategorio
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/eo/muziko.adoc b/src/content/eo/muziko.adoc
new file mode 100644
index 0000000..0e83314
--- /dev/null
+++ b/src/content/eo/muziko.adoc
@@ -0,0 +1,36 @@
+= Muziko
+:dengoso: ../music/dengoso
+:marcha: ../music/marcha-dos-marinheiros
+:choro: ../music/choro-da-saudade
+
+Partituroj kiujn mi gravuris, ĉiu kun sintezita plenumo. La LilyPond-fontoj estas en la deponejo; la PDF-oj estas generitaj el ili.
+
+== Partituroj
+
+João Pernambuco, link:{dengoso}.pdf[Dengoso]
+
+++++
+<audio controls>
+ <source src="../music/dengoso.ogg" type="audio/ogg" />
+</audio>
+++++
+
+Canhoto, link:{marcha}.pdf[Marcha dos Marinheiros]
+
+++++
+<audio controls>
+ <source src="../music/marcha-dos-marinheiros.ogg" type="audio/ogg" />
+</audio>
+++++
+
+Agustín Barrios, link:{choro}.pdf[Choro da Saudade]
+
+++++
+<audio controls>
+ <source src="../music/choro-da-saudade.ogg" type="audio/ogg" />
+</audio>
+++++
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/eo/pastebin/index.adoc b/src/content/eo/pastebin/index.adoc
new file mode 100644
index 0000000..59cb93e
--- /dev/null
+++ b/src/content/eo/pastebin/index.adoc
@@ -0,0 +1,5 @@
+= Pastebins
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/eo/pastebin/kategorioj.adoc b/src/content/eo/pastebin/kategorioj.adoc
new file mode 100644
index 0000000..a67c911
--- /dev/null
+++ b/src/content/eo/pastebin/kategorioj.adoc
@@ -0,0 +1,6 @@
+= Artikoloj laŭ kategorio
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/eo/podkasto/index.adoc b/src/content/eo/podkasto/index.adoc
new file mode 100644
index 0000000..4450e5d
--- /dev/null
+++ b/src/content/eo/podkasto/index.adoc
@@ -0,0 +1,5 @@
+= Podkasto
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/eo/podkasto/kategorioj.adoc b/src/content/eo/podkasto/kategorioj.adoc
new file mode 100644
index 0000000..a67c911
--- /dev/null
+++ b/src/content/eo/podkasto/kategorioj.adoc
@@ -0,0 +1,6 @@
+= Artikoloj laŭ kategorio
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/eo/pri.adoc b/src/content/eo/pri.adoc
new file mode 100644
index 0000000..1da9565
--- /dev/null
+++ b/src/content/eo/pri.adoc
@@ -0,0 +1,26 @@
+= Pri
+
+:mailto: mailto:~euandreh/public-inbox@lists.sr.ht
+:archive: https://lists.sr.ht/~euandreh/public-inbox
+:cargo2nix: ../en/blog/2020/10/05/cargo2nix.html
+:swift2nix: ../en/blog/2020/10/05/swift2nix.html
+:database: ../en/blog/2020/08/31/database-i-wish-i-had.html
+:fallible: ../en/blog/2021/02/17/fallible.html
+:flags: ../en/blog/2020/10/19/feature-flags.html
+:interviewing: ../en/blog/2020/10/20/wrong-interviewing.html
+:stateless: ../en/blog/2019/06/02/nixos-stateless-workstation.html
+:bugtracker: ../en/blog/2020/11/07/diy-bugs.html
+
+Saluton, mi estas EuAndreh -- André Miranda Moreira. Mi verkas programojn kaj foje muzikojn. Vi povas trovi la kielo por kontakti min ĉe la fino de ĉi tiu paĝo, aŭ vi ankaŭ povas sendi mesaĝon al mia {mailto}[publika leterkesto] ({archive}[arkivo]).
+
+Ĉi tiu estas mia persona retejo, kie mi verkas artikolojn, publikas programojn kaj aliajn rilatajn laborojn.
+
+I write about robust software: the kind that still works after the network drops, after the vendor leaves, and after you stop paying attention.
+
+That shows up in a few places. I care about reproducible systems, so I run a link:{stateless}[stateless NixOS workstation] and have written bridges like link:{cargo2nix}[cargo2nix] and link:{swift2nix}[swift2nix] to make Rust and Swift first-class citizens inside Nix builds. I care about durable data, which is why I keep returning to local-first software, persistent data structures, and link:{database}[the database I wish existed]: embedded, immutable, and syncable. And I care about tools that don't need a company behind them, so I reach for Git, email and plain text before I reach for a service -- far enough that link:{bugtracker}[my bug tracker is a directory].
+
+I also think about how teams ship -- fault injection (link:{fallible}[fallible]), link:{flags}[feature flags], and link:{interviewing}[how we interview engineers] -- because robustness is a team habit before it is a technology.
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/eo/screencast/index.adoc b/src/content/eo/screencast/index.adoc
new file mode 100644
index 0000000..930c244
--- /dev/null
+++ b/src/content/eo/screencast/index.adoc
@@ -0,0 +1,5 @@
+= Screencasts
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/eo/screencast/kategorioj.adoc b/src/content/eo/screencast/kategorioj.adoc
new file mode 100644
index 0000000..a67c911
--- /dev/null
+++ b/src/content/eo/screencast/kategorioj.adoc
@@ -0,0 +1,6 @@
+= Artikoloj laŭ kategorio
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/es/acerca.adoc b/src/content/es/acerca.adoc
new file mode 100644
index 0000000..685decd
--- /dev/null
+++ b/src/content/es/acerca.adoc
@@ -0,0 +1,26 @@
+= Acerca
+
+:mailto: mailto:~euandreh/public-inbox@lists.sr.ht
+:archive: https://lists.sr.ht/~euandreh/public-inbox
+:cargo2nix: ../en/blog/2020/10/05/cargo2nix.html
+:swift2nix: ../en/blog/2020/10/05/swift2nix.html
+:database: ../en/blog/2020/08/31/database-i-wish-i-had.html
+:fallible: ../en/blog/2021/02/17/fallible.html
+:flags: ../en/blog/2020/10/19/feature-flags.html
+:interviewing: ../en/blog/2020/10/20/wrong-interviewing.html
+:stateless: ../en/blog/2019/06/02/nixos-stateless-workstation.html
+:bugtracker: ../en/blog/2020/11/07/diy-bugs.html
+
+Hola, soy EuAndreh -- André Miranda Moreira. Escribo software y, ocasionalmente, música. Puedes encontrar mis datos de contacto al pie de esta página, o escribir a mi {mailto}[buzón público] ({archive}[archivo]).
+
+Este es mi sitio personal, donde escribo artículos, publico software y otros trabajos relacionados.
+
+I write about robust software: the kind that still works after the network drops, after the vendor leaves, and after you stop paying attention.
+
+That shows up in a few places. I care about reproducible systems, so I run a link:{stateless}[stateless NixOS workstation] and have written bridges like link:{cargo2nix}[cargo2nix] and link:{swift2nix}[swift2nix] to make Rust and Swift first-class citizens inside Nix builds. I care about durable data, which is why I keep returning to local-first software, persistent data structures, and link:{database}[the database I wish existed]: embedded, immutable, and syncable. And I care about tools that don't need a company behind them, so I reach for Git, email and plain text before I reach for a service -- far enough that link:{bugtracker}[my bug tracker is a directory].
+
+I also think about how teams ship -- fault injection (link:{fallible}[fallible]), link:{flags}[feature flags], and link:{interviewing}[how we interview engineers] -- because robustness is a team habit before it is a technology.
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/es/blog/categorias.adoc b/src/content/es/blog/categorias.adoc
new file mode 100644
index 0000000..bdff794
--- /dev/null
+++ b/src/content/es/blog/categorias.adoc
@@ -0,0 +1,6 @@
+= Artículos por categoría
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/es/blog/index.adoc b/src/content/es/blog/index.adoc
new file mode 100644
index 0000000..ec9c676
--- /dev/null
+++ b/src/content/es/blog/index.adoc
@@ -0,0 +1,5 @@
+= Blog
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/es/diapositivas/categorias.adoc b/src/content/es/diapositivas/categorias.adoc
new file mode 100644
index 0000000..bdff794
--- /dev/null
+++ b/src/content/es/diapositivas/categorias.adoc
@@ -0,0 +1,6 @@
+= Artículos por categoría
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/es/diapositivas/index.adoc b/src/content/es/diapositivas/index.adoc
new file mode 100644
index 0000000..4870b35
--- /dev/null
+++ b/src/content/es/diapositivas/index.adoc
@@ -0,0 +1,7 @@
+= Diapositivas
+
+Presentaciones renderizadas en PDF: no hace falta un navegador para leerlas, ni un marco de presentación en JavaScript que arrastrar.
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/es/haa/categorias.adoc b/src/content/es/haa/categorias.adoc
new file mode 100644
index 0000000..bdff794
--- /dev/null
+++ b/src/content/es/haa/categorias.adoc
@@ -0,0 +1,6 @@
+= Artículos por categoría
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/es/haa/index.adoc b/src/content/es/haa/index.adoc
new file mode 100644
index 0000000..0613ea0
--- /dev/null
+++ b/src/content/es/haa/index.adoc
@@ -0,0 +1,11 @@
+= Hoy Aprendí Algo
+
+:anna-e-so: https://til.flourishing.stream/
+
+**H**oy **A**prendí **A**lgo (del inglés, _**T**oday **I** **L**earned_): pequeñas entradas de conocimiento útil.
+
+Copia descarada de {anna-e-so}[Anna e só].
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/es/index.adoc b/src/content/es/index.adoc
new file mode 100644
index 0000000..ee20c82
--- /dev/null
+++ b/src/content/es/index.adoc
@@ -0,0 +1,7 @@
+= Inicio
+
+Lo más reciente de cada colección está abajo.
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/es/musica.adoc b/src/content/es/musica.adoc
new file mode 100644
index 0000000..91e5759
--- /dev/null
+++ b/src/content/es/musica.adoc
@@ -0,0 +1,36 @@
+= Música
+:dengoso: ../music/dengoso
+:marcha: ../music/marcha-dos-marinheiros
+:choro: ../music/choro-da-saudade
+
+Partituras que he grabado, cada una con una interpretación sintetizada. Las fuentes de LilyPond están en el repositorio; los PDF se generan a partir de ellas.
+
+== Partituras
+
+João Pernambuco, link:{dengoso}.pdf[Dengoso]
+
+++++
+<audio controls>
+ <source src="../music/dengoso.ogg" type="audio/ogg" />
+</audio>
+++++
+
+Canhoto, link:{marcha}.pdf[Marcha dos Marinheiros]
+
+++++
+<audio controls>
+ <source src="../music/marcha-dos-marinheiros.ogg" type="audio/ogg" />
+</audio>
+++++
+
+Agustín Barrios, link:{choro}.pdf[Choro da Saudade]
+
+++++
+<audio controls>
+ <source src="../music/choro-da-saudade.ogg" type="audio/ogg" />
+</audio>
+++++
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/es/pastebin/categorias.adoc b/src/content/es/pastebin/categorias.adoc
new file mode 100644
index 0000000..bdff794
--- /dev/null
+++ b/src/content/es/pastebin/categorias.adoc
@@ -0,0 +1,6 @@
+= Artículos por categoría
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/es/pastebin/index.adoc b/src/content/es/pastebin/index.adoc
new file mode 100644
index 0000000..59cb93e
--- /dev/null
+++ b/src/content/es/pastebin/index.adoc
@@ -0,0 +1,5 @@
+= Pastebins
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/es/podcast/categorias.adoc b/src/content/es/podcast/categorias.adoc
new file mode 100644
index 0000000..bdff794
--- /dev/null
+++ b/src/content/es/podcast/categorias.adoc
@@ -0,0 +1,6 @@
+= Artículos por categoría
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/es/podcast/index.adoc b/src/content/es/podcast/index.adoc
new file mode 100644
index 0000000..6e84602
--- /dev/null
+++ b/src/content/es/podcast/index.adoc
@@ -0,0 +1,5 @@
+= Podcast
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/es/screencast/categorias.adoc b/src/content/es/screencast/categorias.adoc
new file mode 100644
index 0000000..bdff794
--- /dev/null
+++ b/src/content/es/screencast/categorias.adoc
@@ -0,0 +1,6 @@
+= Artículos por categoría
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/es/screencast/index.adoc b/src/content/es/screencast/index.adoc
new file mode 100644
index 0000000..930c244
--- /dev/null
+++ b/src/content/es/screencast/index.adoc
@@ -0,0 +1,5 @@
+= Screencasts
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/favicon.ico b/src/content/favicon.ico
index 8f2130a..19a4c9a 100644
--- a/src/content/favicon.ico
+++ b/src/content/favicon.ico
Binary files differ
diff --git a/src/content/favicon.png b/src/content/favicon.png
index 136d8cb..b74fc41 100644
--- a/src/content/favicon.png
+++ b/src/content/favicon.png
Binary files differ
diff --git a/src/content/fr/a-propos.adoc b/src/content/fr/a-propos.adoc
new file mode 100644
index 0000000..6e53be0
--- /dev/null
+++ b/src/content/fr/a-propos.adoc
@@ -0,0 +1,26 @@
+= À propos
+
+:mailto: mailto:~euandreh/public-inbox@lists.sr.ht
+:archive: https://lists.sr.ht/~euandreh/public-inbox
+:cargo2nix: ../en/blog/2020/10/05/cargo2nix.html
+:swift2nix: ../en/blog/2020/10/05/swift2nix.html
+:database: ../en/blog/2020/08/31/database-i-wish-i-had.html
+:fallible: ../en/blog/2021/02/17/fallible.html
+:flags: ../en/blog/2020/10/19/feature-flags.html
+:interviewing: ../en/blog/2020/10/20/wrong-interviewing.html
+:stateless: ../en/blog/2019/06/02/nixos-stateless-workstation.html
+:bugtracker: ../en/blog/2020/11/07/diy-bugs.html
+
+Salut, je suis EuAndreh -- André Miranda Moreira. J'écris logiciel et parfois musique. Vous pouvez rencontrer de donnés pour me contacter au bas cette de page, ou vous pouvez également envoyer une message dans ma {mailto}[boîte de réception publique] ({archive}[archive]).
+
+Ceci est mon site personnel où j'écris des articles, publie des logicielles et d'autres travaux connexes.
+
+I write about robust software: the kind that still works after the network drops, after the vendor leaves, and after you stop paying attention.
+
+That shows up in a few places. I care about reproducible systems, so I run a link:{stateless}[stateless NixOS workstation] and have written bridges like link:{cargo2nix}[cargo2nix] and link:{swift2nix}[swift2nix] to make Rust and Swift first-class citizens inside Nix builds. I care about durable data, which is why I keep returning to local-first software, persistent data structures, and link:{database}[the database I wish existed]: embedded, immutable, and syncable. And I care about tools that don't need a company behind them, so I reach for Git, email and plain text before I reach for a service -- far enough that link:{bugtracker}[my bug tracker is a directory].
+
+I also think about how teams ship -- fault injection (link:{fallible}[fallible]), link:{flags}[feature flags], and link:{interviewing}[how we interview engineers] -- because robustness is a team habit before it is a technology.
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/fr/aja/categories.adoc b/src/content/fr/aja/categories.adoc
new file mode 100644
index 0000000..0035d46
--- /dev/null
+++ b/src/content/fr/aja/categories.adoc
@@ -0,0 +1,6 @@
+= Articles par catégorie
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/fr/aja/index.adoc b/src/content/fr/aja/index.adoc
new file mode 100644
index 0000000..383e990
--- /dev/null
+++ b/src/content/fr/aja/index.adoc
@@ -0,0 +1,11 @@
+= Aujourd'hui J'ai Appris
+
+:anna-e-so: https://til.flourishing.stream/
+
+**A**ujourd'hui **J**'ai **A**ppris (de l'anglais, _**T**oday **I** **L**earned_): petites postes de connaissances utiles.
+
+Copie éhontée de {anna-e-so}[Anna e só].
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/fr/blog/categories.adoc b/src/content/fr/blog/categories.adoc
new file mode 100644
index 0000000..0035d46
--- /dev/null
+++ b/src/content/fr/blog/categories.adoc
@@ -0,0 +1,6 @@
+= Articles par catégorie
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/fr/blog/index.adoc b/src/content/fr/blog/index.adoc
new file mode 100644
index 0000000..4883948
--- /dev/null
+++ b/src/content/fr/blog/index.adoc
@@ -0,0 +1,5 @@
+= Blogue
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/fr/diapositives/categories.adoc b/src/content/fr/diapositives/categories.adoc
new file mode 100644
index 0000000..0035d46
--- /dev/null
+++ b/src/content/fr/diapositives/categories.adoc
@@ -0,0 +1,6 @@
+= Articles par catégorie
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/fr/diapositives/index.adoc b/src/content/fr/diapositives/index.adoc
new file mode 100644
index 0000000..3888992
--- /dev/null
+++ b/src/content/fr/diapositives/index.adoc
@@ -0,0 +1,7 @@
+= Diapositives
+
+Présentations rendues en PDF: aucun navigateur n'est nécessaire pour les lire, et aucun cadriciel de présentation en JavaScript à traîner.
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/fr/index.adoc b/src/content/fr/index.adoc
new file mode 100644
index 0000000..a243683
--- /dev/null
+++ b/src/content/fr/index.adoc
@@ -0,0 +1,7 @@
+= Accueil
+
+Les plus récents de chaque collection sont ci-dessous.
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/fr/musique.adoc b/src/content/fr/musique.adoc
new file mode 100644
index 0000000..27b8f17
--- /dev/null
+++ b/src/content/fr/musique.adoc
@@ -0,0 +1,36 @@
+= Musique
+:dengoso: ../music/dengoso
+:marcha: ../music/marcha-dos-marinheiros
+:choro: ../music/choro-da-saudade
+
+Partitions que j'ai gravées, chacune avec un rendu synthétisé. Les sources LilyPond sont dans le dépôt; les PDF en sont générés.
+
+== Partitions
+
+João Pernambuco, link:{dengoso}.pdf[Dengoso]
+
+++++
+<audio controls>
+ <source src="../music/dengoso.ogg" type="audio/ogg" />
+</audio>
+++++
+
+Canhoto, link:{marcha}.pdf[Marcha dos Marinheiros]
+
+++++
+<audio controls>
+ <source src="../music/marcha-dos-marinheiros.ogg" type="audio/ogg" />
+</audio>
+++++
+
+Agustín Barrios, link:{choro}.pdf[Choro da Saudade]
+
+++++
+<audio controls>
+ <source src="../music/choro-da-saudade.ogg" type="audio/ogg" />
+</audio>
+++++
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/fr/pastebin/categories.adoc b/src/content/fr/pastebin/categories.adoc
new file mode 100644
index 0000000..0035d46
--- /dev/null
+++ b/src/content/fr/pastebin/categories.adoc
@@ -0,0 +1,6 @@
+= Articles par catégorie
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/fr/pastebin/index.adoc b/src/content/fr/pastebin/index.adoc
new file mode 100644
index 0000000..59cb93e
--- /dev/null
+++ b/src/content/fr/pastebin/index.adoc
@@ -0,0 +1,5 @@
+= Pastebins
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/fr/podcast/categories.adoc b/src/content/fr/podcast/categories.adoc
new file mode 100644
index 0000000..0035d46
--- /dev/null
+++ b/src/content/fr/podcast/categories.adoc
@@ -0,0 +1,6 @@
+= Articles par catégorie
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/fr/podcast/index.adoc b/src/content/fr/podcast/index.adoc
new file mode 100644
index 0000000..6e84602
--- /dev/null
+++ b/src/content/fr/podcast/index.adoc
@@ -0,0 +1,5 @@
+= Podcast
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/fr/screencast/categories.adoc b/src/content/fr/screencast/categories.adoc
new file mode 100644
index 0000000..0035d46
--- /dev/null
+++ b/src/content/fr/screencast/categories.adoc
@@ -0,0 +1,6 @@
+= Articles par catégorie
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/fr/screencast/index.adoc b/src/content/fr/screencast/index.adoc
new file mode 100644
index 0000000..930c244
--- /dev/null
+++ b/src/content/fr/screencast/index.adoc
@@ -0,0 +1,5 @@
+= Screencasts
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/img/favicon.svg b/src/content/img/favicon.svg
index ce566b2..fbec3fc 100644
--- a/src/content/img/favicon.svg
+++ b/src/content/img/favicon.svg
@@ -1,62 +1,84 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
- <path d="M 0 8 L 1 8 L 1 9 L 0 9 L 0 8 Z" />
- <path d="M 0 13 L 1 13 L 1 14 L 0 14 L 0 13 Z" />
- <path d="M 1 8 L 2 8 L 2 9 L 1 9 L 1 8 Z" />
- <path d="M 1 13 L 2 13 L 2 14 L 1 14 L 1 13 Z" />
- <path d="M 2 8 L 3 8 L 3 9 L 2 9 L 2 8 Z" />
- <path d="M 2 13 L 3 13 L 3 14 L 2 14 L 2 13 Z" />
- <path d="M 3 8 L 4 8 L 4 9 L 3 9 L 3 8 Z" />
- <path d="M 3 13 L 4 13 L 4 14 L 3 14 L 3 13 Z" />
- <path d="M 4 7 L 5 7 L 5 8 L 4 8 L 4 7 Z" />
- <path d="M 4 8 L 5 8 L 5 9 L 4 9 L 4 8 Z" />
- <path d="M 4 13 L 5 13 L 5 14 L 4 14 L 4 13 Z" />
- <path d="M 5 6 L 6 6 L 6 7 L 5 7 L 5 6 Z" />
- <path d="M 5 7 L 6 7 L 6 8 L 5 8 L 5 7 Z" />
- <path d="M 5 13 L 6 13 L 6 14 L 5 14 L 5 13 Z" />
- <path d="M 6 5 L 7 5 L 7 6 L 6 6 L 6 5 Z" />
- <path d="M 6 6 L 7 6 L 7 7 L 6 7 L 6 6 Z" />
- <path d="M 6 14 L 7 14 L 7 15 L 6 15 L 6 14 Z" />
- <path d="M 7 1 L 8 1 L 8 2 L 7 2 L 7 1 Z" />
- <path d="M 7 14 L 8 14 L 8 15 L 7 15 L 7 14 Z" />
- <path d="M 7 15 L 8 15 L 8 16 L 7 16 L 7 15 Z" />
- <path d="M 7 2 L 8 2 L 8 3 L 7 3 L 7 2 Z" />
- <path d="M 7 3 L 8 3 L 8 4 L 7 4 L 7 3 Z" />
- <path d="M 7 4 L 8 4 L 8 5 L 7 5 L 7 4 Z" />
- <path d="M 7 5 L 8 5 L 8 6 L 7 6 L 7 5 Z" />
- <path d="M 8 1 L 9 1 L 9 2 L 8 2 L 8 1 Z" />
- <path d="M 8 15 L 9 15 L 9 16 L 8 16 L 8 15 Z" />
- <path d="M 9 1 L 10 1 L 10 2 L 9 2 L 9 1 Z" />
- <path d="M 9 2 L 10 2 L 10 3 L 9 3 L 9 2 Z" />
- <path d="M 9 6 L 10 6 L 10 7 L 9 7 L 9 6 Z" />
- <path d="M 9 15 L 10 15 L 10 16 L 9 16 L 9 15 Z" />
- <path d="M 10 2 L 11 2 L 11 3 L 10 3 L 10 2 Z" />
- <path d="M 10 3 L 11 3 L 11 4 L 10 4 L 10 3 Z" />
- <path d="M 10 4 L 11 4 L 11 5 L 10 5 L 10 4 Z" />
- <path d="M 10 5 L 11 5 L 11 6 L 10 6 L 10 5 Z" />
- <path d="M 10 6 L 11 6 L 11 7 L 10 7 L 10 6 Z" />
- <path d="M 11 6 L 12 6 L 12 7 L 11 7 L 11 6 Z" />
- <path d="M 11 8 L 12 8 L 12 9 L 11 9 L 11 8 Z" />
- <path d="M 10 15 L 11 15 L 11 16 L 10 16 L 10 15 Z" />
- <path d="M 11 10 L 12 10 L 12 11 L 11 11 L 11 10 Z" />
- <path d="M 11 12 L 12 12 L 12 13 L 11 13 L 11 12 Z" />
- <path d="M 11 14 L 12 14 L 12 15 L 11 15 L 11 14 Z" />
- <path d="M 11 15 L 12 15 L 12 16 L 11 16 L 11 15 Z" />
- <path d="M 12 6 L 13 6 L 13 7 L 12 7 L 12 6 Z" />
- <path d="M 12 8 L 13 8 L 13 9 L 12 9 L 12 8 Z" />
- <path d="M 12 10 L 13 10 L 13 11 L 12 11 L 12 10 Z" />
- <path d="M 12 12 L 13 12 L 13 13 L 12 13 L 12 12 Z" />
- <path d="M 12 14 L 13 14 L 13 15 L 12 15 L 12 14 Z" />
- <path d="M 13 6 L 14 6 L 14 7 L 13 7 L 13 6 Z" />
- <path d="M 13 8 L 14 8 L 14 9 L 13 9 L 13 8 Z" />
- <path d="M 13 10 L 14 10 L 14 11 L 13 11 L 13 10 Z" />
- <path d="M 13 12 L 14 12 L 14 13 L 13 13 L 13 12 Z" />
- <path d="M 13 13 L 14 13 L 14 14 L 13 14 L 13 13 Z" />
- <path d="M 13 14 L 14 14 L 14 15 L 13 15 L 13 14 Z" />
- <path d="M 14 7 L 15 7 L 15 8 L 14 8 L 14 7 Z" />
- <path d="M 14 8 L 15 8 L 15 9 L 14 9 L 14 8 Z" />
- <path d="M 14 9 L 15 9 L 15 10 L 14 10 L 14 9 Z" />
- <path d="M 14 10 L 15 10 L 15 11 L 14 11 L 14 10 Z" />
- <path d="M 14 11 L 15 11 L 15 12 L 14 12 L 14 11 Z" />
- <path d="M 14 12 L 15 12 L 15 13 L 14 13 L 14 12 Z" />
+ <path fill="#1F6F5B" d="M 0 0 L 1 0 L 1 1 L 0 1 L 0 0 Z" />
+ <path fill="#1F6F5B" d="M 0 15 L 1 15 L 1 16 L 0 16 L 0 15 Z" />
+ <path fill="#1F6F5B" d="M 15 0 L 16 0 L 16 1 L 15 1 L 15 0 Z" />
+ <path fill="#1F6F5B" d="M 1 0 L 2 0 L 2 1 L 1 1 L 1 0 Z" />
+ <path fill="#1F6F5B" d="M 1 15 L 2 15 L 2 16 L 1 16 L 1 15 Z" />
+ <path fill="#1F6F5B" d="M 0 1 L 1 1 L 1 2 L 0 2 L 0 1 Z" />
+ <path fill="#1F6F5B" d="M 15 1 L 16 1 L 16 2 L 15 2 L 15 1 Z" />
+ <path fill="#1F6F5B" d="M 2 0 L 3 0 L 3 1 L 2 1 L 2 0 Z" />
+ <path fill="#1F6F5B" d="M 2 15 L 3 15 L 3 16 L 2 16 L 2 15 Z" />
+ <path fill="#1F6F5B" d="M 0 2 L 1 2 L 1 3 L 0 3 L 0 2 Z" />
+ <path fill="#1F6F5B" d="M 15 2 L 16 2 L 16 3 L 15 3 L 15 2 Z" />
+ <path fill="#1F6F5B" d="M 3 0 L 4 0 L 4 1 L 3 1 L 3 0 Z" />
+ <path fill="#1F6F5B" d="M 3 15 L 4 15 L 4 16 L 3 16 L 3 15 Z" />
+ <path fill="#1F6F5B" d="M 0 3 L 1 3 L 1 4 L 0 4 L 0 3 Z" />
+ <path fill="#1F6F5B" d="M 15 3 L 16 3 L 16 4 L 15 4 L 15 3 Z" />
+ <path fill="#1F6F5B" d="M 4 0 L 5 0 L 5 1 L 4 1 L 4 0 Z" />
+ <path fill="#1F6F5B" d="M 4 15 L 5 15 L 5 16 L 4 16 L 4 15 Z" />
+ <path fill="#1F6F5B" d="M 0 4 L 1 4 L 1 5 L 0 5 L 0 4 Z" />
+ <path fill="#1F6F5B" d="M 15 4 L 16 4 L 16 5 L 15 5 L 15 4 Z" />
+ <path fill="#1F6F5B" d="M 5 0 L 6 0 L 6 1 L 5 1 L 5 0 Z" />
+ <path fill="#1F6F5B" d="M 5 15 L 6 15 L 6 16 L 5 16 L 5 15 Z" />
+ <path fill="#1F6F5B" d="M 0 5 L 1 5 L 1 6 L 0 6 L 0 5 Z" />
+ <path fill="#1F6F5B" d="M 15 5 L 16 5 L 16 6 L 15 6 L 15 5 Z" />
+ <path fill="#1F6F5B" d="M 6 0 L 7 0 L 7 1 L 6 1 L 6 0 Z" />
+ <path fill="#1F6F5B" d="M 6 15 L 7 15 L 7 16 L 6 16 L 6 15 Z" />
+ <path fill="#1F6F5B" d="M 0 6 L 1 6 L 1 7 L 0 7 L 0 6 Z" />
+ <path fill="#1F6F5B" d="M 15 6 L 16 6 L 16 7 L 15 7 L 15 6 Z" />
+ <path fill="#1F6F5B" d="M 7 0 L 8 0 L 8 1 L 7 1 L 7 0 Z" />
+ <path fill="#1F6F5B" d="M 7 15 L 8 15 L 8 16 L 7 16 L 7 15 Z" />
+ <path fill="#1F6F5B" d="M 0 7 L 1 7 L 1 8 L 0 8 L 0 7 Z" />
+ <path fill="#1F6F5B" d="M 15 7 L 16 7 L 16 8 L 15 8 L 15 7 Z" />
+ <path fill="#1F6F5B" d="M 8 0 L 9 0 L 9 1 L 8 1 L 8 0 Z" />
+ <path fill="#1F6F5B" d="M 8 15 L 9 15 L 9 16 L 8 16 L 8 15 Z" />
+ <path fill="#1F6F5B" d="M 0 8 L 1 8 L 1 9 L 0 9 L 0 8 Z" />
+ <path fill="#1F6F5B" d="M 15 8 L 16 8 L 16 9 L 15 9 L 15 8 Z" />
+ <path fill="#1F6F5B" d="M 9 0 L 10 0 L 10 1 L 9 1 L 9 0 Z" />
+ <path fill="#1F6F5B" d="M 9 15 L 10 15 L 10 16 L 9 16 L 9 15 Z" />
+ <path fill="#1F6F5B" d="M 0 9 L 1 9 L 1 10 L 0 10 L 0 9 Z" />
+ <path fill="#1F6F5B" d="M 15 9 L 16 9 L 16 10 L 15 10 L 15 9 Z" />
+ <path fill="#1F6F5B" d="M 10 0 L 11 0 L 11 1 L 10 1 L 10 0 Z" />
+ <path fill="#1F6F5B" d="M 10 15 L 11 15 L 11 16 L 10 16 L 10 15 Z" />
+ <path fill="#1F6F5B" d="M 0 10 L 1 10 L 1 11 L 0 11 L 0 10 Z" />
+ <path fill="#1F6F5B" d="M 15 10 L 16 10 L 16 11 L 15 11 L 15 10 Z" />
+ <path fill="#1F6F5B" d="M 11 0 L 12 0 L 12 1 L 11 1 L 11 0 Z" />
+ <path fill="#1F6F5B" d="M 11 15 L 12 15 L 12 16 L 11 16 L 11 15 Z" />
+ <path fill="#1F6F5B" d="M 0 11 L 1 11 L 1 12 L 0 12 L 0 11 Z" />
+ <path fill="#1F6F5B" d="M 15 11 L 16 11 L 16 12 L 15 12 L 15 11 Z" />
+ <path fill="#1F6F5B" d="M 12 0 L 13 0 L 13 1 L 12 1 L 12 0 Z" />
+ <path fill="#1F6F5B" d="M 12 15 L 13 15 L 13 16 L 12 16 L 12 15 Z" />
+ <path fill="#1F6F5B" d="M 0 12 L 1 12 L 1 13 L 0 13 L 0 12 Z" />
+ <path fill="#1F6F5B" d="M 15 12 L 16 12 L 16 13 L 15 13 L 15 12 Z" />
+ <path fill="#1F6F5B" d="M 13 0 L 14 0 L 14 1 L 13 1 L 13 0 Z" />
+ <path fill="#1F6F5B" d="M 13 15 L 14 15 L 14 16 L 13 16 L 13 15 Z" />
+ <path fill="#1F6F5B" d="M 0 13 L 1 13 L 1 14 L 0 14 L 0 13 Z" />
+ <path fill="#1F6F5B" d="M 15 13 L 16 13 L 16 14 L 15 14 L 15 13 Z" />
+ <path fill="#1F6F5B" d="M 14 0 L 15 0 L 15 1 L 14 1 L 14 0 Z" />
+ <path fill="#1F6F5B" d="M 14 15 L 15 15 L 15 16 L 14 16 L 14 15 Z" />
+ <path fill="#1F6F5B" d="M 0 14 L 1 14 L 1 15 L 0 15 L 0 14 Z" />
+ <path fill="#1F6F5B" d="M 15 14 L 16 14 L 16 15 L 15 15 L 15 14 Z" />
+ <path fill="#1F6F5B" d="M 15 15 L 16 15 L 16 16 L 15 16 L 15 15 Z" />
+ <path fill="#1F6F5B" d="M 4 7 L 5 7 L 5 8 L 4 8 L 4 7 Z" />
+ <path fill="#1F6F5B" d="M 5 7 L 6 7 L 6 8 L 5 8 L 5 7 Z" />
+ <path fill="#1F6F5B" d="M 3 8 L 4 8 L 4 9 L 3 9 L 3 8 Z" />
+ <path fill="#1F6F5B" d="M 6 8 L 7 8 L 7 9 L 6 9 L 6 8 Z" />
+ <path fill="#1F6F5B" d="M 3 9 L 4 9 L 4 10 L 3 10 L 3 9 Z" />
+ <path fill="#1F6F5B" d="M 4 9 L 5 9 L 5 10 L 4 10 L 4 9 Z" />
+ <path fill="#1F6F5B" d="M 5 9 L 6 9 L 6 10 L 5 10 L 5 9 Z" />
+ <path fill="#1F6F5B" d="M 3 10 L 4 10 L 4 11 L 3 11 L 3 10 Z" />
+ <path fill="#1F6F5B" d="M 4 11 L 5 11 L 5 12 L 4 12 L 4 11 Z" />
+ <path fill="#1F6F5B" d="M 5 11 L 6 11 L 6 12 L 5 12 L 5 11 Z" />
+ <path fill="#1F6F5B" d="M 8 7 L 9 7 L 9 8 L 8 8 L 8 7 Z" />
+ <path fill="#1F6F5B" d="M 11 7 L 12 7 L 12 8 L 11 8 L 11 7 Z" />
+ <path fill="#1F6F5B" d="M 8 8 L 9 8 L 9 9 L 8 9 L 8 8 Z" />
+ <path fill="#1F6F5B" d="M 11 8 L 12 8 L 12 9 L 11 9 L 11 8 Z" />
+ <path fill="#1F6F5B" d="M 8 9 L 9 9 L 9 10 L 8 10 L 8 9 Z" />
+ <path fill="#1F6F5B" d="M 11 9 L 12 9 L 12 10 L 11 10 L 11 9 Z" />
+ <path fill="#1F6F5B" d="M 8 10 L 9 10 L 9 11 L 8 11 L 8 10 Z" />
+ <path fill="#1F6F5B" d="M 11 10 L 12 10 L 12 11 L 11 11 L 11 10 Z" />
+ <path fill="#1F6F5B" d="M 9 11 L 10 11 L 10 12 L 9 12 L 9 11 Z" />
+ <path fill="#1F6F5B" d="M 10 11 L 11 11 L 11 12 L 10 12 L 10 11 Z" />
+ <path fill="#1F6F5B" d="M 11 11 L 12 11 L 12 12 L 11 12 L 11 11 Z" />
</svg>
diff --git a/src/content/img/logo/dark.svg b/src/content/img/logo/dark.svg
index 96e0f87..87b705b 100644
--- a/src/content/img/logo/dark.svg
+++ b/src/content/img/logo/dark.svg
@@ -1,62 +1,84 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
- <path fill="white" d="M 0 8 L 1 8 L 1 9 L 0 9 L 0 8 Z" />
- <path fill="white" d="M 0 13 L 1 13 L 1 14 L 0 14 L 0 13 Z" />
- <path fill="white" d="M 1 8 L 2 8 L 2 9 L 1 9 L 1 8 Z" />
- <path fill="white" d="M 1 13 L 2 13 L 2 14 L 1 14 L 1 13 Z" />
- <path fill="white" d="M 2 8 L 3 8 L 3 9 L 2 9 L 2 8 Z" />
- <path fill="white" d="M 2 13 L 3 13 L 3 14 L 2 14 L 2 13 Z" />
- <path fill="white" d="M 3 8 L 4 8 L 4 9 L 3 9 L 3 8 Z" />
- <path fill="white" d="M 3 13 L 4 13 L 4 14 L 3 14 L 3 13 Z" />
- <path fill="white" d="M 4 7 L 5 7 L 5 8 L 4 8 L 4 7 Z" />
- <path fill="white" d="M 4 8 L 5 8 L 5 9 L 4 9 L 4 8 Z" />
- <path fill="white" d="M 4 13 L 5 13 L 5 14 L 4 14 L 4 13 Z" />
- <path fill="white" d="M 5 6 L 6 6 L 6 7 L 5 7 L 5 6 Z" />
- <path fill="white" d="M 5 7 L 6 7 L 6 8 L 5 8 L 5 7 Z" />
- <path fill="white" d="M 5 13 L 6 13 L 6 14 L 5 14 L 5 13 Z" />
- <path fill="white" d="M 6 5 L 7 5 L 7 6 L 6 6 L 6 5 Z" />
- <path fill="white" d="M 6 6 L 7 6 L 7 7 L 6 7 L 6 6 Z" />
- <path fill="white" d="M 6 14 L 7 14 L 7 15 L 6 15 L 6 14 Z" />
- <path fill="white" d="M 7 1 L 8 1 L 8 2 L 7 2 L 7 1 Z" />
- <path fill="white" d="M 7 14 L 8 14 L 8 15 L 7 15 L 7 14 Z" />
+ <path fill="white" d="M 0 0 L 1 0 L 1 1 L 0 1 L 0 0 Z" />
+ <path fill="white" d="M 0 15 L 1 15 L 1 16 L 0 16 L 0 15 Z" />
+ <path fill="white" d="M 15 0 L 16 0 L 16 1 L 15 1 L 15 0 Z" />
+ <path fill="white" d="M 1 0 L 2 0 L 2 1 L 1 1 L 1 0 Z" />
+ <path fill="white" d="M 1 15 L 2 15 L 2 16 L 1 16 L 1 15 Z" />
+ <path fill="white" d="M 0 1 L 1 1 L 1 2 L 0 2 L 0 1 Z" />
+ <path fill="white" d="M 15 1 L 16 1 L 16 2 L 15 2 L 15 1 Z" />
+ <path fill="white" d="M 2 0 L 3 0 L 3 1 L 2 1 L 2 0 Z" />
+ <path fill="white" d="M 2 15 L 3 15 L 3 16 L 2 16 L 2 15 Z" />
+ <path fill="white" d="M 0 2 L 1 2 L 1 3 L 0 3 L 0 2 Z" />
+ <path fill="white" d="M 15 2 L 16 2 L 16 3 L 15 3 L 15 2 Z" />
+ <path fill="white" d="M 3 0 L 4 0 L 4 1 L 3 1 L 3 0 Z" />
+ <path fill="white" d="M 3 15 L 4 15 L 4 16 L 3 16 L 3 15 Z" />
+ <path fill="white" d="M 0 3 L 1 3 L 1 4 L 0 4 L 0 3 Z" />
+ <path fill="white" d="M 15 3 L 16 3 L 16 4 L 15 4 L 15 3 Z" />
+ <path fill="white" d="M 4 0 L 5 0 L 5 1 L 4 1 L 4 0 Z" />
+ <path fill="white" d="M 4 15 L 5 15 L 5 16 L 4 16 L 4 15 Z" />
+ <path fill="white" d="M 0 4 L 1 4 L 1 5 L 0 5 L 0 4 Z" />
+ <path fill="white" d="M 15 4 L 16 4 L 16 5 L 15 5 L 15 4 Z" />
+ <path fill="white" d="M 5 0 L 6 0 L 6 1 L 5 1 L 5 0 Z" />
+ <path fill="white" d="M 5 15 L 6 15 L 6 16 L 5 16 L 5 15 Z" />
+ <path fill="white" d="M 0 5 L 1 5 L 1 6 L 0 6 L 0 5 Z" />
+ <path fill="white" d="M 15 5 L 16 5 L 16 6 L 15 6 L 15 5 Z" />
+ <path fill="white" d="M 6 0 L 7 0 L 7 1 L 6 1 L 6 0 Z" />
+ <path fill="white" d="M 6 15 L 7 15 L 7 16 L 6 16 L 6 15 Z" />
+ <path fill="white" d="M 0 6 L 1 6 L 1 7 L 0 7 L 0 6 Z" />
+ <path fill="white" d="M 15 6 L 16 6 L 16 7 L 15 7 L 15 6 Z" />
+ <path fill="white" d="M 7 0 L 8 0 L 8 1 L 7 1 L 7 0 Z" />
<path fill="white" d="M 7 15 L 8 15 L 8 16 L 7 16 L 7 15 Z" />
- <path fill="white" d="M 7 2 L 8 2 L 8 3 L 7 3 L 7 2 Z" />
- <path fill="white" d="M 7 3 L 8 3 L 8 4 L 7 4 L 7 3 Z" />
- <path fill="white" d="M 7 4 L 8 4 L 8 5 L 7 5 L 7 4 Z" />
- <path fill="white" d="M 7 5 L 8 5 L 8 6 L 7 6 L 7 5 Z" />
- <path fill="white" d="M 8 1 L 9 1 L 9 2 L 8 2 L 8 1 Z" />
+ <path fill="white" d="M 0 7 L 1 7 L 1 8 L 0 8 L 0 7 Z" />
+ <path fill="white" d="M 15 7 L 16 7 L 16 8 L 15 8 L 15 7 Z" />
+ <path fill="white" d="M 8 0 L 9 0 L 9 1 L 8 1 L 8 0 Z" />
<path fill="white" d="M 8 15 L 9 15 L 9 16 L 8 16 L 8 15 Z" />
- <path fill="white" d="M 9 1 L 10 1 L 10 2 L 9 2 L 9 1 Z" />
- <path fill="white" d="M 9 2 L 10 2 L 10 3 L 9 3 L 9 2 Z" />
- <path fill="white" d="M 9 6 L 10 6 L 10 7 L 9 7 L 9 6 Z" />
+ <path fill="white" d="M 0 8 L 1 8 L 1 9 L 0 9 L 0 8 Z" />
+ <path fill="white" d="M 15 8 L 16 8 L 16 9 L 15 9 L 15 8 Z" />
+ <path fill="white" d="M 9 0 L 10 0 L 10 1 L 9 1 L 9 0 Z" />
<path fill="white" d="M 9 15 L 10 15 L 10 16 L 9 16 L 9 15 Z" />
- <path fill="white" d="M 10 2 L 11 2 L 11 3 L 10 3 L 10 2 Z" />
- <path fill="white" d="M 10 3 L 11 3 L 11 4 L 10 4 L 10 3 Z" />
- <path fill="white" d="M 10 4 L 11 4 L 11 5 L 10 5 L 10 4 Z" />
- <path fill="white" d="M 10 5 L 11 5 L 11 6 L 10 6 L 10 5 Z" />
- <path fill="white" d="M 10 6 L 11 6 L 11 7 L 10 7 L 10 6 Z" />
- <path fill="white" d="M 11 6 L 12 6 L 12 7 L 11 7 L 11 6 Z" />
- <path fill="white" d="M 11 8 L 12 8 L 12 9 L 11 9 L 11 8 Z" />
+ <path fill="white" d="M 0 9 L 1 9 L 1 10 L 0 10 L 0 9 Z" />
+ <path fill="white" d="M 15 9 L 16 9 L 16 10 L 15 10 L 15 9 Z" />
+ <path fill="white" d="M 10 0 L 11 0 L 11 1 L 10 1 L 10 0 Z" />
<path fill="white" d="M 10 15 L 11 15 L 11 16 L 10 16 L 10 15 Z" />
- <path fill="white" d="M 11 10 L 12 10 L 12 11 L 11 11 L 11 10 Z" />
- <path fill="white" d="M 11 12 L 12 12 L 12 13 L 11 13 L 11 12 Z" />
- <path fill="white" d="M 11 14 L 12 14 L 12 15 L 11 15 L 11 14 Z" />
+ <path fill="white" d="M 0 10 L 1 10 L 1 11 L 0 11 L 0 10 Z" />
+ <path fill="white" d="M 15 10 L 16 10 L 16 11 L 15 11 L 15 10 Z" />
+ <path fill="white" d="M 11 0 L 12 0 L 12 1 L 11 1 L 11 0 Z" />
<path fill="white" d="M 11 15 L 12 15 L 12 16 L 11 16 L 11 15 Z" />
- <path fill="white" d="M 12 6 L 13 6 L 13 7 L 12 7 L 12 6 Z" />
- <path fill="white" d="M 12 8 L 13 8 L 13 9 L 12 9 L 12 8 Z" />
- <path fill="white" d="M 12 10 L 13 10 L 13 11 L 12 11 L 12 10 Z" />
- <path fill="white" d="M 12 12 L 13 12 L 13 13 L 12 13 L 12 12 Z" />
- <path fill="white" d="M 12 14 L 13 14 L 13 15 L 12 15 L 12 14 Z" />
- <path fill="white" d="M 13 6 L 14 6 L 14 7 L 13 7 L 13 6 Z" />
- <path fill="white" d="M 13 8 L 14 8 L 14 9 L 13 9 L 13 8 Z" />
- <path fill="white" d="M 13 10 L 14 10 L 14 11 L 13 11 L 13 10 Z" />
- <path fill="white" d="M 13 12 L 14 12 L 14 13 L 13 13 L 13 12 Z" />
- <path fill="white" d="M 13 13 L 14 13 L 14 14 L 13 14 L 13 13 Z" />
- <path fill="white" d="M 13 14 L 14 14 L 14 15 L 13 15 L 13 14 Z" />
- <path fill="white" d="M 14 7 L 15 7 L 15 8 L 14 8 L 14 7 Z" />
- <path fill="white" d="M 14 8 L 15 8 L 15 9 L 14 9 L 14 8 Z" />
- <path fill="white" d="M 14 9 L 15 9 L 15 10 L 14 10 L 14 9 Z" />
- <path fill="white" d="M 14 10 L 15 10 L 15 11 L 14 11 L 14 10 Z" />
- <path fill="white" d="M 14 11 L 15 11 L 15 12 L 14 12 L 14 11 Z" />
- <path fill="white" d="M 14 12 L 15 12 L 15 13 L 14 13 L 14 12 Z" />
+ <path fill="white" d="M 0 11 L 1 11 L 1 12 L 0 12 L 0 11 Z" />
+ <path fill="white" d="M 15 11 L 16 11 L 16 12 L 15 12 L 15 11 Z" />
+ <path fill="white" d="M 12 0 L 13 0 L 13 1 L 12 1 L 12 0 Z" />
+ <path fill="white" d="M 12 15 L 13 15 L 13 16 L 12 16 L 12 15 Z" />
+ <path fill="white" d="M 0 12 L 1 12 L 1 13 L 0 13 L 0 12 Z" />
+ <path fill="white" d="M 15 12 L 16 12 L 16 13 L 15 13 L 15 12 Z" />
+ <path fill="white" d="M 13 0 L 14 0 L 14 1 L 13 1 L 13 0 Z" />
+ <path fill="white" d="M 13 15 L 14 15 L 14 16 L 13 16 L 13 15 Z" />
+ <path fill="white" d="M 0 13 L 1 13 L 1 14 L 0 14 L 0 13 Z" />
+ <path fill="white" d="M 15 13 L 16 13 L 16 14 L 15 14 L 15 13 Z" />
+ <path fill="white" d="M 14 0 L 15 0 L 15 1 L 14 1 L 14 0 Z" />
+ <path fill="white" d="M 14 15 L 15 15 L 15 16 L 14 16 L 14 15 Z" />
+ <path fill="white" d="M 0 14 L 1 14 L 1 15 L 0 15 L 0 14 Z" />
+ <path fill="white" d="M 15 14 L 16 14 L 16 15 L 15 15 L 15 14 Z" />
+ <path fill="white" d="M 15 15 L 16 15 L 16 16 L 15 16 L 15 15 Z" />
+ <path fill="#5CC2A2" d="M 4 7 L 5 7 L 5 8 L 4 8 L 4 7 Z" />
+ <path fill="#5CC2A2" d="M 5 7 L 6 7 L 6 8 L 5 8 L 5 7 Z" />
+ <path fill="#5CC2A2" d="M 3 8 L 4 8 L 4 9 L 3 9 L 3 8 Z" />
+ <path fill="#5CC2A2" d="M 6 8 L 7 8 L 7 9 L 6 9 L 6 8 Z" />
+ <path fill="#5CC2A2" d="M 3 9 L 4 9 L 4 10 L 3 10 L 3 9 Z" />
+ <path fill="#5CC2A2" d="M 4 9 L 5 9 L 5 10 L 4 10 L 4 9 Z" />
+ <path fill="#5CC2A2" d="M 5 9 L 6 9 L 6 10 L 5 10 L 5 9 Z" />
+ <path fill="#5CC2A2" d="M 3 10 L 4 10 L 4 11 L 3 11 L 3 10 Z" />
+ <path fill="#5CC2A2" d="M 4 11 L 5 11 L 5 12 L 4 12 L 4 11 Z" />
+ <path fill="#5CC2A2" d="M 5 11 L 6 11 L 6 12 L 5 12 L 5 11 Z" />
+ <path fill="#5CC2A2" d="M 8 7 L 9 7 L 9 8 L 8 8 L 8 7 Z" />
+ <path fill="#5CC2A2" d="M 11 7 L 12 7 L 12 8 L 11 8 L 11 7 Z" />
+ <path fill="#5CC2A2" d="M 8 8 L 9 8 L 9 9 L 8 9 L 8 8 Z" />
+ <path fill="#5CC2A2" d="M 11 8 L 12 8 L 12 9 L 11 9 L 11 8 Z" />
+ <path fill="#5CC2A2" d="M 8 9 L 9 9 L 9 10 L 8 10 L 8 9 Z" />
+ <path fill="#5CC2A2" d="M 11 9 L 12 9 L 12 10 L 11 10 L 11 9 Z" />
+ <path fill="#5CC2A2" d="M 8 10 L 9 10 L 9 11 L 8 11 L 8 10 Z" />
+ <path fill="#5CC2A2" d="M 11 10 L 12 10 L 12 11 L 11 11 L 11 10 Z" />
+ <path fill="#5CC2A2" d="M 9 11 L 10 11 L 10 12 L 9 12 L 9 11 Z" />
+ <path fill="#5CC2A2" d="M 10 11 L 11 11 L 11 12 L 10 12 L 10 11 Z" />
+ <path fill="#5CC2A2" d="M 11 11 L 12 11 L 12 12 L 11 12 L 11 11 Z" />
</svg>
diff --git a/src/content/img/logo/light.svg b/src/content/img/logo/light.svg
index ab6000b..b6dd72a 100644
--- a/src/content/img/logo/light.svg
+++ b/src/content/img/logo/light.svg
@@ -1,62 +1,84 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
- <path fill="black" d="M 0 8 L 1 8 L 1 9 L 0 9 L 0 8 Z" />
- <path fill="black" d="M 0 13 L 1 13 L 1 14 L 0 14 L 0 13 Z" />
- <path fill="black" d="M 1 8 L 2 8 L 2 9 L 1 9 L 1 8 Z" />
- <path fill="black" d="M 1 13 L 2 13 L 2 14 L 1 14 L 1 13 Z" />
- <path fill="black" d="M 2 8 L 3 8 L 3 9 L 2 9 L 2 8 Z" />
- <path fill="black" d="M 2 13 L 3 13 L 3 14 L 2 14 L 2 13 Z" />
- <path fill="black" d="M 3 8 L 4 8 L 4 9 L 3 9 L 3 8 Z" />
- <path fill="black" d="M 3 13 L 4 13 L 4 14 L 3 14 L 3 13 Z" />
- <path fill="black" d="M 4 7 L 5 7 L 5 8 L 4 8 L 4 7 Z" />
- <path fill="black" d="M 4 8 L 5 8 L 5 9 L 4 9 L 4 8 Z" />
- <path fill="black" d="M 4 13 L 5 13 L 5 14 L 4 14 L 4 13 Z" />
- <path fill="black" d="M 5 6 L 6 6 L 6 7 L 5 7 L 5 6 Z" />
- <path fill="black" d="M 5 7 L 6 7 L 6 8 L 5 8 L 5 7 Z" />
- <path fill="black" d="M 5 13 L 6 13 L 6 14 L 5 14 L 5 13 Z" />
- <path fill="black" d="M 6 5 L 7 5 L 7 6 L 6 6 L 6 5 Z" />
- <path fill="black" d="M 6 6 L 7 6 L 7 7 L 6 7 L 6 6 Z" />
- <path fill="black" d="M 6 14 L 7 14 L 7 15 L 6 15 L 6 14 Z" />
- <path fill="black" d="M 7 1 L 8 1 L 8 2 L 7 2 L 7 1 Z" />
- <path fill="black" d="M 7 14 L 8 14 L 8 15 L 7 15 L 7 14 Z" />
+ <path fill="black" d="M 0 0 L 1 0 L 1 1 L 0 1 L 0 0 Z" />
+ <path fill="black" d="M 0 15 L 1 15 L 1 16 L 0 16 L 0 15 Z" />
+ <path fill="black" d="M 15 0 L 16 0 L 16 1 L 15 1 L 15 0 Z" />
+ <path fill="black" d="M 1 0 L 2 0 L 2 1 L 1 1 L 1 0 Z" />
+ <path fill="black" d="M 1 15 L 2 15 L 2 16 L 1 16 L 1 15 Z" />
+ <path fill="black" d="M 0 1 L 1 1 L 1 2 L 0 2 L 0 1 Z" />
+ <path fill="black" d="M 15 1 L 16 1 L 16 2 L 15 2 L 15 1 Z" />
+ <path fill="black" d="M 2 0 L 3 0 L 3 1 L 2 1 L 2 0 Z" />
+ <path fill="black" d="M 2 15 L 3 15 L 3 16 L 2 16 L 2 15 Z" />
+ <path fill="black" d="M 0 2 L 1 2 L 1 3 L 0 3 L 0 2 Z" />
+ <path fill="black" d="M 15 2 L 16 2 L 16 3 L 15 3 L 15 2 Z" />
+ <path fill="black" d="M 3 0 L 4 0 L 4 1 L 3 1 L 3 0 Z" />
+ <path fill="black" d="M 3 15 L 4 15 L 4 16 L 3 16 L 3 15 Z" />
+ <path fill="black" d="M 0 3 L 1 3 L 1 4 L 0 4 L 0 3 Z" />
+ <path fill="black" d="M 15 3 L 16 3 L 16 4 L 15 4 L 15 3 Z" />
+ <path fill="black" d="M 4 0 L 5 0 L 5 1 L 4 1 L 4 0 Z" />
+ <path fill="black" d="M 4 15 L 5 15 L 5 16 L 4 16 L 4 15 Z" />
+ <path fill="black" d="M 0 4 L 1 4 L 1 5 L 0 5 L 0 4 Z" />
+ <path fill="black" d="M 15 4 L 16 4 L 16 5 L 15 5 L 15 4 Z" />
+ <path fill="black" d="M 5 0 L 6 0 L 6 1 L 5 1 L 5 0 Z" />
+ <path fill="black" d="M 5 15 L 6 15 L 6 16 L 5 16 L 5 15 Z" />
+ <path fill="black" d="M 0 5 L 1 5 L 1 6 L 0 6 L 0 5 Z" />
+ <path fill="black" d="M 15 5 L 16 5 L 16 6 L 15 6 L 15 5 Z" />
+ <path fill="black" d="M 6 0 L 7 0 L 7 1 L 6 1 L 6 0 Z" />
+ <path fill="black" d="M 6 15 L 7 15 L 7 16 L 6 16 L 6 15 Z" />
+ <path fill="black" d="M 0 6 L 1 6 L 1 7 L 0 7 L 0 6 Z" />
+ <path fill="black" d="M 15 6 L 16 6 L 16 7 L 15 7 L 15 6 Z" />
+ <path fill="black" d="M 7 0 L 8 0 L 8 1 L 7 1 L 7 0 Z" />
<path fill="black" d="M 7 15 L 8 15 L 8 16 L 7 16 L 7 15 Z" />
- <path fill="black" d="M 7 2 L 8 2 L 8 3 L 7 3 L 7 2 Z" />
- <path fill="black" d="M 7 3 L 8 3 L 8 4 L 7 4 L 7 3 Z" />
- <path fill="black" d="M 7 4 L 8 4 L 8 5 L 7 5 L 7 4 Z" />
- <path fill="black" d="M 7 5 L 8 5 L 8 6 L 7 6 L 7 5 Z" />
- <path fill="black" d="M 8 1 L 9 1 L 9 2 L 8 2 L 8 1 Z" />
+ <path fill="black" d="M 0 7 L 1 7 L 1 8 L 0 8 L 0 7 Z" />
+ <path fill="black" d="M 15 7 L 16 7 L 16 8 L 15 8 L 15 7 Z" />
+ <path fill="black" d="M 8 0 L 9 0 L 9 1 L 8 1 L 8 0 Z" />
<path fill="black" d="M 8 15 L 9 15 L 9 16 L 8 16 L 8 15 Z" />
- <path fill="black" d="M 9 1 L 10 1 L 10 2 L 9 2 L 9 1 Z" />
- <path fill="black" d="M 9 2 L 10 2 L 10 3 L 9 3 L 9 2 Z" />
- <path fill="black" d="M 9 6 L 10 6 L 10 7 L 9 7 L 9 6 Z" />
+ <path fill="black" d="M 0 8 L 1 8 L 1 9 L 0 9 L 0 8 Z" />
+ <path fill="black" d="M 15 8 L 16 8 L 16 9 L 15 9 L 15 8 Z" />
+ <path fill="black" d="M 9 0 L 10 0 L 10 1 L 9 1 L 9 0 Z" />
<path fill="black" d="M 9 15 L 10 15 L 10 16 L 9 16 L 9 15 Z" />
- <path fill="black" d="M 10 2 L 11 2 L 11 3 L 10 3 L 10 2 Z" />
- <path fill="black" d="M 10 3 L 11 3 L 11 4 L 10 4 L 10 3 Z" />
- <path fill="black" d="M 10 4 L 11 4 L 11 5 L 10 5 L 10 4 Z" />
- <path fill="black" d="M 10 5 L 11 5 L 11 6 L 10 6 L 10 5 Z" />
- <path fill="black" d="M 10 6 L 11 6 L 11 7 L 10 7 L 10 6 Z" />
- <path fill="black" d="M 11 6 L 12 6 L 12 7 L 11 7 L 11 6 Z" />
- <path fill="black" d="M 11 8 L 12 8 L 12 9 L 11 9 L 11 8 Z" />
+ <path fill="black" d="M 0 9 L 1 9 L 1 10 L 0 10 L 0 9 Z" />
+ <path fill="black" d="M 15 9 L 16 9 L 16 10 L 15 10 L 15 9 Z" />
+ <path fill="black" d="M 10 0 L 11 0 L 11 1 L 10 1 L 10 0 Z" />
<path fill="black" d="M 10 15 L 11 15 L 11 16 L 10 16 L 10 15 Z" />
- <path fill="black" d="M 11 10 L 12 10 L 12 11 L 11 11 L 11 10 Z" />
- <path fill="black" d="M 11 12 L 12 12 L 12 13 L 11 13 L 11 12 Z" />
- <path fill="black" d="M 11 14 L 12 14 L 12 15 L 11 15 L 11 14 Z" />
+ <path fill="black" d="M 0 10 L 1 10 L 1 11 L 0 11 L 0 10 Z" />
+ <path fill="black" d="M 15 10 L 16 10 L 16 11 L 15 11 L 15 10 Z" />
+ <path fill="black" d="M 11 0 L 12 0 L 12 1 L 11 1 L 11 0 Z" />
<path fill="black" d="M 11 15 L 12 15 L 12 16 L 11 16 L 11 15 Z" />
- <path fill="black" d="M 12 6 L 13 6 L 13 7 L 12 7 L 12 6 Z" />
- <path fill="black" d="M 12 8 L 13 8 L 13 9 L 12 9 L 12 8 Z" />
- <path fill="black" d="M 12 10 L 13 10 L 13 11 L 12 11 L 12 10 Z" />
- <path fill="black" d="M 12 12 L 13 12 L 13 13 L 12 13 L 12 12 Z" />
- <path fill="black" d="M 12 14 L 13 14 L 13 15 L 12 15 L 12 14 Z" />
- <path fill="black" d="M 13 6 L 14 6 L 14 7 L 13 7 L 13 6 Z" />
- <path fill="black" d="M 13 8 L 14 8 L 14 9 L 13 9 L 13 8 Z" />
- <path fill="black" d="M 13 10 L 14 10 L 14 11 L 13 11 L 13 10 Z" />
- <path fill="black" d="M 13 12 L 14 12 L 14 13 L 13 13 L 13 12 Z" />
- <path fill="black" d="M 13 13 L 14 13 L 14 14 L 13 14 L 13 13 Z" />
- <path fill="black" d="M 13 14 L 14 14 L 14 15 L 13 15 L 13 14 Z" />
- <path fill="black" d="M 14 7 L 15 7 L 15 8 L 14 8 L 14 7 Z" />
- <path fill="black" d="M 14 8 L 15 8 L 15 9 L 14 9 L 14 8 Z" />
- <path fill="black" d="M 14 9 L 15 9 L 15 10 L 14 10 L 14 9 Z" />
- <path fill="black" d="M 14 10 L 15 10 L 15 11 L 14 11 L 14 10 Z" />
- <path fill="black" d="M 14 11 L 15 11 L 15 12 L 14 12 L 14 11 Z" />
- <path fill="black" d="M 14 12 L 15 12 L 15 13 L 14 13 L 14 12 Z" />
+ <path fill="black" d="M 0 11 L 1 11 L 1 12 L 0 12 L 0 11 Z" />
+ <path fill="black" d="M 15 11 L 16 11 L 16 12 L 15 12 L 15 11 Z" />
+ <path fill="black" d="M 12 0 L 13 0 L 13 1 L 12 1 L 12 0 Z" />
+ <path fill="black" d="M 12 15 L 13 15 L 13 16 L 12 16 L 12 15 Z" />
+ <path fill="black" d="M 0 12 L 1 12 L 1 13 L 0 13 L 0 12 Z" />
+ <path fill="black" d="M 15 12 L 16 12 L 16 13 L 15 13 L 15 12 Z" />
+ <path fill="black" d="M 13 0 L 14 0 L 14 1 L 13 1 L 13 0 Z" />
+ <path fill="black" d="M 13 15 L 14 15 L 14 16 L 13 16 L 13 15 Z" />
+ <path fill="black" d="M 0 13 L 1 13 L 1 14 L 0 14 L 0 13 Z" />
+ <path fill="black" d="M 15 13 L 16 13 L 16 14 L 15 14 L 15 13 Z" />
+ <path fill="black" d="M 14 0 L 15 0 L 15 1 L 14 1 L 14 0 Z" />
+ <path fill="black" d="M 14 15 L 15 15 L 15 16 L 14 16 L 14 15 Z" />
+ <path fill="black" d="M 0 14 L 1 14 L 1 15 L 0 15 L 0 14 Z" />
+ <path fill="black" d="M 15 14 L 16 14 L 16 15 L 15 15 L 15 14 Z" />
+ <path fill="black" d="M 15 15 L 16 15 L 16 16 L 15 16 L 15 15 Z" />
+ <path fill="#1F6F5B" d="M 4 7 L 5 7 L 5 8 L 4 8 L 4 7 Z" />
+ <path fill="#1F6F5B" d="M 5 7 L 6 7 L 6 8 L 5 8 L 5 7 Z" />
+ <path fill="#1F6F5B" d="M 3 8 L 4 8 L 4 9 L 3 9 L 3 8 Z" />
+ <path fill="#1F6F5B" d="M 6 8 L 7 8 L 7 9 L 6 9 L 6 8 Z" />
+ <path fill="#1F6F5B" d="M 3 9 L 4 9 L 4 10 L 3 10 L 3 9 Z" />
+ <path fill="#1F6F5B" d="M 4 9 L 5 9 L 5 10 L 4 10 L 4 9 Z" />
+ <path fill="#1F6F5B" d="M 5 9 L 6 9 L 6 10 L 5 10 L 5 9 Z" />
+ <path fill="#1F6F5B" d="M 3 10 L 4 10 L 4 11 L 3 11 L 3 10 Z" />
+ <path fill="#1F6F5B" d="M 4 11 L 5 11 L 5 12 L 4 12 L 4 11 Z" />
+ <path fill="#1F6F5B" d="M 5 11 L 6 11 L 6 12 L 5 12 L 5 11 Z" />
+ <path fill="#1F6F5B" d="M 8 7 L 9 7 L 9 8 L 8 8 L 8 7 Z" />
+ <path fill="#1F6F5B" d="M 11 7 L 12 7 L 12 8 L 11 8 L 11 7 Z" />
+ <path fill="#1F6F5B" d="M 8 8 L 9 8 L 9 9 L 8 9 L 8 8 Z" />
+ <path fill="#1F6F5B" d="M 11 8 L 12 8 L 12 9 L 11 9 L 11 8 Z" />
+ <path fill="#1F6F5B" d="M 8 9 L 9 9 L 9 10 L 8 10 L 8 9 Z" />
+ <path fill="#1F6F5B" d="M 11 9 L 12 9 L 12 10 L 11 10 L 11 9 Z" />
+ <path fill="#1F6F5B" d="M 8 10 L 9 10 L 9 11 L 8 11 L 8 10 Z" />
+ <path fill="#1F6F5B" d="M 11 10 L 12 10 L 12 11 L 11 11 L 11 10 Z" />
+ <path fill="#1F6F5B" d="M 9 11 L 10 11 L 10 12 L 9 12 L 9 11 Z" />
+ <path fill="#1F6F5B" d="M 10 11 L 11 11 L 11 12 L 10 12 L 10 11 Z" />
+ <path fill="#1F6F5B" d="M 11 11 L 12 11 L 12 12 L 11 12 L 11 11 Z" />
</svg>
diff --git a/src/content/img/thumb/dark.svg b/src/content/img/thumb/dark.svg
new file mode 100644
index 0000000..fe512a7
--- /dev/null
+++ b/src/content/img/thumb/dark.svg
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
+ <path fill="#5CC2A2" d="M 0 8 L 1 8 L 1 9 L 0 9 L 0 8 Z" />
+ <path fill="#5CC2A2" d="M 0 13 L 1 13 L 1 14 L 0 14 L 0 13 Z" />
+ <path fill="#5CC2A2" d="M 1 8 L 2 8 L 2 9 L 1 9 L 1 8 Z" />
+ <path fill="#5CC2A2" d="M 1 13 L 2 13 L 2 14 L 1 14 L 1 13 Z" />
+ <path fill="#5CC2A2" d="M 2 8 L 3 8 L 3 9 L 2 9 L 2 8 Z" />
+ <path fill="#5CC2A2" d="M 2 13 L 3 13 L 3 14 L 2 14 L 2 13 Z" />
+ <path fill="#5CC2A2" d="M 3 8 L 4 8 L 4 9 L 3 9 L 3 8 Z" />
+ <path fill="#5CC2A2" d="M 3 13 L 4 13 L 4 14 L 3 14 L 3 13 Z" />
+ <path fill="#5CC2A2" d="M 4 7 L 5 7 L 5 8 L 4 8 L 4 7 Z" />
+ <path fill="#5CC2A2" d="M 4 8 L 5 8 L 5 9 L 4 9 L 4 8 Z" />
+ <path fill="#5CC2A2" d="M 4 13 L 5 13 L 5 14 L 4 14 L 4 13 Z" />
+ <path fill="#5CC2A2" d="M 5 6 L 6 6 L 6 7 L 5 7 L 5 6 Z" />
+ <path fill="#5CC2A2" d="M 5 7 L 6 7 L 6 8 L 5 8 L 5 7 Z" />
+ <path fill="#5CC2A2" d="M 5 13 L 6 13 L 6 14 L 5 14 L 5 13 Z" />
+ <path fill="#5CC2A2" d="M 6 5 L 7 5 L 7 6 L 6 6 L 6 5 Z" />
+ <path fill="#5CC2A2" d="M 6 6 L 7 6 L 7 7 L 6 7 L 6 6 Z" />
+ <path fill="#5CC2A2" d="M 6 14 L 7 14 L 7 15 L 6 15 L 6 14 Z" />
+ <path fill="#5CC2A2" d="M 7 1 L 8 1 L 8 2 L 7 2 L 7 1 Z" />
+ <path fill="#5CC2A2" d="M 7 14 L 8 14 L 8 15 L 7 15 L 7 14 Z" />
+ <path fill="#5CC2A2" d="M 7 15 L 8 15 L 8 16 L 7 16 L 7 15 Z" />
+ <path fill="#5CC2A2" d="M 7 2 L 8 2 L 8 3 L 7 3 L 7 2 Z" />
+ <path fill="#5CC2A2" d="M 7 3 L 8 3 L 8 4 L 7 4 L 7 3 Z" />
+ <path fill="#5CC2A2" d="M 7 4 L 8 4 L 8 5 L 7 5 L 7 4 Z" />
+ <path fill="#5CC2A2" d="M 7 5 L 8 5 L 8 6 L 7 6 L 7 5 Z" />
+ <path fill="#5CC2A2" d="M 8 1 L 9 1 L 9 2 L 8 2 L 8 1 Z" />
+ <path fill="#5CC2A2" d="M 8 15 L 9 15 L 9 16 L 8 16 L 8 15 Z" />
+ <path fill="#5CC2A2" d="M 9 1 L 10 1 L 10 2 L 9 2 L 9 1 Z" />
+ <path fill="#5CC2A2" d="M 9 2 L 10 2 L 10 3 L 9 3 L 9 2 Z" />
+ <path fill="#5CC2A2" d="M 9 6 L 10 6 L 10 7 L 9 7 L 9 6 Z" />
+ <path fill="#5CC2A2" d="M 9 15 L 10 15 L 10 16 L 9 16 L 9 15 Z" />
+ <path fill="#5CC2A2" d="M 10 2 L 11 2 L 11 3 L 10 3 L 10 2 Z" />
+ <path fill="#5CC2A2" d="M 10 3 L 11 3 L 11 4 L 10 4 L 10 3 Z" />
+ <path fill="#5CC2A2" d="M 10 4 L 11 4 L 11 5 L 10 5 L 10 4 Z" />
+ <path fill="#5CC2A2" d="M 10 5 L 11 5 L 11 6 L 10 6 L 10 5 Z" />
+ <path fill="#5CC2A2" d="M 10 6 L 11 6 L 11 7 L 10 7 L 10 6 Z" />
+ <path fill="#5CC2A2" d="M 11 6 L 12 6 L 12 7 L 11 7 L 11 6 Z" />
+ <path fill="#5CC2A2" d="M 11 8 L 12 8 L 12 9 L 11 9 L 11 8 Z" />
+ <path fill="#5CC2A2" d="M 10 15 L 11 15 L 11 16 L 10 16 L 10 15 Z" />
+ <path fill="#5CC2A2" d="M 11 10 L 12 10 L 12 11 L 11 11 L 11 10 Z" />
+ <path fill="#5CC2A2" d="M 11 12 L 12 12 L 12 13 L 11 13 L 11 12 Z" />
+ <path fill="#5CC2A2" d="M 11 14 L 12 14 L 12 15 L 11 15 L 11 14 Z" />
+ <path fill="#5CC2A2" d="M 11 15 L 12 15 L 12 16 L 11 16 L 11 15 Z" />
+ <path fill="#5CC2A2" d="M 12 6 L 13 6 L 13 7 L 12 7 L 12 6 Z" />
+ <path fill="#5CC2A2" d="M 12 8 L 13 8 L 13 9 L 12 9 L 12 8 Z" />
+ <path fill="#5CC2A2" d="M 12 10 L 13 10 L 13 11 L 12 11 L 12 10 Z" />
+ <path fill="#5CC2A2" d="M 12 12 L 13 12 L 13 13 L 12 13 L 12 12 Z" />
+ <path fill="#5CC2A2" d="M 12 14 L 13 14 L 13 15 L 12 15 L 12 14 Z" />
+ <path fill="#5CC2A2" d="M 13 6 L 14 6 L 14 7 L 13 7 L 13 6 Z" />
+ <path fill="#5CC2A2" d="M 13 8 L 14 8 L 14 9 L 13 9 L 13 8 Z" />
+ <path fill="#5CC2A2" d="M 13 10 L 14 10 L 14 11 L 13 11 L 13 10 Z" />
+ <path fill="#5CC2A2" d="M 13 12 L 14 12 L 14 13 L 13 13 L 13 12 Z" />
+ <path fill="#5CC2A2" d="M 13 13 L 14 13 L 14 14 L 13 14 L 13 13 Z" />
+ <path fill="#5CC2A2" d="M 13 14 L 14 14 L 14 15 L 13 15 L 13 14 Z" />
+ <path fill="#5CC2A2" d="M 14 7 L 15 7 L 15 8 L 14 8 L 14 7 Z" />
+ <path fill="#5CC2A2" d="M 14 8 L 15 8 L 15 9 L 14 9 L 14 8 Z" />
+ <path fill="#5CC2A2" d="M 14 9 L 15 9 L 15 10 L 14 10 L 14 9 Z" />
+ <path fill="#5CC2A2" d="M 14 10 L 15 10 L 15 11 L 14 11 L 14 10 Z" />
+ <path fill="#5CC2A2" d="M 14 11 L 15 11 L 15 12 L 14 12 L 14 11 Z" />
+ <path fill="#5CC2A2" d="M 14 12 L 15 12 L 15 13 L 14 13 L 14 12 Z" />
+</svg>
diff --git a/src/content/img/thumb/light.svg b/src/content/img/thumb/light.svg
new file mode 100644
index 0000000..2561eea
--- /dev/null
+++ b/src/content/img/thumb/light.svg
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
+ <path fill="#1F6F5B" d="M 0 8 L 1 8 L 1 9 L 0 9 L 0 8 Z" />
+ <path fill="#1F6F5B" d="M 0 13 L 1 13 L 1 14 L 0 14 L 0 13 Z" />
+ <path fill="#1F6F5B" d="M 1 8 L 2 8 L 2 9 L 1 9 L 1 8 Z" />
+ <path fill="#1F6F5B" d="M 1 13 L 2 13 L 2 14 L 1 14 L 1 13 Z" />
+ <path fill="#1F6F5B" d="M 2 8 L 3 8 L 3 9 L 2 9 L 2 8 Z" />
+ <path fill="#1F6F5B" d="M 2 13 L 3 13 L 3 14 L 2 14 L 2 13 Z" />
+ <path fill="#1F6F5B" d="M 3 8 L 4 8 L 4 9 L 3 9 L 3 8 Z" />
+ <path fill="#1F6F5B" d="M 3 13 L 4 13 L 4 14 L 3 14 L 3 13 Z" />
+ <path fill="#1F6F5B" d="M 4 7 L 5 7 L 5 8 L 4 8 L 4 7 Z" />
+ <path fill="#1F6F5B" d="M 4 8 L 5 8 L 5 9 L 4 9 L 4 8 Z" />
+ <path fill="#1F6F5B" d="M 4 13 L 5 13 L 5 14 L 4 14 L 4 13 Z" />
+ <path fill="#1F6F5B" d="M 5 6 L 6 6 L 6 7 L 5 7 L 5 6 Z" />
+ <path fill="#1F6F5B" d="M 5 7 L 6 7 L 6 8 L 5 8 L 5 7 Z" />
+ <path fill="#1F6F5B" d="M 5 13 L 6 13 L 6 14 L 5 14 L 5 13 Z" />
+ <path fill="#1F6F5B" d="M 6 5 L 7 5 L 7 6 L 6 6 L 6 5 Z" />
+ <path fill="#1F6F5B" d="M 6 6 L 7 6 L 7 7 L 6 7 L 6 6 Z" />
+ <path fill="#1F6F5B" d="M 6 14 L 7 14 L 7 15 L 6 15 L 6 14 Z" />
+ <path fill="#1F6F5B" d="M 7 1 L 8 1 L 8 2 L 7 2 L 7 1 Z" />
+ <path fill="#1F6F5B" d="M 7 14 L 8 14 L 8 15 L 7 15 L 7 14 Z" />
+ <path fill="#1F6F5B" d="M 7 15 L 8 15 L 8 16 L 7 16 L 7 15 Z" />
+ <path fill="#1F6F5B" d="M 7 2 L 8 2 L 8 3 L 7 3 L 7 2 Z" />
+ <path fill="#1F6F5B" d="M 7 3 L 8 3 L 8 4 L 7 4 L 7 3 Z" />
+ <path fill="#1F6F5B" d="M 7 4 L 8 4 L 8 5 L 7 5 L 7 4 Z" />
+ <path fill="#1F6F5B" d="M 7 5 L 8 5 L 8 6 L 7 6 L 7 5 Z" />
+ <path fill="#1F6F5B" d="M 8 1 L 9 1 L 9 2 L 8 2 L 8 1 Z" />
+ <path fill="#1F6F5B" d="M 8 15 L 9 15 L 9 16 L 8 16 L 8 15 Z" />
+ <path fill="#1F6F5B" d="M 9 1 L 10 1 L 10 2 L 9 2 L 9 1 Z" />
+ <path fill="#1F6F5B" d="M 9 2 L 10 2 L 10 3 L 9 3 L 9 2 Z" />
+ <path fill="#1F6F5B" d="M 9 6 L 10 6 L 10 7 L 9 7 L 9 6 Z" />
+ <path fill="#1F6F5B" d="M 9 15 L 10 15 L 10 16 L 9 16 L 9 15 Z" />
+ <path fill="#1F6F5B" d="M 10 2 L 11 2 L 11 3 L 10 3 L 10 2 Z" />
+ <path fill="#1F6F5B" d="M 10 3 L 11 3 L 11 4 L 10 4 L 10 3 Z" />
+ <path fill="#1F6F5B" d="M 10 4 L 11 4 L 11 5 L 10 5 L 10 4 Z" />
+ <path fill="#1F6F5B" d="M 10 5 L 11 5 L 11 6 L 10 6 L 10 5 Z" />
+ <path fill="#1F6F5B" d="M 10 6 L 11 6 L 11 7 L 10 7 L 10 6 Z" />
+ <path fill="#1F6F5B" d="M 11 6 L 12 6 L 12 7 L 11 7 L 11 6 Z" />
+ <path fill="#1F6F5B" d="M 11 8 L 12 8 L 12 9 L 11 9 L 11 8 Z" />
+ <path fill="#1F6F5B" d="M 10 15 L 11 15 L 11 16 L 10 16 L 10 15 Z" />
+ <path fill="#1F6F5B" d="M 11 10 L 12 10 L 12 11 L 11 11 L 11 10 Z" />
+ <path fill="#1F6F5B" d="M 11 12 L 12 12 L 12 13 L 11 13 L 11 12 Z" />
+ <path fill="#1F6F5B" d="M 11 14 L 12 14 L 12 15 L 11 15 L 11 14 Z" />
+ <path fill="#1F6F5B" d="M 11 15 L 12 15 L 12 16 L 11 16 L 11 15 Z" />
+ <path fill="#1F6F5B" d="M 12 6 L 13 6 L 13 7 L 12 7 L 12 6 Z" />
+ <path fill="#1F6F5B" d="M 12 8 L 13 8 L 13 9 L 12 9 L 12 8 Z" />
+ <path fill="#1F6F5B" d="M 12 10 L 13 10 L 13 11 L 12 11 L 12 10 Z" />
+ <path fill="#1F6F5B" d="M 12 12 L 13 12 L 13 13 L 12 13 L 12 12 Z" />
+ <path fill="#1F6F5B" d="M 12 14 L 13 14 L 13 15 L 12 15 L 12 14 Z" />
+ <path fill="#1F6F5B" d="M 13 6 L 14 6 L 14 7 L 13 7 L 13 6 Z" />
+ <path fill="#1F6F5B" d="M 13 8 L 14 8 L 14 9 L 13 9 L 13 8 Z" />
+ <path fill="#1F6F5B" d="M 13 10 L 14 10 L 14 11 L 13 11 L 13 10 Z" />
+ <path fill="#1F6F5B" d="M 13 12 L 14 12 L 14 13 L 13 13 L 13 12 Z" />
+ <path fill="#1F6F5B" d="M 13 13 L 14 13 L 14 14 L 13 14 L 13 13 Z" />
+ <path fill="#1F6F5B" d="M 13 14 L 14 14 L 14 15 L 13 15 L 13 14 Z" />
+ <path fill="#1F6F5B" d="M 14 7 L 15 7 L 15 8 L 14 8 L 14 7 Z" />
+ <path fill="#1F6F5B" d="M 14 8 L 15 8 L 15 9 L 14 9 L 14 8 Z" />
+ <path fill="#1F6F5B" d="M 14 9 L 15 9 L 15 10 L 14 10 L 14 9 Z" />
+ <path fill="#1F6F5B" d="M 14 10 L 15 10 L 15 11 L 14 11 L 14 10 Z" />
+ <path fill="#1F6F5B" d="M 14 11 L 15 11 L 15 12 L 14 12 L 14 11 Z" />
+ <path fill="#1F6F5B" d="M 14 12 L 15 12 L 15 13 L 14 13 L 14 12 Z" />
+</svg>
diff --git a/src/content/img/wordmark/dark.svg b/src/content/img/wordmark/dark.svg
new file mode 100644
index 0000000..27ea302
--- /dev/null
+++ b/src/content/img/wordmark/dark.svg
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 46 9" width="46" height="9">
+ <path fill="#5CC2A2" d="M 2 3 L 3 3 L 3 4 L 2 4 L 2 3 Z" />
+ <path fill="#5CC2A2" d="M 3 3 L 4 3 L 4 4 L 3 4 L 3 3 Z" />
+ <path fill="#5CC2A2" d="M 1 4 L 2 4 L 2 5 L 1 5 L 1 4 Z" />
+ <path fill="#5CC2A2" d="M 4 4 L 5 4 L 5 5 L 4 5 L 4 4 Z" />
+ <path fill="#5CC2A2" d="M 1 5 L 2 5 L 2 6 L 1 6 L 1 5 Z" />
+ <path fill="#5CC2A2" d="M 2 5 L 3 5 L 3 6 L 2 6 L 2 5 Z" />
+ <path fill="#5CC2A2" d="M 3 5 L 4 5 L 4 6 L 3 6 L 3 5 Z" />
+ <path fill="#5CC2A2" d="M 1 6 L 2 6 L 2 7 L 1 7 L 1 6 Z" />
+ <path fill="#5CC2A2" d="M 2 7 L 3 7 L 3 8 L 2 8 L 2 7 Z" />
+ <path fill="#5CC2A2" d="M 3 7 L 4 7 L 4 8 L 3 8 L 3 7 Z" />
+ <path fill="#5CC2A2" d="M 6 3 L 7 3 L 7 4 L 6 4 L 6 3 Z" />
+ <path fill="#5CC2A2" d="M 9 3 L 10 3 L 10 4 L 9 4 L 9 3 Z" />
+ <path fill="#5CC2A2" d="M 6 4 L 7 4 L 7 5 L 6 5 L 6 4 Z" />
+ <path fill="#5CC2A2" d="M 9 4 L 10 4 L 10 5 L 9 5 L 9 4 Z" />
+ <path fill="#5CC2A2" d="M 6 5 L 7 5 L 7 6 L 6 6 L 6 5 Z" />
+ <path fill="#5CC2A2" d="M 9 5 L 10 5 L 10 6 L 9 6 L 9 5 Z" />
+ <path fill="#5CC2A2" d="M 6 6 L 7 6 L 7 7 L 6 7 L 6 6 Z" />
+ <path fill="#5CC2A2" d="M 9 6 L 10 6 L 10 7 L 9 7 L 9 6 Z" />
+ <path fill="#5CC2A2" d="M 7 7 L 8 7 L 8 8 L 7 8 L 7 7 Z" />
+ <path fill="#5CC2A2" d="M 8 7 L 9 7 L 9 8 L 8 8 L 8 7 Z" />
+ <path fill="#5CC2A2" d="M 9 7 L 10 7 L 10 8 L 9 8 L 9 7 Z" />
+ <path fill="white" d="M 12 3 L 13 3 L 13 4 L 12 4 L 12 3 Z" />
+ <path fill="white" d="M 13 3 L 14 3 L 14 4 L 13 4 L 13 3 Z" />
+ <path fill="white" d="M 14 4 L 15 4 L 15 5 L 14 5 L 14 4 Z" />
+ <path fill="white" d="M 12 5 L 13 5 L 13 6 L 12 6 L 12 5 Z" />
+ <path fill="white" d="M 13 5 L 14 5 L 14 6 L 13 6 L 13 5 Z" />
+ <path fill="white" d="M 14 5 L 15 5 L 15 6 L 14 6 L 14 5 Z" />
+ <path fill="white" d="M 11 6 L 12 6 L 12 7 L 11 7 L 11 6 Z" />
+ <path fill="white" d="M 14 6 L 15 6 L 15 7 L 14 7 L 14 6 Z" />
+ <path fill="white" d="M 12 7 L 13 7 L 13 8 L 12 8 L 12 7 Z" />
+ <path fill="white" d="M 13 7 L 14 7 L 14 8 L 13 8 L 13 7 Z" />
+ <path fill="white" d="M 14 7 L 15 7 L 15 8 L 14 8 L 14 7 Z" />
+ <path fill="white" d="M 16 3 L 17 3 L 17 4 L 16 4 L 16 3 Z" />
+ <path fill="white" d="M 17 3 L 18 3 L 18 4 L 17 4 L 17 3 Z" />
+ <path fill="white" d="M 18 3 L 19 3 L 19 4 L 18 4 L 18 3 Z" />
+ <path fill="white" d="M 16 4 L 17 4 L 17 5 L 16 5 L 16 4 Z" />
+ <path fill="white" d="M 19 4 L 20 4 L 20 5 L 19 5 L 19 4 Z" />
+ <path fill="white" d="M 16 5 L 17 5 L 17 6 L 16 6 L 16 5 Z" />
+ <path fill="white" d="M 19 5 L 20 5 L 20 6 L 19 6 L 19 5 Z" />
+ <path fill="white" d="M 16 6 L 17 6 L 17 7 L 16 7 L 16 6 Z" />
+ <path fill="white" d="M 19 6 L 20 6 L 20 7 L 19 7 L 19 6 Z" />
+ <path fill="white" d="M 16 7 L 17 7 L 17 8 L 16 8 L 16 7 Z" />
+ <path fill="white" d="M 19 7 L 20 7 L 20 8 L 19 8 L 19 7 Z" />
+ <path fill="white" d="M 24 1 L 25 1 L 25 2 L 24 2 L 24 1 Z" />
+ <path fill="white" d="M 24 2 L 25 2 L 25 3 L 24 3 L 24 2 Z" />
+ <path fill="white" d="M 22 3 L 23 3 L 23 4 L 22 4 L 22 3 Z" />
+ <path fill="white" d="M 23 3 L 24 3 L 24 4 L 23 4 L 23 3 Z" />
+ <path fill="white" d="M 24 3 L 25 3 L 25 4 L 24 4 L 24 3 Z" />
+ <path fill="white" d="M 21 4 L 22 4 L 22 5 L 21 5 L 21 4 Z" />
+ <path fill="white" d="M 24 4 L 25 4 L 25 5 L 24 5 L 24 4 Z" />
+ <path fill="white" d="M 21 5 L 22 5 L 22 6 L 21 6 L 21 5 Z" />
+ <path fill="white" d="M 24 5 L 25 5 L 25 6 L 24 6 L 24 5 Z" />
+ <path fill="white" d="M 21 6 L 22 6 L 22 7 L 21 7 L 21 6 Z" />
+ <path fill="white" d="M 24 6 L 25 6 L 25 7 L 24 7 L 24 6 Z" />
+ <path fill="white" d="M 22 7 L 23 7 L 23 8 L 22 8 L 22 7 Z" />
+ <path fill="white" d="M 23 7 L 24 7 L 24 8 L 23 8 L 23 7 Z" />
+ <path fill="white" d="M 24 7 L 25 7 L 25 8 L 24 8 L 24 7 Z" />
+ <path fill="white" d="M 26 3 L 27 3 L 27 4 L 26 4 L 26 3 Z" />
+ <path fill="white" d="M 28 3 L 29 3 L 29 4 L 28 4 L 28 3 Z" />
+ <path fill="white" d="M 29 3 L 30 3 L 30 4 L 29 4 L 29 3 Z" />
+ <path fill="white" d="M 26 4 L 27 4 L 27 5 L 26 5 L 26 4 Z" />
+ <path fill="white" d="M 27 4 L 28 4 L 28 5 L 27 5 L 27 4 Z" />
+ <path fill="white" d="M 26 5 L 27 5 L 27 6 L 26 6 L 26 5 Z" />
+ <path fill="white" d="M 26 6 L 27 6 L 27 7 L 26 7 L 26 6 Z" />
+ <path fill="white" d="M 26 7 L 27 7 L 27 8 L 26 8 L 26 7 Z" />
+ <path fill="white" d="M 32 3 L 33 3 L 33 4 L 32 4 L 32 3 Z" />
+ <path fill="white" d="M 33 3 L 34 3 L 34 4 L 33 4 L 33 3 Z" />
+ <path fill="white" d="M 31 4 L 32 4 L 32 5 L 31 5 L 31 4 Z" />
+ <path fill="white" d="M 34 4 L 35 4 L 35 5 L 34 5 L 34 4 Z" />
+ <path fill="white" d="M 31 5 L 32 5 L 32 6 L 31 6 L 31 5 Z" />
+ <path fill="white" d="M 32 5 L 33 5 L 33 6 L 32 6 L 32 5 Z" />
+ <path fill="white" d="M 33 5 L 34 5 L 34 6 L 33 6 L 33 5 Z" />
+ <path fill="white" d="M 31 6 L 32 6 L 32 7 L 31 7 L 31 6 Z" />
+ <path fill="white" d="M 32 7 L 33 7 L 33 8 L 32 8 L 32 7 Z" />
+ <path fill="white" d="M 33 7 L 34 7 L 34 8 L 33 8 L 33 7 Z" />
+ <path fill="white" d="M 36 1 L 37 1 L 37 2 L 36 2 L 36 1 Z" />
+ <path fill="white" d="M 36 2 L 37 2 L 37 3 L 36 3 L 36 2 Z" />
+ <path fill="white" d="M 36 3 L 37 3 L 37 4 L 36 4 L 36 3 Z" />
+ <path fill="white" d="M 37 3 L 38 3 L 38 4 L 37 4 L 37 3 Z" />
+ <path fill="white" d="M 38 3 L 39 3 L 39 4 L 38 4 L 38 3 Z" />
+ <path fill="white" d="M 36 4 L 37 4 L 37 5 L 36 5 L 36 4 Z" />
+ <path fill="white" d="M 39 4 L 40 4 L 40 5 L 39 5 L 39 4 Z" />
+ <path fill="white" d="M 36 5 L 37 5 L 37 6 L 36 6 L 36 5 Z" />
+ <path fill="white" d="M 39 5 L 40 5 L 40 6 L 39 6 L 39 5 Z" />
+ <path fill="white" d="M 36 6 L 37 6 L 37 7 L 36 7 L 36 6 Z" />
+ <path fill="white" d="M 39 6 L 40 6 L 40 7 L 39 7 L 39 6 Z" />
+ <path fill="white" d="M 36 7 L 37 7 L 37 8 L 36 8 L 36 7 Z" />
+ <path fill="white" d="M 39 7 L 40 7 L 40 8 L 39 8 L 39 7 Z" />
+ <path fill="white" d="M 42 7 L 43 7 L 43 8 L 42 8 L 42 7 Z" />
+ <path fill="white" d="M 43 7 L 44 7 L 44 8 L 43 8 L 43 7 Z" />
+ <path fill="white" d="M 44 7 L 45 7 L 45 8 L 44 8 L 44 7 Z" />
+</svg>
diff --git a/src/content/img/wordmark/light.svg b/src/content/img/wordmark/light.svg
new file mode 100644
index 0000000..e4d2b10
--- /dev/null
+++ b/src/content/img/wordmark/light.svg
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 46 9" width="46" height="9">
+ <path fill="#1F6F5B" d="M 2 3 L 3 3 L 3 4 L 2 4 L 2 3 Z" />
+ <path fill="#1F6F5B" d="M 3 3 L 4 3 L 4 4 L 3 4 L 3 3 Z" />
+ <path fill="#1F6F5B" d="M 1 4 L 2 4 L 2 5 L 1 5 L 1 4 Z" />
+ <path fill="#1F6F5B" d="M 4 4 L 5 4 L 5 5 L 4 5 L 4 4 Z" />
+ <path fill="#1F6F5B" d="M 1 5 L 2 5 L 2 6 L 1 6 L 1 5 Z" />
+ <path fill="#1F6F5B" d="M 2 5 L 3 5 L 3 6 L 2 6 L 2 5 Z" />
+ <path fill="#1F6F5B" d="M 3 5 L 4 5 L 4 6 L 3 6 L 3 5 Z" />
+ <path fill="#1F6F5B" d="M 1 6 L 2 6 L 2 7 L 1 7 L 1 6 Z" />
+ <path fill="#1F6F5B" d="M 2 7 L 3 7 L 3 8 L 2 8 L 2 7 Z" />
+ <path fill="#1F6F5B" d="M 3 7 L 4 7 L 4 8 L 3 8 L 3 7 Z" />
+ <path fill="#1F6F5B" d="M 6 3 L 7 3 L 7 4 L 6 4 L 6 3 Z" />
+ <path fill="#1F6F5B" d="M 9 3 L 10 3 L 10 4 L 9 4 L 9 3 Z" />
+ <path fill="#1F6F5B" d="M 6 4 L 7 4 L 7 5 L 6 5 L 6 4 Z" />
+ <path fill="#1F6F5B" d="M 9 4 L 10 4 L 10 5 L 9 5 L 9 4 Z" />
+ <path fill="#1F6F5B" d="M 6 5 L 7 5 L 7 6 L 6 6 L 6 5 Z" />
+ <path fill="#1F6F5B" d="M 9 5 L 10 5 L 10 6 L 9 6 L 9 5 Z" />
+ <path fill="#1F6F5B" d="M 6 6 L 7 6 L 7 7 L 6 7 L 6 6 Z" />
+ <path fill="#1F6F5B" d="M 9 6 L 10 6 L 10 7 L 9 7 L 9 6 Z" />
+ <path fill="#1F6F5B" d="M 7 7 L 8 7 L 8 8 L 7 8 L 7 7 Z" />
+ <path fill="#1F6F5B" d="M 8 7 L 9 7 L 9 8 L 8 8 L 8 7 Z" />
+ <path fill="#1F6F5B" d="M 9 7 L 10 7 L 10 8 L 9 8 L 9 7 Z" />
+ <path fill="black" d="M 12 3 L 13 3 L 13 4 L 12 4 L 12 3 Z" />
+ <path fill="black" d="M 13 3 L 14 3 L 14 4 L 13 4 L 13 3 Z" />
+ <path fill="black" d="M 14 4 L 15 4 L 15 5 L 14 5 L 14 4 Z" />
+ <path fill="black" d="M 12 5 L 13 5 L 13 6 L 12 6 L 12 5 Z" />
+ <path fill="black" d="M 13 5 L 14 5 L 14 6 L 13 6 L 13 5 Z" />
+ <path fill="black" d="M 14 5 L 15 5 L 15 6 L 14 6 L 14 5 Z" />
+ <path fill="black" d="M 11 6 L 12 6 L 12 7 L 11 7 L 11 6 Z" />
+ <path fill="black" d="M 14 6 L 15 6 L 15 7 L 14 7 L 14 6 Z" />
+ <path fill="black" d="M 12 7 L 13 7 L 13 8 L 12 8 L 12 7 Z" />
+ <path fill="black" d="M 13 7 L 14 7 L 14 8 L 13 8 L 13 7 Z" />
+ <path fill="black" d="M 14 7 L 15 7 L 15 8 L 14 8 L 14 7 Z" />
+ <path fill="black" d="M 16 3 L 17 3 L 17 4 L 16 4 L 16 3 Z" />
+ <path fill="black" d="M 17 3 L 18 3 L 18 4 L 17 4 L 17 3 Z" />
+ <path fill="black" d="M 18 3 L 19 3 L 19 4 L 18 4 L 18 3 Z" />
+ <path fill="black" d="M 16 4 L 17 4 L 17 5 L 16 5 L 16 4 Z" />
+ <path fill="black" d="M 19 4 L 20 4 L 20 5 L 19 5 L 19 4 Z" />
+ <path fill="black" d="M 16 5 L 17 5 L 17 6 L 16 6 L 16 5 Z" />
+ <path fill="black" d="M 19 5 L 20 5 L 20 6 L 19 6 L 19 5 Z" />
+ <path fill="black" d="M 16 6 L 17 6 L 17 7 L 16 7 L 16 6 Z" />
+ <path fill="black" d="M 19 6 L 20 6 L 20 7 L 19 7 L 19 6 Z" />
+ <path fill="black" d="M 16 7 L 17 7 L 17 8 L 16 8 L 16 7 Z" />
+ <path fill="black" d="M 19 7 L 20 7 L 20 8 L 19 8 L 19 7 Z" />
+ <path fill="black" d="M 24 1 L 25 1 L 25 2 L 24 2 L 24 1 Z" />
+ <path fill="black" d="M 24 2 L 25 2 L 25 3 L 24 3 L 24 2 Z" />
+ <path fill="black" d="M 22 3 L 23 3 L 23 4 L 22 4 L 22 3 Z" />
+ <path fill="black" d="M 23 3 L 24 3 L 24 4 L 23 4 L 23 3 Z" />
+ <path fill="black" d="M 24 3 L 25 3 L 25 4 L 24 4 L 24 3 Z" />
+ <path fill="black" d="M 21 4 L 22 4 L 22 5 L 21 5 L 21 4 Z" />
+ <path fill="black" d="M 24 4 L 25 4 L 25 5 L 24 5 L 24 4 Z" />
+ <path fill="black" d="M 21 5 L 22 5 L 22 6 L 21 6 L 21 5 Z" />
+ <path fill="black" d="M 24 5 L 25 5 L 25 6 L 24 6 L 24 5 Z" />
+ <path fill="black" d="M 21 6 L 22 6 L 22 7 L 21 7 L 21 6 Z" />
+ <path fill="black" d="M 24 6 L 25 6 L 25 7 L 24 7 L 24 6 Z" />
+ <path fill="black" d="M 22 7 L 23 7 L 23 8 L 22 8 L 22 7 Z" />
+ <path fill="black" d="M 23 7 L 24 7 L 24 8 L 23 8 L 23 7 Z" />
+ <path fill="black" d="M 24 7 L 25 7 L 25 8 L 24 8 L 24 7 Z" />
+ <path fill="black" d="M 26 3 L 27 3 L 27 4 L 26 4 L 26 3 Z" />
+ <path fill="black" d="M 28 3 L 29 3 L 29 4 L 28 4 L 28 3 Z" />
+ <path fill="black" d="M 29 3 L 30 3 L 30 4 L 29 4 L 29 3 Z" />
+ <path fill="black" d="M 26 4 L 27 4 L 27 5 L 26 5 L 26 4 Z" />
+ <path fill="black" d="M 27 4 L 28 4 L 28 5 L 27 5 L 27 4 Z" />
+ <path fill="black" d="M 26 5 L 27 5 L 27 6 L 26 6 L 26 5 Z" />
+ <path fill="black" d="M 26 6 L 27 6 L 27 7 L 26 7 L 26 6 Z" />
+ <path fill="black" d="M 26 7 L 27 7 L 27 8 L 26 8 L 26 7 Z" />
+ <path fill="black" d="M 32 3 L 33 3 L 33 4 L 32 4 L 32 3 Z" />
+ <path fill="black" d="M 33 3 L 34 3 L 34 4 L 33 4 L 33 3 Z" />
+ <path fill="black" d="M 31 4 L 32 4 L 32 5 L 31 5 L 31 4 Z" />
+ <path fill="black" d="M 34 4 L 35 4 L 35 5 L 34 5 L 34 4 Z" />
+ <path fill="black" d="M 31 5 L 32 5 L 32 6 L 31 6 L 31 5 Z" />
+ <path fill="black" d="M 32 5 L 33 5 L 33 6 L 32 6 L 32 5 Z" />
+ <path fill="black" d="M 33 5 L 34 5 L 34 6 L 33 6 L 33 5 Z" />
+ <path fill="black" d="M 31 6 L 32 6 L 32 7 L 31 7 L 31 6 Z" />
+ <path fill="black" d="M 32 7 L 33 7 L 33 8 L 32 8 L 32 7 Z" />
+ <path fill="black" d="M 33 7 L 34 7 L 34 8 L 33 8 L 33 7 Z" />
+ <path fill="black" d="M 36 1 L 37 1 L 37 2 L 36 2 L 36 1 Z" />
+ <path fill="black" d="M 36 2 L 37 2 L 37 3 L 36 3 L 36 2 Z" />
+ <path fill="black" d="M 36 3 L 37 3 L 37 4 L 36 4 L 36 3 Z" />
+ <path fill="black" d="M 37 3 L 38 3 L 38 4 L 37 4 L 37 3 Z" />
+ <path fill="black" d="M 38 3 L 39 3 L 39 4 L 38 4 L 38 3 Z" />
+ <path fill="black" d="M 36 4 L 37 4 L 37 5 L 36 5 L 36 4 Z" />
+ <path fill="black" d="M 39 4 L 40 4 L 40 5 L 39 5 L 39 4 Z" />
+ <path fill="black" d="M 36 5 L 37 5 L 37 6 L 36 6 L 36 5 Z" />
+ <path fill="black" d="M 39 5 L 40 5 L 40 6 L 39 6 L 39 5 Z" />
+ <path fill="black" d="M 36 6 L 37 6 L 37 7 L 36 7 L 36 6 Z" />
+ <path fill="black" d="M 39 6 L 40 6 L 40 7 L 39 7 L 39 6 Z" />
+ <path fill="black" d="M 36 7 L 37 7 L 37 8 L 36 8 L 36 7 Z" />
+ <path fill="black" d="M 39 7 L 40 7 L 40 8 L 39 8 L 39 7 Z" />
+ <path fill="black" d="M 42 7 L 43 7 L 43 8 L 42 8 L 42 7 Z" />
+ <path fill="black" d="M 43 7 L 44 7 L 44 8 L 43 8 L 43 7 Z" />
+ <path fill="black" d="M 44 7 L 45 7 L 45 8 L 44 8 L 44 7 Z" />
+</svg>
diff --git a/src/content/music/choro-da-saudade.ly b/src/content/music/choro-da-saudade.ly
new file mode 100644
index 0000000..2f6e686
--- /dev/null
+++ b/src/content/music/choro-da-saudade.ly
@@ -0,0 +1,233 @@
+\version "2.20.0"
+\pointAndClickOff
+#(ly:set-option 'embed-source-code #t)
+
+\include "common.ly.include"
+\include "guitar.ly.include"
+
+\header {
+ title = "Choro da Saudade"
+ composer = "Agustín Barrios"
+ copyright = \copyright
+ tagline = \tagline
+}
+
+globals = {
+ \clef "treble_8"
+ \key g \minor
+ \time 2/4
+ \set fingeringOrientations = #'(left)
+ \tempo Largo 4 = 50
+}
+
+upper = \relative c {
+ \globals
+
+ \repeat volta 2 {
+ <g-0>16 <d'-0> <bes'-2 ees-4> d, <bes'-2 d-3> d, d, d' |
+ <a-1> <d-0> <c'-4 ees-3> d, <c'-4 d-2> d, d, d' |
+ }
+
+ <g,-0>8. <d'-0>16 <bes'-2>\tenuto( <a-1>) a <g-0> |
+ \repeat volta 2 {
+ \mark \markup { \musicglyph #"scripts.segno" }
+ \repeat unfold 2 {
+ <ees'-2>4 <cis-1>16( <d-2>) <fis-1> <a-4> |
+ \pestanaSpan "III" <bes-4>8.\startTextSpan <g-1>16 <d-1> <bes-1> <g-3>( <d-0>)\stopTextSpan |
+ \pestanaSpan "V" <c-1>\startTextSpan <d'-2> <fis-3>( <g-4>) <a-1> <bes-2> <c-4> a\stopTextSpan |
+ }
+ \alternative {
+ {
+ <d-4>4\fermata <c-4>16( <bes-2>) <a-1> <g\2-4> |
+ <bes-2>4 <a-1>16( <bes-2>) <c-4> a |
+ \pestanaSpan "I" <g-4>4\startTextSpan <f-1>16 <ees-4> <d-3> <c-1>\stopTextSpan |
+ \pestanaSpan "III" <f-4>(\startTextSpan <d-1>) <bes-1> <g-1> <f-1>( <d-0>) <bes-1> <g-9>\stopTextSpan |
+ <fis-2>8.\fermata <d'-0>16 <bes'-2>\tenuto( <a-1>) a <g-0> |
+ }
+
+ {
+ <d''-4>4 \pestanaSpan "VII" <b-1>16\startTextSpan <g-2> <f-4> <d-1>\stopTextSpan |
+ \pestanaSpan "V" <c-1>\startTextSpan <g-1> <ees-4> <c-1> <a-3>( <bes-4>) <c-1> <ees-4>\stopTextSpan |
+ \pestanaSpan "III" <d-0>\startTextSpan <bes'-1> <d g-1>8 \appoggiatura { <d-1>16 <f-4> } <ees-2>16 <d-1> <c-3> <bes-1>\stopTextSpan |
+ <a,-1>( <cis-4>) <e-1> <g-0> <d-0> <fis-3> <a-1> <d-2> \mark \markup { \musicglyph #"scripts.coda" } |
+ }
+ }
+ }
+ \alternative {
+ { <g,-0> <d-0> g d bes'\tenuto( a) a g | }
+ { <g-0> <bes-2 d-3>8 <bes d>16 <bes d> <bes ees-4>8 <bes d>16 | }
+ }
+
+ % part B
+ r <c-4 d-2>8 <c d>16 r <bes-1 d-2>8 <bes d> 16 |
+ r <a-1 d-2>8 <a d>16 <a d> <a cis-1>8 <a d>16 |
+ r <b-0 g'-4>8 <b g'>16 r <c-1 g'-4>8 <c g'>16 |
+
+ \pestanaSpan "III" r\startTextSpan <d-1 g-1>8 <d g>16 <d g> <f-4 g-1>8 <e-3 g-1>16 |
+ r <ees-2 g-1>8 <ees g>16 r <ees g>8 <ees g>16\stopTextSpan |
+ \pestanaSpan "II" r\startTextSpan <c-4 ees-3 fis-1>8 <c ees fis>16 <c ees fis> <c ees g-2>8\stopTextSpan \pestanaSpan "III" <bes-1 d-1 bes'-4>16\startTextSpan |
+ r <c-3 d-1 a'-4>8 <c d a'>16 r <c-3 dis-2 a'-4>8 <c dis a'>16\stopTextSpan |
+
+ r <g-2 cis-3 bes'-4>8 <g cis bes'>16 r <g cis bes'>8 <g cis bes'>16 |
+ r <d'-2 g-3 bes-1>8 <d g bes>16 <d g bes> <d g c-4>8 <d g bes>16 |
+ r <cis-3 g'-4 a-2>8 <cis g' a>16 r <cis g' a>8 <cis g' a>16 |
+ \pestanaSpan "V" r\startTextSpan <c-1 fis-3 a-1>8 <c fis a>16 <c fis a> <c fis bes-2>8 <c fis a>16\stopTextSpan |
+
+ r <b-3 f'-4 g-2>8 <b f' g>16 r <b f' g>8 <b f' g>16 |
+ \pestanaSpan "III" r\startTextSpan <bes-1 e-3 g-1>8 <bes e g>16 <bes e g> <bes e a-4>8 <bes e g>16\stopTextSpan |
+ \pestanaSpan "I" r\startTextSpan <a-2 c-1 f-1>8 <a c f>16\stopTextSpan r <f'-1 bes-2 f'-4>8 <f bes f'>16 |
+ r <d-1 gis-4>8 <d gis>16 r <cis-2 g'-3 e'-4>8 <cis g' e'>16 |
+
+ \pestanaSpan "V" r\startTextSpan <c-1 fis-3 a-1>8 <c fis a>16\stopTextSpan r <b-0 f'-3 d'-4>8 <b f' d'>16 |
+ \pestanaSpan "V" r\startTextSpan <g-1 ees'-3 c'-4>8 <g ees' c'>16 <g ees' c'> <g ees' b'-2>8 <g ees' c'>16\stopTextSpan |
+ \pestanaSpan "III" r\startTextSpan <bes-1 d-1 bes'-4>8 <bes d bes'>16 <bes d bes'> <bes d a'-3>8 <bes d g-1>16\stopTextSpan |
+ r <g-2 cis-3 bes'-4>8 <g cis bes'>16 \pestanaSpan "III" r\startTextSpan <c-3 d-1 a'-4>8 <c d a'>16 |
+
+ <bes-1 d-1 g-1>8.\stopTextSpan <d,-0>16 <bes'-2>\tenuto( <a-1>) a <g-0> |
+
+ \bar "||"
+ <g-0>8. <d'-1\3>16\mordent <b-3\4> d <g-2> <b-1>
+ \key g \major
+ \repeat volta 2 {
+ <g,,-0>16 <d'''-4> <b,-0 e-2> g, <gis-1\6> <d'''-4> <b,-0 f'-3> gis, |
+ <a-1\6> <d''-4> <c,-3 fis-1> \corda "5" <d,-1>\startTextSpan <e-3>\stopTextSpan <g-0> <b-4\4> <d-1> |
+ <g,,-0> <g''-1> <bes,-2 d-1> g, <g'-4\4> <fis'-1> <bes,-2 cis-1> g |
+
+ <fis-3> <e'-0> <a,-1 d-2> e' <fis-2\3> <a-1> <c-4> <d-1> |
+ <ais,,-1> <e'''-4> <cis,-2 g'-3> ais, \pestanaSpan "VII" <b-3>\startTextSpan <d''-4> <d,-1 fis-1> b,\stopTextSpan |
+ <cis-1> <cis''-4> <ais,-2 e'-3> cis, <d-1> <b''-1> <b,-3 d-1> d, |
+ <e-1> <d''-2> <b,-0 cis-3> e,, <fis-3> <fis''-1> <e,-1 ais-2> fis' |
+
+ \rall <b,,-3>\startTextSpan <fis'-4> <d'-2> <cis-1> <c-4>\fermata <d-2>\fermata <fis-1>\fermata <a-4>\fermata\glissando |
+ \once \override NoteColumn.glissando-skip = ##t <g,,-0>16\stopTextSpan <d'''-4> <b,-0 e-2> g, <gis-1\6> <d'''-4> <b,-0 f'-3> gis, |
+ <a-1\6> <d''-4> <c,-3 fis-1> <c'-2>\mordent <a-4\2> <fis-1> <d-1\3> <c-4\4> |
+ <b-1> <f'-2 e'-4>8 <f e'>16 <g,,-0> <f''-1 b-2 g'-4>8 <f b e-3>16 |
+
+ <a,-3>16 <c-1 e-1 c'-4>8. r4 |
+ \pestanaSpan "VII" <a,-1\6>16\startTextSpan <c''-3> <e,-4 g-2> a,,\stopTextSpan \pestanaSpan "VI" <cis-1\5>\startTextSpan <ais''-1> <cis,-1 g'-4> cis,\stopTextSpan |
+ <d-0> <b''-1>8 <d,-1 g-2>16 r4 |
+ <e,,-1>16 <g'-4 b-3 cis-1>8 <g b cis>16 <d,-0> <c''-1 fis-3 b-4>8 <c fis a-1>16 |
+ }
+ \alternative {
+ { <b-2 d-1 g-1>8. <d-1\3>16\mordent <b-3\4> <d-1> <g-2> <b-1> | }
+ { <b,-2 d-1 g-1>\tenuto <bes-2 d-3>8 <bes d>16 <bes d> <bes ees-4>8 <bes d>16 | }
+ }
+
+ \bar "||"
+
+ <g-0>4 <b'-2 d-3 g-4>
+
+ \bar "|."
+}
+
+lower = \relative c' {
+ \globals
+ \set stringNumberOrientations = #'(down)
+
+ \repeat volta 2 {
+ g,4. d8 |
+ a'4. d,8 |
+ }
+
+ g8. r16 r4 |
+ \repeat volta 2 {
+ r16 <a-1> <g'-3> <c-4> d,,4 |
+ <g-0>8 <bes'-1 d-1> r4 |
+ <c,-1>2 |
+
+ r16 <bes-2\6> <d'-1> <g-3> r4 |
+ r16 <d,,-0> <d'-0> <c'-1> r4 |
+ r16 <ees,,-1> <ees'-1> <a-2> r4 |
+ d,,2 |
+
+ fis8. r16 r4 |
+ r16 <a-1> <g'-3> <c-4> d,,4 |
+ <g-0>8 <bes'-1 d-1> r4 |
+ <c,-1>2 |
+
+ r16 <b-2\6> <f''-3> <g-1> r4 |
+ r2 |
+ d,4 d, |
+ r d |
+ }
+ \alternative {
+ { <g-0> r | }
+ { <g-0>4. d8 | }
+ }
+
+ % part B
+ <a'-1>4 <g-0> |
+ <fis-3>4. <d-0>8 |
+ <g-0>4 <a-2>\glissando |
+ <b-2>4. <g-0>8 |
+
+ <c-3>4 <bes-1> |
+ <a-1>4. <g-0>8 |
+ <fis-2>4\glissando <f-1>\glissando |
+ <e-1>\glissando <ees-1> |
+
+ <d-0>4. d8 |
+ \slashedGrace cis'\glissando <e-1\5>4\glissando <ees-1> |
+ <d-0>4. d8 |
+ <d-1\5>4 <des-1> |
+
+ <c-2>4. <bes-1>8 |
+ <ees-1>4 <d-0> |
+ <e-3\5> <a,-1\6> |
+ <d,-0> <g-0> |
+
+ <c-1>4. c8 |
+ <d-0>4. d8 |
+ <e,-1>4 <fis-2> |
+ <g-0>8. r16 r4 |
+
+ \bar "||"
+
+ \repeat volta 2 {
+ <g-0>4 b' |
+ g,8. g16 gis8. gis16 |
+ a8. d16 e4 |
+ g,8. g16 g'8. g16 |
+
+ fis8 a fis'4 |
+ ais,,8. ais16 b8. b16 |
+ cis8. cis16 d8. d16 |
+ e8. <e,-1>32( <eis-2>) fis8 e' |
+
+ b4 <d, fis'-3> |
+ g8. g16 gis8. gis16 |
+ a4 r |
+ b' g, |
+
+ \corda "4" a'8.\startTextSpan <gis-2>16 <b-3> <a-1>8\stopTextSpan \once \set stringNumberOrientations = #'(up) <e-3\5>16 |
+ a,8. a16 cis8. cis16 |
+ d4 \corda "5" <cis-1>16\startTextSpan <e-4> <d-2>\mordent <b-3>\stopTextSpan |
+ e,4 d |
+ }
+ \alternative {
+ { <g-0> b' | }
+ { <g,-0>4. <d-0>8 | }
+ }
+
+ \bar "||"
+
+ <g-0>4 <g-0 d'-0>
+
+ \bar "|."
+}
+
+staff = \new Staff <<
+ \upper
+ \\
+ \lower
+>>
+
+\score {
+ \staff
+ \layout { }
+}
+
+\score {
+ \unfoldRepeats \staff
+ \midi { }
+}
diff --git a/src/content/music/choro-da-saudade.ogg b/src/content/music/choro-da-saudade.ogg
new file mode 100644
index 0000000..c6a89cf
--- /dev/null
+++ b/src/content/music/choro-da-saudade.ogg
Binary files differ
diff --git a/src/content/music/common.ly.include b/src/content/music/common.ly.include
new file mode 100644
index 0000000..b6ba93a
--- /dev/null
+++ b/src/content/music/common.ly.include
@@ -0,0 +1,15 @@
+copyright = \markup {
+ \with-url #"https://creativecommons.org/licenses/by-sa/4.0/" \line {
+ CC BY-SA 4.0
+ }
+}
+tagline = \markup {
+ \with-url #"https://euandre.org/music.html" \line {
+ euandre.org/music.html
+ }
+}
+
+rall = {
+ \override TextSpanner.bound-details.left.text = "rall."
+ \override TextSpanner.bound-details.right.text = "a tempo"
+}
diff --git a/src/content/music/dengoso.ly b/src/content/music/dengoso.ly
new file mode 100644
index 0000000..b5a3fae
--- /dev/null
+++ b/src/content/music/dengoso.ly
@@ -0,0 +1,310 @@
+\version "2.20.0"
+\pointAndClickOff
+#(ly:set-option 'embed-source-code #t)
+
+\include "common.ly.include"
+\include "guitar.ly.include"
+
+\header {
+ title = "Dengoso"
+ composer = "João Pernambuco"
+ copyright = \copyright
+ tagline = \tagline
+}
+
+globals = {
+ \clef "treble_8"
+ \key d \major
+ \time 2/4
+ \set fingeringOrientations = #'(left)
+ \tempo Andante 4 = 100
+}
+
+upper = \relative c {
+ \globals
+
+ r16 <d-0>8 <e-1>16 <fis-3> <e-1>8 <d-0>16 |
+ \corda "4" <a'-3>8.\startTextSpan a16 <g-1>8. g16 |
+ <fis-3>8. fis16 <f-2>8. <e-1>16\stopTextSpan |
+ r2 |
+
+ r16 d8 e16 fis e8 d16 |
+ \corda "4" a'8.\startTextSpan a16 g8. g16 |
+ fis8. fis16 f8. e16\stopTextSpan |
+ r2 |
+
+ <d'-2>8 \pestanaSpan "II" <fis,-3 a-1>\startTextSpan fis' d16\stopTextSpan <e-0>~ |
+ e2 |
+ d8 \pestanaSpan "II" <fis, a>\startTextSpan fis' d16\stopTextSpan e~ |
+ e2 |
+
+ \repeat volta 2 {
+ \pestanaSpan "V" a16\startTextSpan <a,-3 d-4>8 <a d>16 r16 <g'-4>8 <fis-3>16 |
+ a16 <g,-1 cis-2>8 <g cis>16 r16 <cis-2> <fis-4> <cis-2>\stopTextSpan |
+ <a-1 e'-0>16 <a d>8 \pestanaSingle "II" <a cis>16 <a b>8 a |
+ <b-0>16 <cis,-3 g'-4>8 <cis g'>16 \pestanaSpan "II" cis'8\startTextSpan <g a>\stopTextSpan |
+
+ <d'-2>16 <fis,-3 a-1>8 <fis a>16 <fis'-4>8 <a,-3 c-2> |
+ <e'-0>16 <a,-2 cis-3>8 <a cis>16 r16 <g-0> <cis-3> <e-0> |
+ <d gis d'>16 <d gis d'>8 <d gis d'>16 <d gis cis>8 <d gis b> |
+ <e'-4>16 <cis,-2 g'-3>8 <cis g'>16 <cis g' b\2>8 <cis-2 g'-3 a-1> |
+
+ \pestanaSpan "V" a'16\startTextSpan <a, d>8 <a d>16 r16 <g'>8 <fis>16 |
+ a16 <g, cis>8 <g cis>16 r16 <cis> <fis> <cis>\stopTextSpan |
+ <a e'>16 <a d>8 \pestanaSingle "II" <a cis>16 <a b>8 a |
+ <b>16 <cis, g'>8 <cis g'>16 \pestanaSpan "II" cis'8\startTextSpan <g a>\stopTextSpan |
+
+ d'16 <fis, a>8 <fis a>16 dis'8 <fis, a> |
+ <e'-0>8 <b-3 d-2>16 <ais-2 cis-1> <g b e> <d'-2>( <b-0>) <g-0> |
+ <fis-3> <d'-2> <a-1> <fis-3> <e-1> <cis'-2> <g-0> <e-1>
+ }
+ \alternative {
+ { d, \pestanaSpan "II" a''\startTextSpan d fis g fis g gis\stopTextSpan | }
+ { r4 \pestanaSingle "VII" <d fis d'> | }
+ }
+
+ \repeat volta 2 {
+ \key d \minor
+
+ <a-3>16 a <d-4 f-2> a a a <d f> a |
+ <g-3> g <bes-1 e-4> g g g <bes e> g |
+ <f-3> f <a-2 d-4> f f f <a d> f |
+ r <g-0 cis-2> <g cis> e' r <bes-3>( <a-2>) <g-0> |
+
+ <fis-4> <c-3> <dis-1>( <fis-4>) <a-2> <fis-4> c' a |
+ <d-4> <bes-3>( <g-0>) <d-0> <g-0> <d-0> <bes'-3>( <g-0>) |
+ <gis-1> <d-0> <b'-9> <gis-1> <d'-4>( <b-0>) <f'-4\2>( <d-1>) |
+ <bes'-2>( <a-1>) <g-4> <f-2> <e-1> <d-3> <cis-2> <bes-4\4> |
+
+ <a-3> a <d-4 f-2> a a a <d f> a |
+ <g-3> g <bes-1 e-4> g g g <bes e> g |
+ \pestanaSpan "III" f,\startTextSpan f <bes' d> f, f f <bes' d> f,\stopTextSpan |
+ \pestanaSpan "II" r\startTextSpan <a' cis> <a cis>( e')~ e4\stopTextSpan
+
+ \pestanaSpan "III" <bes-1>16(\startTextSpan <c-4>) <d-1> <e-4> <f-4>\stopTextSpan <g-1> <a-3> <bes-4> |
+ <bes-4>( <a-3>) <gis-2> <a-3> <d-4>( <bes-1>) <g-3> <e-0> |
+ <f-1> <d-4> <a-2> <f-3> <e-1> <cis'-2> <g-0> <e-1>
+ }
+ \alternative {
+ { r <g-0> <gis-1> <a-2> r <bes-3> <a-2> <g-0> | }
+ { r4 \pestanaSingle "X" <f' a d> | }
+ }
+
+ \key d \major
+ \pestanaSpan "V" a16\startTextSpan <a, d>8 <a d>16 r16 g'8 fis16 |
+ a16 <g, cis>8 <g cis>16 r16 cis fis cis\stopTextSpan |
+ <a e'>16 <a d>8 \pestanaSingle "II" <a cis>16 <a b>8 a |
+ b16 <cis, g'>8 <cis g'>16 \pestanaSpan "II" cis'8\startTextSpan <g a>\stopTextSpan |
+
+ d'16 <fis, a>8 <fis a>16 fis'8 <a, c> |
+ e'16 <a, cis>8 <a cis>16 r16 g cis e |
+ <d gis d'>16 <d gis d'>8 <d gis d'>16 <d gis cis>8 <d gis b> |
+ e'16 <cis, g'>8 <cis g'>16 <cis g' b\2>8 <cis g' a> |
+
+ \pestanaSpan "V" a'16\startTextSpan <a, d>8 <a d>16 r16 g'8 fis16 |
+ a16 <g, cis>8 <g cis>16 r16 cis fis cis\stopTextSpan |
+ <a e'>16 <a d>8 \pestanaSingle "II" <a cis>16 <a b>8 a |
+ b16 <cis, g'>8 <cis g'>16 \pestanaSpan "II" cis'8\startTextSpan <g a>\stopTextSpan |
+
+ d'16 <fis, a>8 <fis a>16 dis'8 <fis, a> |
+ e'8 <b d>16 <ais cis> <g b e> d'( b) g |
+ fis d' a fis e cis' g e
+ r4 \pestanaSingle "VII" <d' fis d'> |
+ \bar "||"
+
+ \key d \minor
+ <f-2>16 <a,-3 d-4>8 <a d>16 f'8 <a, d> |
+ <e'-4>16 <g,-3 bes-1>8 <g bes>16 <d'-4>8 <f,-3 a-2> |
+ <cis'-4>16 <e,-3 g-0>8 <e g>16 <f'-1>8 <g, cis> |
+ <e'-0>16 <g,-0 cis-4>8 <g cis>16 <d'-4>8 <f,-3 a-2> |
+
+ <f'-2>16 <a,-3 d-4>8 <a d>16 f'8 <a, d> |
+ \pestanaSpan "V" <e'-1>16\startTextSpan <a,-3 c-1>8 <a c>16 a,8 <a' c> |
+ <a-3>16 <c-1> <a'-4> c,\stopTextSpan <aes-3> <b-1> <b'-4> b, |
+ \pestanaSpan "V" <a-3>\startTextSpan <c-1> <a'-1> c, a c a' c,\stopTextSpan |
+
+ <f-2> <a,-3 d-4>8 <a d>16 f'8 <a, d> |
+ f'16 <a, d>8 <a d>16 <e'-1>8 <a,-3 cis-2> |
+ \pestanaSpan "III" <g'-1>16\startTextSpan <bes,-1 e-3>8 <bes e>16 g'8 <bes, e> |
+ g'16 <bes, e>8 <bes e>16\stopTextSpan \pestanaSpan "V" <a'-1>8\startTextSpan <c,-1 fis-3>\stopTextSpan |
+
+ \pestanaSpan "V" <bes'-2>16\startTextSpan <d,-3 g-4>8 <d g>16 <d g> <a'-1>8 <g-4>16 |
+ <f-2> <a,-3 d-4>8 <a d>16 <a d> <e'-0>8 <d-4>16 |
+ <c-2>^"[#]"
+ <a-3>8 a16 a <f'-2>8 <e-1>16 |
+ <d-4> <a-3>8 a16 a a8 a16\stopTextSpan |
+
+ \key d \major
+ \pestanaSpan "V" a'16\startTextSpan <a, d>8 <a d>16 r16 g'8 fis16 |
+ a16 <g, cis>8 <g cis>16 r16 cis fis cis\stopTextSpan |
+ <a e'>16 <a d>8 \pestanaSingle "II" <a cis>16 <a b>8 a |
+ b16 <cis, g'>8 <cis g'>16 \pestanaSpan "II" cis'8\startTextSpan <g a>\stopTextSpan |
+
+ d'16 <fis, a>8 <fis a>16 fis'8 <a, c> |
+ e'16 <a, cis>8 <a cis>16 r16 g cis e |
+ <d gis d'>16 <d gis d'>8 <d gis d'>16 <d gis cis>8 <d gis b> |
+ e'16 <cis, g'>8 <cis g'>16 <cis g' b\2>8 <cis g' a> |
+
+ \pestanaSpan "V" a'16\startTextSpan <a, d>8 <a d>16 r16 g'8 fis16 |
+ a16 <g, cis>8 <g cis>16 r16 cis fis cis\stopTextSpan |
+ <a e'>16 <a d>8 \pestanaSingle "II" <a cis>16 <a b>8 a |
+ b16 <cis, g'>8 <cis g'>16 \pestanaSpan "II" cis'8\startTextSpan <g a>\stopTextSpan |
+
+ d'16 <fis, a>8 <fis a>16 dis'8 <fis, a> |
+ e'8 <b d>16 <ais cis> <g b e> d'( b) g |
+ fis d' a fis e cis' g e
+ r4 \pestanaSingle "VII" <d' fis d'> |
+ \bar "|."
+}
+
+lower = \relative c {
+ \globals
+ \set stringNumberOrientations = #'(down)
+
+ r2_\markup { \circle 6 = D } |
+ r8 a r a |
+ r8 a r a |
+ r16 <a-0>8 <b-1>16 <cis-3> <b-1>8 <a-0>16 |
+
+ d16 r8. r4
+ r8 a r a |
+ r8 a r a |
+ r16 a8 b16 cis b8 a16 |
+
+ d,4. a''16 a,~ |
+ a a8 b16 cis b8 a16
+ d,4. a''16 a,~ |
+ a a8 b16 cis b8 a16 |
+
+ \repeat volta 2 {
+ d,4 dis' |
+ <e-3> g, |
+ <fis'-3>16 fis8 fis16 fis8 fis |
+ e,4 cis' |
+
+ d, dis |
+ <e-1> <ais-1> |
+ e' e |
+ a, r4 |
+
+ d, <dis'-2> |
+ e g, |
+ fis'16 fis8 fis16 fis8 fis |
+ e,4 cis' |
+
+ d, fis |
+ <e-4>8 r8 r4 |
+ fis'8. fis16 e8. e16 |
+ }
+ \alternative {
+ { d,2 | }
+ { d8 a' d4 | }
+ }
+
+ \repeat volta 2 {
+ \key d \minor
+
+ a'16\4 a8 a16 a a8 a16 |
+ g\4 g8 g16 g g8 g16 |
+ f f8 f16 f f8 f16 |
+ e4 a, |
+
+ r2 |
+ d8. r16 r4 |
+ r2 |
+ a2 |
+
+ a'16 a8 a16 a a8 a16 |
+ g g8 g16 g g8 g16 |
+ f, f8 f16 f f8 f16 |
+ e4 r16 <e-1>( <f-2>) <fis-3> |
+
+ g4 r4 |
+ d g' |
+ d8. f16 e8. e16 |
+ }
+ \alternative {
+ { d4 r | }
+ { d8 a d4 | }
+ }
+
+ \key d \major
+ d,4 dis' |
+ <e> g, |
+ <fis'>16 fis8 fis16 fis8 fis |
+ e,4 cis' |
+
+ d, dis |
+ <e> <ais> |
+ e' e |
+ a, r4 |
+
+ d, <dis'> |
+ e g, |
+ fis'16 fis8 fis16 fis8 fis |
+ e,4 cis' |
+
+ d, fis |
+ <e>8 r8 r4 |
+ fis'8. fis16 e8. e16 |
+ d,8 a' d4 |
+
+ \key d \minor
+ d,4 d |
+ a' a |
+ a a |
+ a d, |
+
+ d d |
+ a' a |
+ a' aes |
+ a a |
+
+ d,, d |
+ d a' |
+ a a |
+ a d |
+
+ <g-1> r |
+ d, r |
+ a'2 |
+ d, |
+
+ \key d \major
+ d4 dis' |
+ e g, |
+ fis'16 fis8 fis16 fis8 fis |
+ e,4 cis' |
+
+ d, dis |
+ e ais |
+ e' e |
+ a, r4 |
+
+ d, dis' |
+ e g, |
+ fis'16 fis8 fis16 fis8 fis |
+ e,4 cis' |
+
+ d, fis |
+ e8 r8 r4 |
+ fis'8. fis16 e8. e16 |
+ d,8 a' d4 |
+ \bar "|."
+}
+
+staff = \new Staff <<
+ \upper
+ \\
+ \lower
+>>
+
+\score {
+ \staff
+ \layout { }
+}
+
+\score {
+ \unfoldRepeats \staff
+ \midi { }
+}
diff --git a/src/content/music/dengoso.ogg b/src/content/music/dengoso.ogg
new file mode 100644
index 0000000..8041487
--- /dev/null
+++ b/src/content/music/dengoso.ogg
Binary files differ
diff --git a/src/content/music/guitar.ly.include b/src/content/music/guitar.ly.include
new file mode 100644
index 0000000..5081dd9
--- /dev/null
+++ b/src/content/music/guitar.ly.include
@@ -0,0 +1,33 @@
+
+pestanaPrivate = \markup {
+ \fontsize #-2
+ \combine
+ \serif C
+ \translate #'(0.6 . -0.4) \draw-line #'(0 . 2.0)
+}
+
+pestanaSingle =
+ #(define-music-function (fret-number music) (string? ly:music?)
+ #{
+ $music ^\markup { \pestanaPrivate \fontsize #-2 $fret-number }
+ #})
+
+pestanaSpan =
+ #(define-music-function (fret-number) (string?)
+ #{
+ \once \override TextSpanner.font-shape = #'upright
+ \once \override TextSpanner.style = #'solid
+ \once \override TextSpanner.bound-details.left.stencil-align-dir-y = #CENTER
+ \once \override TextSpanner.bound-details.right.text = \markup { \draw-line #'(0 . -.5) }
+ \once \override TextSpanner.bound-details.left.text = \markup { \pestanaPrivate \fontsize #-2 $fret-number }
+ #})
+
+corda =
+ #(define-music-function (string-number) (string?)
+ #{
+ \once \override TextSpanner.style = #'solid
+ \once \override TextSpanner.font-size = #-5
+ \once \override TextSpanner.bound-details.left.stencil-align-dir-y = #CENTER
+ \once \override TextSpanner.bound-details.right.text = \markup { \draw-line #'( 0 . -.5) }
+ \once \override TextSpanner.bound-details.left.text = \markup { \circle \number $string-number }
+ #})
diff --git a/src/content/music/marcha-dos-marinheiros.ly b/src/content/music/marcha-dos-marinheiros.ly
new file mode 100644
index 0000000..48cc1c6
--- /dev/null
+++ b/src/content/music/marcha-dos-marinheiros.ly
@@ -0,0 +1,78 @@
+\version "2.20.0"
+\pointAndClickOff
+#(ly:set-option 'embed-source-code #t)
+
+\include "common.ly.include"
+\include "guitar.ly.include"
+
+\header {
+ title = "Marcha dos Marinheiros"
+ composer = "Canhoto"
+ copyright = \copyright
+ tagline = \tagline
+}
+
+globals = {
+ \clef "treble_8"
+ \key a \minor
+ \time 2/4
+ \set fingeringOrientations = #'(left)
+ \tempo Adagio 4 = 70
+}
+
+upper = \relative c {
+ \globals
+}
+
+lower = \relative c {
+ \globals
+ \set stringNumberOrientations = #'(down)
+
+ d4~ d16 <b-2> <c-3> <d-0> |
+ <e-2>4~ e16 <a,-0> <b-2> <c-3> |
+ d4~ d16 <gis,-4> <a-0> <b-2> |
+ <c-4>4 a8 e |
+
+ d'4~ d16 <b-2> <c-3> <d-0> |
+ <e-2>4~ e16 e <d-0> <c-4> |
+ <e-2>8 <d-0> <c-4> <b-2> |
+ a4~ a16 e' ees d |
+
+ \repeat volta 2 {
+ \mark \markup { \musicglyph #"scripts.segno" }
+ <c-4>4 <b-2>8 <e,-0> |
+ <a-0>4~ a16 <a'-2> <aes-1> <g-0> |
+ <f-3>4 <e-1>8 <a,-0> ||
+ <d-0>4~ d16 d e f |
+
+ d'8 c b a |
+ }
+ \alternative {
+ {
+ gis4~ gis16 d e f |
+ e8 d c b |
+ a4~ a16 e' ees d |
+ }
+ {
+ c'8 b a f |
+ e d c b |
+ a2 |
+ }
+ }
+}
+
+staff = \new Staff <<
+ \upper
+ \\
+ \lower
+>>
+
+\score {
+ \staff
+ \layout { }
+}
+
+\score {
+ \unfoldRepeats \staff
+ \midi { }
+}
diff --git a/src/content/music/marcha-dos-marinheiros.ogg b/src/content/music/marcha-dos-marinheiros.ogg
new file mode 100644
index 0000000..c07dabe
--- /dev/null
+++ b/src/content/music/marcha-dos-marinheiros.ogg
Binary files differ
diff --git a/src/content/pt/blog/categorias.adoc b/src/content/pt/blog/categorias.adoc
new file mode 100644
index 0000000..19e7cb7
--- /dev/null
+++ b/src/content/pt/blog/categorias.adoc
@@ -0,0 +1,6 @@
+= Artigos por categoria
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/pt/blog/index.adoc b/src/content/pt/blog/index.adoc
new file mode 100644
index 0000000..ec9c676
--- /dev/null
+++ b/src/content/pt/blog/index.adoc
@@ -0,0 +1,5 @@
+= Blog
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/pt/eslaides/categorias.adoc b/src/content/pt/eslaides/categorias.adoc
new file mode 100644
index 0000000..19e7cb7
--- /dev/null
+++ b/src/content/pt/eslaides/categorias.adoc
@@ -0,0 +1,6 @@
+= Artigos por categoria
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/pt/eslaides/index.adoc b/src/content/pt/eslaides/index.adoc
new file mode 100644
index 0000000..4e22115
--- /dev/null
+++ b/src/content/pt/eslaides/index.adoc
@@ -0,0 +1,7 @@
+= Eslaides
+
+Apresentações renderizadas em PDF: nenhum navegador é necessário para lê-las, e nenhum framework de apresentação em JavaScript para carregar junto.
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/pt/index.adoc b/src/content/pt/index.adoc
new file mode 100644
index 0000000..e11d16b
--- /dev/null
+++ b/src/content/pt/index.adoc
@@ -0,0 +1,7 @@
+= Início
+
+O mais recente de cada coleção está abaixo.
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/pt/musica.adoc b/src/content/pt/musica.adoc
new file mode 100644
index 0000000..5ebffeb
--- /dev/null
+++ b/src/content/pt/musica.adoc
@@ -0,0 +1,36 @@
+= Música
+:dengoso: ../music/dengoso
+:marcha: ../music/marcha-dos-marinheiros
+:choro: ../music/choro-da-saudade
+
+Partituras que eu gravei, cada uma com uma execução sintetizada. Os fontes em LilyPond estão no repositório; os PDFs são gerados a partir deles.
+
+== Partituras
+
+João Pernambuco, link:{dengoso}.pdf[Dengoso]
+
+++++
+<audio controls>
+ <source src="../music/dengoso.ogg" type="audio/ogg" />
+</audio>
+++++
+
+Canhoto, link:{marcha}.pdf[Marcha dos Marinheiros]
+
+++++
+<audio controls>
+ <source src="../music/marcha-dos-marinheiros.ogg" type="audio/ogg" />
+</audio>
+++++
+
+Agustín Barrios, link:{choro}.pdf[Choro da Saudade]
+
+++++
+<audio controls>
+ <source src="../music/choro-da-saudade.ogg" type="audio/ogg" />
+</audio>
+++++
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/pt/pastebin/categorias.adoc b/src/content/pt/pastebin/categorias.adoc
new file mode 100644
index 0000000..19e7cb7
--- /dev/null
+++ b/src/content/pt/pastebin/categorias.adoc
@@ -0,0 +1,6 @@
+= Artigos por categoria
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/pt/pastebin/index.adoc b/src/content/pt/pastebin/index.adoc
new file mode 100644
index 0000000..59cb93e
--- /dev/null
+++ b/src/content/pt/pastebin/index.adoc
@@ -0,0 +1,5 @@
+= Pastebins
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/pt/podcast/categorias.adoc b/src/content/pt/podcast/categorias.adoc
new file mode 100644
index 0000000..19e7cb7
--- /dev/null
+++ b/src/content/pt/podcast/categorias.adoc
@@ -0,0 +1,6 @@
+= Artigos por categoria
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/pt/podcast/index.adoc b/src/content/pt/podcast/index.adoc
new file mode 100644
index 0000000..6e84602
--- /dev/null
+++ b/src/content/pt/podcast/index.adoc
@@ -0,0 +1,5 @@
+= Podcast
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/pt/screencast/categorias.adoc b/src/content/pt/screencast/categorias.adoc
new file mode 100644
index 0000000..19e7cb7
--- /dev/null
+++ b/src/content/pt/screencast/categorias.adoc
@@ -0,0 +1,6 @@
+= Artigos por categoria
+:type: categories
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/pt/screencast/index.adoc b/src/content/pt/screencast/index.adoc
new file mode 100644
index 0000000..930c244
--- /dev/null
+++ b/src/content/pt/screencast/index.adoc
@@ -0,0 +1,5 @@
+= Screencasts
+
+
+
+// Generated from po4a(1).
diff --git a/src/content/pt/sobre.adoc b/src/content/pt/sobre.adoc
index 2d7ad20..61ed4ea 100644
--- a/src/content/pt/sobre.adoc
+++ b/src/content/pt/sobre.adoc
@@ -2,10 +2,24 @@
:mailto: mailto:~euandreh/public-inbox@lists.sr.ht
:archive: https://lists.sr.ht/~euandreh/public-inbox
+:cargo2nix: ../en/blog/2020/10/05/cargo2nix.html
+:swift2nix: ../en/blog/2020/10/05/swift2nix.html
+:database: ../en/blog/2020/08/31/database-i-wish-i-had.html
+:fallible: ../en/blog/2021/02/17/fallible.html
+:flags: ../en/blog/2020/10/19/feature-flags.html
+:interviewing: ../en/blog/2020/10/20/wrong-interviewing.html
+:stateless: ../en/blog/2019/06/02/nixos-stateless-workstation.html
+:bugtracker: ../en/blog/2020/11/07/diy-bugs.html
-Oi, eu sou EuAndreh. Eu escrevo software e, ocasionalmente, música. Você encontra meu dados para entrar em contato no rodapé desta página, ou pode mandar também uma mensagem para minha {mailto}[caixa de entrada pública] ({archive}[arquivo]).
+Oi, eu sou EuAndreh -- André Miranda Moreira. Eu escrevo software e ocasionalmente música. Você encontra dados para me contatar no rodapé desta página, ou pode mandar também uma mensagem para minha {mailto}[caixa de entrada pública] ({archive}[arquivo]).
-Esse é o meu site pessoal onde eu escrevo artigos, publico software e outros trabalhos relacionados.
+Esse é o meu site pessoal onde eu escrevo artigos, público software e outros trabalhos relacionados.
+
+I write about robust software: the kind that still works after the network drops, after the vendor leaves, and after you stop paying attention.
+
+That shows up in a few places. I care about reproducible systems, so I run a link:{stateless}[stateless NixOS workstation] and have written bridges like link:{cargo2nix}[cargo2nix] and link:{swift2nix}[swift2nix] to make Rust and Swift first-class citizens inside Nix builds. I care about durable data, which is why I keep returning to local-first software, persistent data structures, and link:{database}[the database I wish existed]: embedded, immutable, and syncable. And I care about tools that don't need a company behind them, so I reach for Git, email and plain text before I reach for a service -- far enough that link:{bugtracker}[my bug tracker is a directory].
+
+I also think about how teams ship -- fault injection (link:{fallible}[fallible]), link:{flags}[feature flags], and link:{interviewing}[how we interview engineers] -- because robustness is a team habit before it is a technology.
diff --git a/src/content/public.asc.txt b/src/content/public.asc.txt
index d548547..2519d0f 100644
--- a/src/content/public.asc.txt
+++ b/src/content/public.asc.txt
@@ -11,45 +11,45 @@ rp1x5i/04198ocRZT3MzXx8H25tLMS/rHmE87YdgPhMTWheSUevyhoGNHfAOcDwX
P2oGzELXbLqHxtjENMEw2E996KrSmpcz7WOqIl3PHS1J6eRZoYQesXE+SZTeIiYb
wD0kkZGYhBZbtLC4VWIuU2T3AL/2hF6aUh1tj1B6vcV0i3HpIHNbvPAF/I0NUhhc
Gxwwi+ggG/MBHBbxkq7LvG5DfDbav0ZoZaov5dyhtX0CBWjVYATvjRfeAwARAQAB
-tBlFdUFuZHJlaCA8ZXVAZXVhbmRyZS5vcmc+iQI5BBMBCAAjBQJY1b4eAhsDBwsJ
-CAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQgfkOw801YGCWzg//QtDpwgbDY9uC
-Y9a/RgUsbqGAYzSInsbyDCXrAAhWGzkDMLPeFp03Sw9QyCDe0wWu8L2H4hV/FN58
-+4G6353ISwkqsf9R+P9lQs/5dwG7lp5/Gez8bZK3y7zFrdtVwcOCb4De+9fhPsgP
-9pRU8dHpLNo8Ui9IzbiYla7aGxXQdkXU2cvOuEoiuFgvcWU1KWNOWrjImATcC8EF
-8VaEaZYGRXz8lML8KgsAUxrjFkk6tqxrMlOLTjY0BuzcYZpt5XLZ2NuSIDYBoSib
-uBQ1H7DLGa+r0hnNjVEBmMOvFA1hbWa33h1AyYjYhoeVlBYpoHuDosEFqkwZ+otz
-zvImaRAOOFX1IehifTGEFie3imuOHdVuRjXb8SGu8Cgeby0T096A/vf+L1S35nc2
-mdRCUE/SIURW6hfH7uT6KqpokU86vozKmNzIcV3zhAXJ9UYwQqZgg2H3DOcTtZyE
-jVBl2glspoclsfR20T+g+qPqNDAgoDbC71fEAbUTACQau162utpHiabog7e7vyhI
-go5xdjxA8xb3Jtn39pYzbg75ArZqPbxHNZ38m00EBtC5EkD4DFh0cpQ2peuZIh1k
-c5bragCt8o6cV9t4jaq+TtVv4PrFEPqEd+w1FqqwabBq3xSsIgKg2X5rXQkktymB
-un+oN41wofuTZIoGNt8nnGb+skFBxgyJAlYEEwEKAEACGwMHCwkIBwMCAQYVCAIJ
-CgsEFgIDAQIeAQIXgBYhBFva6biy9sa8uw1s5YH5DsPNNWBgBQJmm7wJBQkRiGTr
-AAoJEIH5DsPNNWBgkxoQAJToyFUvioGD/91ztneUBOD2LOkQaIH4ZUTyIEjxpiep
-ry7QIC33wc2OLJo8QKfPZNaZlvy0EcuzSNZz2Za0NUg5y/lzDOCyjrRw7/szHcPX
-saUVHcjwYjQV/yiB1GJcuHefrEwXmgjQbkGmNZw8t7DuEm7qMrAdbAg3b+S4yX8X
-wgAHXhCx33E7sLCwGDzWzXKhWyn3RNoDtTrsSyoXEUi2tUAJ6oEVIQd2MMH3gMxt
-f31sg+cWTMdx7s2ZYatQ9hYOu86HR0O5fGD4T9Ae1TEhV8t3ZiUvozB/BfMUYwGx
-UajxAN+QJZJrsuc98MlqTDk3vViqCyptFJaIjZHaLrB3rhtw9VISnEnXY1WsiL4R
-+0R99xKt6D/yZXljXS1HR3w9VLpzE0NikYVkYBbsFFTZ8SrMwVCzY+wWjP0miQ2X
-gAvX3+Lr1PeAmigKkCnt9gKuZz5b2hD0WWb7P9HXLR04XtOq2QOvA5kObl51C+Af
-BcdEM9Q9FzQHqa5ofq0ZeS4WL72nkJFuAiGhi9kcpV70YHBMk0BuSIEuctGempuJ
-4CeH6X2pKMHNJ5ijctZJiMPB8V2AzlWPber44IXD9f8B6HAQsNpZJ4+rWw2zCeKf
-Jj/n0UIqwEU9vG1KMU3OaAqrlaP0ePd3+BgjgIemMSwzNtSamtVc8TbqodXWKQF/
-tCFwYXBvLmltIFN1cHBvcnQgPHN1cHBvcnRAcGFwby5pbT6JAlcEEwEIAEEWIQRb
-2um4svbGvLsNbOWB+Q7DzTVgYAUCaBSRgAIbAwUJEYhk6wULCQgHAgIiAgYVCgkI
-CwIEFgIDAQIeBwIXgAAKCRCB+Q7DzTVgYJ7dEACtW+VEVBIj2v390ph+6bB9hnvq
-b78oC0EGryuKcKh797lld02jOW1WsIa2l7RzsS+XWDoY6c1DhCha75avLdr4uXgz
-oLjScXinRNa5y9ECYdhiAScTVE3PbqURFbd9txPAY6rCP0Lo05tfXrX7zs/5UK8W
-98FN4mSNge7YZEmOSSFFEQLaLRs/JH89GSlAXjfdke5cEiYqvabXXle/Zv/umm7B
-J2ndd1bUgRbcL4SznMMo+JAhHzr1KpuvhFj/Dv4IUeMsWwv5x0KOXcCSUNpEnN0v
-vw1aIFgI7JuQiYyMK0g3ubUfser5jOX7faz4Y5PJEcx6mzTrrdjC8vqCDaN+iA7O
-pIS951yaYs6oI09ev1xQKCO+tJVFgPYMSh4lTMHMndZnMvTGUZy1Mt1tE+u8/Ps+
-Ps6UbApinrL1saeg1ODsels16mANGsQ62LXQfqTaQomKD/FfGHokOIogdj/hwhGC
-XqhAoUtPoO5gQRqToenWybMNyu+fKo1gERU0WjXdqkledgY3Hi8JvjAaBgGyUmoi
-DdRzGt8CAYZ72Tpwj4/yziVPw+myP6JmOvaRatZbL6SvFUEGnmip+ZWhA3yw0oyC
-dSQ2TL6q2G/qjA/0cpxTGEyiObSLqlZgamV8i+bpqXn6yt3CoUk4xm0jsChHK9zF
-w+7WzwhBSyDeeAIm8rkCDQRY1b4eARAA87doimtYEpLB7UxZD0OVC6lBdrBwwpa9
+tCFwYXBvLmltIFN1cHBvcnQgPHN1cHBvcnRAcGFwby5pbT6JAlcEEwEIAEECGwMF
+CwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AWIQRb2um4svbGvLsNbOWB+Q7DzTVg
+YAUCamFh6AUJFU4KygAKCRCB+Q7DzTVgYLrhEACKa/uQt+BCDJIFUHKQ36mKzJR5
+ubKym2HRtsbC2L+aVW8pWR7vk3VhqOWV9PLL4hOPJpKWMvxZEzpfYWvbFgjK3Uet
+b2iBTOOU9YJ5eyO6kCcSKHWMUWegk23RGCCyxLfeTz9pf4QSGwCjkLiWquPOiT3o
+RHCpOkWZg3aBlsxIozmdi/Dv0zNYNfpu6N7NRMfQDv8IYEq66VkpuPulQmSu+SAE
+ja7/Z7ed4/mQxC6gN5EIULrmI42h5LnyCTENNVN8vSZMNvjTeXlU53AHEGChaA9l
++/YxMYKk3RS+CS+1hQWjT2vctZ4+7kVfaZf8rLwcSwHhhmEdgxKCH1stP4vCbtVo
+xOvQNPwLi+5rSve3XlPEwXspteN9Oja8/xPG5qhtHz2dudg77mhL5+Yo5iemiA5A
+6FLtpDKW//cS8vkW1B6RBsq1vwzXdbvF9ePzOf8IO/8vZTBEEv/Taf9SW4pSWE3J
+gzYGjiJwWoZZnfpDus4BVw4No6bfbQ/XGFgXu6q66R5KQRxrhPMOQ1EU9OB6hm4z
+g2V1MALnVFuBWG1iWp+PTH+mlWS/wnXdsKsImOVMDG2bW31mO9diR4qVu7IMZweQ
+FUo6R6e6gcmLW8z/Lad+eyp8srCWed4N+TxVeDG1bECBIF++gjGnHH7F0QXuxIql
+7OZGV01whHmiItNfc7QZRXVBbmRyZWggPGV1QGV1YW5kcmUub3JnPokCOQQTAQgA
+IwUCWNW+HgIbAwcLCQgHAwIBBhUIAgkKCwQWAgMBAh4BAheAAAoJEIH5DsPNNWBg
+ls4P/0LQ6cIGw2PbgmPWv0YFLG6hgGM0iJ7G8gwl6wAIVhs5AzCz3hadN0sPUMgg
+3tMFrvC9h+IVfxTefPuBut+dyEsJKrH/Ufj/ZULP+XcBu5aefxns/G2St8u8xa3b
+VcHDgm+A3vvX4T7ID/aUVPHR6SzaPFIvSM24mJWu2hsV0HZF1NnLzrhKIrhYL3Fl
+NSljTlq4yJgE3AvBBfFWhGmWBkV8/JTC/CoLAFMa4xZJOrasazJTi042NAbs3GGa
+beVy2djbkiA2AaEom7gUNR+wyxmvq9IZzY1RAZjDrxQNYW1mt94dQMmI2IaHlZQW
+KaB7g6LBBapMGfqLc87yJmkQDjhV9SHoYn0xhBYnt4prjh3VbkY12/EhrvAoHm8t
+E9PegP73/i9Ut+Z3NpnUQlBP0iFEVuoXx+7k+iqqaJFPOr6MypjcyHFd84QFyfVG
+MEKmYINh9wznE7WchI1QZdoJbKaHJbH0dtE/oPqj6jQwIKA2wu9XxAG1EwAkGrte
+trraR4mm6IO3u78oSIKOcXY8QPMW9ybZ9/aWM24O+QK2aj28RzWd/JtNBAbQuRJA
++AxYdHKUNqXrmSIdZHOW62oArfKOnFfbeI2qvk7Vb+D6xRD6hHfsNRaqsGmwat8U
+rCICoNl+a10JJLcpgbp/qDeNcKH7k2SKBjbfJ5xm/rJBQcYMiQJWBBMBCgBAAhsD
+BwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AWIQRb2um4svbGvLsNbOWB+Q7DzTVg
+YAUCamFh6AUJFU4KygAKCRCB+Q7DzTVgYJjSD/9rgUEuF/1x+veK/nclBVHt0fyd
+ze+ESqZH+sZx2U564AqP6yehjoz+cervDwmER1ja2sFyCddXS3CUCwr7B0vVMc2R
+MH5yaQ7vtnNV0xAGKs7rPiY/KWfZRoif+2dU9l6v/n4zZTqbC/JY8iuuF8oyC0MH
+o8sKnsmPJiLg0he2PChThjbmVThHhCA6aM35q4zeq3NYLdRECzbySerqcZxONPFv
+9jIYTN6eXdq4Bm/qf835/r9gTtvrodn3Fu7XRINpxxmGTJ2cCbZNBPQbvTd9FsSO
+Sc1I1K6uO81x3xoPSmxH0QIhaJmIfvvatHGexbaqipOYDQA904wzFGrNdNm9/oXc
+CAA61g/URdUtrgnFPe87LydFkhfym5OQSHwywO9OxlHCSKaprziUIq+UuQ1t5ka7
++DrdHj6GA6F/aeOnP9J0wZJY0ESElzhU7HEcObUstDeabI27AhmrwidJmjwYj4i+
+Z+lNFiRM+3dBAxlg6d4V0x4M2yG2lrHdFoMxUT2aKYjgpViPjtvtxQ0q5MOxusg4
+vPHE3sT3UGggezUicf1A5fKhNxN9gE+ImyvxDqZBNN+z1ClVHqGBJt9slaCSRZwi
+tVzf8bu36uZquQzkNrzJgNEwVubG6o2ec7bVxDnvCFPACWY4hbCC80E+1F/hs7I8
+8V9mqXIBAIGz/PyLLLkCDQRY1b4eARAA87doimtYEpLB7UxZD0OVC6lBdrBwwpa9
8pGy6vwMYEgjjefHjwPUikMtjvqg6aIxwDdVFCyYBxJ9+Gn8zJAc48XCVzEYGk1d
jPtba48Mf1Zbg/Til8Jp4ZknbwiD+2dKnYVO/Jmi6tdFBtb1cHo95FZcbH+BLtmQ
PjXiK+049AxEahA0KA0bTT1RMt4dcaGK7sv1YzQ4tQiN8hZDOh1tQoEQOdKyOxg7
@@ -95,5 +95,5 @@ T0PAbuproAk2TXn8NDzsFJP6ERM+3c1ARNiwZ8wCjiNChxX5cEh4ciNltkQ54ZF/
rN/8i+o7WAE9xdIdVHZg0H+WPWa/lOs5yfsSXES3Y7wxOR8chW038/y82TECk7LP
+dZyFKJ26VSvqPHxMqmji/UJjOB+K5xaaCflnzsKzsaEf5fRCIrCp6ldIXh0/x0s
pqsEJPXPOb40oBnNzLOSv9X++wKq+oXWEX8OSqhr
-=bNun
+=mG9H
-----END PGP PUBLIC KEY BLOCK-----
diff --git a/src/content/style.css b/src/content/style.css
index dbd7967..6fad5bf 100644
--- a/src/content/style.css
+++ b/src/content/style.css
@@ -1,41 +1,90 @@
:root {
- --color-fg: black;
- --color-bg: white;
- --color-2nd-fg: #555555;
- --color-2nd-bg: #f5f5f5;
- --color-pre-border: hsla(0, 100%, 0%, 30%);
+ --color-fg: #171D1A;
+ --color-bg: #F3F5F3;
+ --color-2nd-fg: #4A5651;
+ --color-2nd-bg: #E9EDEA;
+ --color-3rd-fg: #6F7B76;
+ --color-line: #CBD3CE;
+ --color-line-soft: #DDE3DF;
+ --color-accent: #1F6F5B;
+ --color-accent-soft: #D8E9E2;
+ --color-ochre: #9A6B1F;
+ --color-code-bg: #E6EBE8;
+ --color-pre-border: #CBD3CE;
--link-url: url(img/link/light.svg);
+ /* The generic families and nothing else: a named face is a
+ dependency that is either installed or silently is not, and
+ the ones worth naming here are Windows and macOS fonts that
+ are absent on the systems this site is built for. The reader
+ has already chosen these three; the hierarchy is in which of
+ them is used where, not in which face answers. */
+ --font-body: sans-serif;
+ --font-display: serif;
+ --font-mono: monospace;
+
color: var(--color-fg);
background-color: var(--color-bg);
}
@media (prefers-color-scheme: dark) {
:root {
- --color-fg: white;
- --color-bg: black;
- --color-2nd-fg: #aaaaaa;
- --color-2nd-bg: #222222;
- --color-pre-border: hsla(50, 100%, 70%, 10%);
+ --color-fg: #E4EAE7;
+ --color-bg: #121716;
+ --color-2nd-fg: #A7B3AE;
+ --color-2nd-bg: #19201E;
+ --color-3rd-fg: #7E8A85;
+ --color-line: #2C3733;
+ --color-line-soft: #222B28;
+ --color-accent: #5CC2A2;
+ --color-accent-soft: #1B3A31;
+ --color-ochre: #D9A85A;
+ --color-code-bg: #1C2522;
+ --color-pre-border: #2C3733;
--link-url: url(img/link/dark.svg);
}
+}
- a {
- color: hsl(211, 100%, 60%);
- &:visited {
- color: hsl(242, 100%, 80%);
- }
- }
+a {
+ color: var(--color-accent);
+}
+
+a:visited {
+ color: var(--color-accent);
}
+
/* General declarations */
body {
margin: 0px auto;
padding: 1%;
- max-width: 750px;
+ max-width: 68ch;
+ font-family: var(--font-body);
+ font-size: 17px;
+ line-height: 1.6;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ font-family: var(--font-display);
+ font-weight: 500;
+ line-height: 1.2;
+ text-wrap: balance;
+}
+
+h1 { font-size: 39px; }
+h2 { font-size: 31px; }
+h3 { font-size: 25px; }
+h4, h5, h6 { font-size: 20px; }
+
+code, pre, .mono {
+ font-family: var(--font-mono);
+}
+
+code {
+ background-color: var(--color-code-bg);
}
.icon {
@@ -52,28 +101,106 @@ header {
padding: 12px 0px 12px 0px;
}
-nav .nav-row {
+/* The home link is the logo. It shares the row with the rest of
+ the navigation rather than sitting on a line above it, so the
+ nav and its list are both rows and everything centres on the
+ same line. */
+nav, nav ol {
display: flex;
flex-direction: row;
- justify-content: space-between;
flex-wrap: wrap;
+ align-items: center;
}
+nav ol {
+ list-style: none;
+ margin: 0px;
+ padding: 0px;
+}
+
+
nav a {
font-size: 18px;
margin: 12px;
text-decoration: none;
}
-nav ul, nav li {
- display: inline;
+/* Twice the logo's own 16-unit grid, so every square it is drawn
+ from lands on whole pixels. */
+nav a img {
+ width: 32px;
+ height: 32px;
+}
+
+/* The languages are a second tier of navigation, not more of the
+ first: smaller, and away to the end of the row. The one being
+ read is not a link, so it is dimmed to say so. */
+nav ol.languages {
+ margin-left: auto;
}
-nav ul li a {
+nav ol.languages a, nav ol.languages li {
+ font-family: var(--font-mono);
font-size: 14px;
margin: 6px;
}
+/* The language being read is the one worth seeing, so it is the
+ only one at full strength. Whether one of the others has been
+ followed before says nothing about where it goes, so visited
+ and unvisited look alike. */
+nav ol.languages li {
+ color: var(--color-fg);
+}
+
+nav ol.languages a,
+nav ol.languages a:visited {
+ color: var(--color-2nd-fg);
+}
+
+nav ol.languages a:hover {
+ color: var(--color-fg);
+}
+
+
+/* A collection's name on the front page heads the list under it
+ rather than inviting a click, so it is set as a heading and only
+ says it is a link when pointed at. */
+h2.collection a {
+ color: var(--color-fg);
+ text-decoration: none;
+}
+
+h2.collection a:hover {
+ text-decoration: underline;
+}
+
+
+/* What the site says about itself, under the navigation on every
+ page --- the header's counterpart to the licence line at the
+ foot of it. */
+#tagline {
+ margin: 0px 12px 6px 12px;
+ font-size: 14px;
+ color: var(--color-2nd-fg);
+}
+
+
+/* A published recording */
+
+#media video, #media audio {
+ width: 100%;
+ max-width: 100%;
+}
+
+/* Both ways down are filenames, which want the same face as any
+ other filename does. */
+#media-links {
+ font-family: var(--font-mono);
+ font-size: 14px;
+ color: var(--color-2nd-fg);
+}
+
/* Article bodies */
@@ -138,7 +265,7 @@ pre.lineno {
border: 1px solid;
border-color: var(--color-pre-border);
border-radius: 10px;
- background-color: var(--color-2nd-bg);
+ background-color: var(--color-code-bg);
}
pre {
@@ -192,7 +319,7 @@ div.header-anchor:hover img {
.plaintext {
margin: auto auto 0 auto;
text-align: right;
- font-family: monospace;
+ font-family: var(--font-mono);
}
a.anchor {
@@ -212,3 +339,57 @@ h2:hover a.anchor {
.line-through {
text-decoration: line-through;
}
+
+
+/* Messages from the mailing list, rendered on the page they
+ answer. They take the secondary background so a thread reads as
+ something quoted onto the page rather than as more of the page,
+ which is the one thing the archive's own rendering makes obvious
+ at a glance. */
+#comments .message {
+ background-color: var(--color-2nd-bg);
+ border: 1px solid;
+ border-color: var(--color-pre-border);
+ border-radius: 10px;
+ padding: 0.5rem 1rem;
+ margin-bottom: 1rem;
+}
+
+/* A reply sits under what it answers. The depth is capped because
+ a long exchange nests further than a narrow screen has room for,
+ and past a few levels the indentation stops telling you anything
+ the order does not. */
+#comments .message[data-depth="1"] { margin-left: 1rem; }
+#comments .message[data-depth="2"] { margin-left: 2rem; }
+#comments .message[data-depth="3"] { margin-left: 3rem; }
+#comments .message[data-depth="4"] { margin-left: 4rem; }
+#comments .message:not([data-depth="0"]):not([data-depth="1"]):not([data-depth="2"]):not([data-depth="3"]):not([data-depth="4"]) {
+ margin-left: 5rem;
+}
+
+#comments .message-header {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: baseline;
+ gap: 0 1rem;
+ font-size: 0.9em;
+}
+
+#comments .from {
+ font-weight: bold;
+}
+
+#comments .date, #comments .reply {
+ color: var(--color-2nd-fg);
+}
+
+#comments .message-body {
+ background-color: transparent;
+ border: none;
+ white-space: pre-wrap;
+ overflow-wrap: anywhere;
+}
+
+@media (max-width: 40rem) {
+ #comments .message[data-depth] { margin-left: 0; }
+}
diff --git a/src/headers/de.txt b/src/headers/de.txt
index ca848a3..ddf6fee 100644
--- a/src/headers/de.txt
+++ b/src/headers/de.txt
@@ -1,13 +1,13 @@
-en/blog/ Blog
+de/blog/ Blog
-en/til/ TIL
+de/hhg/ HHG
-en/podcast/ Podcasts
+de/podcast/ Podcast
-en/screencast/ Screencasts
+de/screencast/ Screencasts
-en/pastebin/ Pastebins
+de/pastebin/ Pastebins
-en/slide/ Slides
+de/folien/ Folien
-en/about.html About
+de/ueber.html Über
diff --git a/src/headers/eo.txt b/src/headers/eo.txt
index ca848a3..e407689 100644
--- a/src/headers/eo.txt
+++ b/src/headers/eo.txt
@@ -1,13 +1,13 @@
-en/blog/ Blog
+eo/blog/ Blogo
-en/til/ TIL
+eo/hml/ HML
-en/podcast/ Podcasts
+eo/podkasto/ Podkasto
-en/screencast/ Screencasts
+eo/screencast/ Screencasts
-en/pastebin/ Pastebins
+eo/pastebin/ Pastebins
-en/slide/ Slides
+eo/lumbildoj/ Lumbildoj
-en/about.html About
+eo/pri.html Pri
diff --git a/src/headers/es.txt b/src/headers/es.txt
index ca848a3..0c17eff 100644
--- a/src/headers/es.txt
+++ b/src/headers/es.txt
@@ -1,13 +1,13 @@
-en/blog/ Blog
+es/blog/ Blog
-en/til/ TIL
+es/haa/ HAA
-en/podcast/ Podcasts
+es/podcast/ Podcast
-en/screencast/ Screencasts
+es/screencast/ Screencasts
-en/pastebin/ Pastebins
+es/pastebin/ Pastebins
-en/slide/ Slides
+es/diapositivas/ Diapositivas
-en/about.html About
+es/acerca.html Acerca
diff --git a/src/headers/fr.txt b/src/headers/fr.txt
index ca848a3..27c7fad 100644
--- a/src/headers/fr.txt
+++ b/src/headers/fr.txt
@@ -1,13 +1,13 @@
-en/blog/ Blog
+fr/blog/ Blogue
-en/til/ TIL
+fr/aja/ AJA
-en/podcast/ Podcasts
+fr/podcast/ Podcast
-en/screencast/ Screencasts
+fr/screencast/ Screencasts
-en/pastebin/ Pastebins
+fr/pastebin/ Pastebins
-en/slide/ Slides
+fr/diapositives/ Diapositives
-en/about.html About
+fr/a-propos.html À propos
diff --git a/src/headers/pt.txt b/src/headers/pt.txt
index 48f2523..cbfa67d 100644
--- a/src/headers/pt.txt
+++ b/src/headers/pt.txt
@@ -1,13 +1,13 @@
-#
+pt/blog/ Blog
pt/hea/ HEA
-#
+pt/podcast/ Podcast
-#
+pt/screencast/ Screencasts
-#
+pt/pastebin/ Pastebins
-#
+pt/eslaides/ Eslaides
pt/sobre.html Sobre
diff --git a/src/linkonly-dirs.txt b/src/linkonly-dirs.txt
deleted file mode 100644
index 3a038f0..0000000
--- a/src/linkonly-dirs.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-src/content/css/
-src/content/favicon/
-src/content/images/
-src/content/resources/
-src/content/static/
diff --git a/src/names/categories/de.txt b/src/names/categories/de.txt
index 36e8f75..2660a43 100644
--- a/src/names/categories/de.txt
+++ b/src/names/categories/de.txt
@@ -1 +1 @@
-categories
+kategorien
diff --git a/src/names/categories/eo.txt b/src/names/categories/eo.txt
index 36e8f75..5469c86 100644
--- a/src/names/categories/eo.txt
+++ b/src/names/categories/eo.txt
@@ -1 +1 @@
-categories
+kategorioj
diff --git a/src/names/categories/es.txt b/src/names/categories/es.txt
index 36e8f75..7216072 100644
--- a/src/names/categories/es.txt
+++ b/src/names/categories/es.txt
@@ -1 +1 @@
-categories
+categorias
diff --git a/src/names/category/de.txt b/src/names/category/de.txt
index 46e72f3..1bcf738 100644
--- a/src/names/category/de.txt
+++ b/src/names/category/de.txt
@@ -1 +1 @@
-category
+kategorie
diff --git a/src/names/category/eo.txt b/src/names/category/eo.txt
index 46e72f3..40103d7 100644
--- a/src/names/category/eo.txt
+++ b/src/names/category/eo.txt
@@ -1 +1 @@
-category
+kategorio
diff --git a/src/names/category/es.txt b/src/names/category/es.txt
index 46e72f3..ae2c934 100644
--- a/src/names/category/es.txt
+++ b/src/names/category/es.txt
@@ -1 +1 @@
-category
+categoria
diff --git a/src/names/category/fr.txt b/src/names/category/fr.txt
index 46e72f3..9bda034 100644
--- a/src/names/category/fr.txt
+++ b/src/names/category/fr.txt
@@ -1 +1 @@
-category
+categorie
diff --git a/src/names/feed/de.txt b/src/names/feed/de.txt
new file mode 100644
index 0000000..51dff98
--- /dev/null
+++ b/src/names/feed/de.txt
@@ -0,0 +1 @@
+Atom-Feed
diff --git a/src/names/feed/en.txt b/src/names/feed/en.txt
new file mode 100644
index 0000000..65e691e
--- /dev/null
+++ b/src/names/feed/en.txt
@@ -0,0 +1 @@
+Atom feed
diff --git a/src/names/feed/eo.txt b/src/names/feed/eo.txt
new file mode 100644
index 0000000..a59609e
--- /dev/null
+++ b/src/names/feed/eo.txt
@@ -0,0 +1 @@
+Atom-fluo
diff --git a/src/names/feed/es.txt b/src/names/feed/es.txt
new file mode 100644
index 0000000..4c621b0
--- /dev/null
+++ b/src/names/feed/es.txt
@@ -0,0 +1 @@
+Fuente Atom
diff --git a/src/names/feed/fr.txt b/src/names/feed/fr.txt
new file mode 100644
index 0000000..52ebba6
--- /dev/null
+++ b/src/names/feed/fr.txt
@@ -0,0 +1 @@
+Flux Atom
diff --git a/src/names/feed/pt.txt b/src/names/feed/pt.txt
new file mode 100644
index 0000000..8de9ae3
--- /dev/null
+++ b/src/names/feed/pt.txt
@@ -0,0 +1 @@
+Feed Atom
diff --git a/src/names/feed/ref.txt b/src/names/feed/ref.txt
new file mode 100644
index 0000000..65e691e
--- /dev/null
+++ b/src/names/feed/ref.txt
@@ -0,0 +1 @@
+Atom feed
diff --git a/src/names/feedicon_alt/de.txt b/src/names/feedicon_alt/de.txt
new file mode 100644
index 0000000..f52db08
--- /dev/null
+++ b/src/names/feedicon_alt/de.txt
@@ -0,0 +1 @@
+Das Symbol des Atom-Feeds
diff --git a/src/names/feedicon_alt/en.txt b/src/names/feedicon_alt/en.txt
new file mode 100644
index 0000000..0cc9aff
--- /dev/null
+++ b/src/names/feedicon_alt/en.txt
@@ -0,0 +1 @@
+The Atom feed icon
diff --git a/src/names/feedicon_alt/eo.txt b/src/names/feedicon_alt/eo.txt
new file mode 100644
index 0000000..d21e80c
--- /dev/null
+++ b/src/names/feedicon_alt/eo.txt
@@ -0,0 +1 @@
+La bildsimbolo de la Atom-fluo
diff --git a/src/names/feedicon_alt/es.txt b/src/names/feedicon_alt/es.txt
new file mode 100644
index 0000000..ba8ba5e
--- /dev/null
+++ b/src/names/feedicon_alt/es.txt
@@ -0,0 +1 @@
+El icono de la fuente Atom
diff --git a/src/names/feedicon_alt/fr.txt b/src/names/feedicon_alt/fr.txt
new file mode 100644
index 0000000..fc9d173
--- /dev/null
+++ b/src/names/feedicon_alt/fr.txt
@@ -0,0 +1 @@
+L'icône du flux Atom
diff --git a/src/names/feedicon_alt/pt.txt b/src/names/feedicon_alt/pt.txt
new file mode 100644
index 0000000..498ed4f
--- /dev/null
+++ b/src/names/feedicon_alt/pt.txt
@@ -0,0 +1 @@
+O ícone do feed Atom
diff --git a/src/names/feedicon_alt/ref.txt b/src/names/feedicon_alt/ref.txt
new file mode 100644
index 0000000..0cc9aff
--- /dev/null
+++ b/src/names/feedicon_alt/ref.txt
@@ -0,0 +1 @@
+The Atom feed icon
diff --git a/src/names/language/de.txt b/src/names/language/de.txt
new file mode 100644
index 0000000..12b1cb7
--- /dev/null
+++ b/src/names/language/de.txt
@@ -0,0 +1 @@
+Deutsch
diff --git a/src/names/language/en.txt b/src/names/language/en.txt
new file mode 100644
index 0000000..fb54c64
--- /dev/null
+++ b/src/names/language/en.txt
@@ -0,0 +1 @@
+English
diff --git a/src/names/language/eo.txt b/src/names/language/eo.txt
new file mode 100644
index 0000000..ddc6c02
--- /dev/null
+++ b/src/names/language/eo.txt
@@ -0,0 +1 @@
+Esperanto
diff --git a/src/names/language/es.txt b/src/names/language/es.txt
new file mode 100644
index 0000000..820e763
--- /dev/null
+++ b/src/names/language/es.txt
@@ -0,0 +1 @@
+Español
diff --git a/src/names/language/fr.txt b/src/names/language/fr.txt
new file mode 100644
index 0000000..8985d03
--- /dev/null
+++ b/src/names/language/fr.txt
@@ -0,0 +1 @@
+Français
diff --git a/src/names/language/pt.txt b/src/names/language/pt.txt
new file mode 100644
index 0000000..7dc0ba9
--- /dev/null
+++ b/src/names/language/pt.txt
@@ -0,0 +1 @@
+Português
diff --git a/src/names/tagline/de.txt b/src/names/tagline/de.txt
new file mode 100644
index 0000000..66ee0ac
--- /dev/null
+++ b/src/names/tagline/de.txt
@@ -0,0 +1 @@
+Robuste Software, für alle.
diff --git a/src/names/tagline/en.txt b/src/names/tagline/en.txt
new file mode 100644
index 0000000..3b35e4a
--- /dev/null
+++ b/src/names/tagline/en.txt
@@ -0,0 +1 @@
+Robust software, for everybody.
diff --git a/src/names/tagline/eo.txt b/src/names/tagline/eo.txt
new file mode 100644
index 0000000..37c8244
--- /dev/null
+++ b/src/names/tagline/eo.txt
@@ -0,0 +1 @@
+Fortika programaro, por ĉiuj.
diff --git a/src/names/tagline/es.txt b/src/names/tagline/es.txt
new file mode 100644
index 0000000..fd782d0
--- /dev/null
+++ b/src/names/tagline/es.txt
@@ -0,0 +1 @@
+Software robusto, para todos.
diff --git a/src/names/tagline/fr.txt b/src/names/tagline/fr.txt
new file mode 100644
index 0000000..3f48d21
--- /dev/null
+++ b/src/names/tagline/fr.txt
@@ -0,0 +1 @@
+Du logiciel robuste, pour tout le monde.
diff --git a/src/names/tagline/pt.txt b/src/names/tagline/pt.txt
new file mode 100644
index 0000000..0822daf
--- /dev/null
+++ b/src/names/tagline/pt.txt
@@ -0,0 +1 @@
+Software robusto, para todo mundo.
diff --git a/src/names/tagline/ref.txt b/src/names/tagline/ref.txt
new file mode 100644
index 0000000..3b35e4a
--- /dev/null
+++ b/src/names/tagline/ref.txt
@@ -0,0 +1 @@
+Robust software, for everybody.
diff --git a/src/pages/de b/src/pages/de
new file mode 120000
index 0000000..f93147f
--- /dev/null
+++ b/src/pages/de
@@ -0,0 +1 @@
+../content/de \ No newline at end of file
diff --git a/src/pages/eo b/src/pages/eo
new file mode 120000
index 0000000..93ea983
--- /dev/null
+++ b/src/pages/eo
@@ -0,0 +1 @@
+../content/eo \ No newline at end of file
diff --git a/src/pages/es b/src/pages/es
new file mode 120000
index 0000000..a203d0f
--- /dev/null
+++ b/src/pages/es
@@ -0,0 +1 @@
+../content/es \ No newline at end of file
diff --git a/src/pages/fr b/src/pages/fr
new file mode 120000
index 0000000..d655574
--- /dev/null
+++ b/src/pages/fr
@@ -0,0 +1 @@
+../content/fr \ No newline at end of file
diff --git a/src/static.conf b/src/static.conf
index 694f0fd..3d379c6 100644
--- a/src/static.conf
+++ b/src/static.conf
@@ -7,5 +7,5 @@ export sourcecode_url_prefix="$sourcecode_url/tree"
export author='EuAndreh'
export site_name="EuAndreh's website"
export feed_title="EuAndreh's blog"
-export feed_url='feed.articles.en.xml'
export feed_alternate_url='./'
+export me_urls='https://hachyderm.io/@euandreh'
diff --git a/src/symlinks.txt b/src/symlinks.txt
index ebd28aa..e67a6fa 100644
--- a/src/symlinks.txt
+++ b/src/symlinks.txt
@@ -72,3 +72,337 @@ feed.pastebins-by-category.en.nix.atom en/pastebin/feed.nix.xml
feed.tils-by-category.en.ci.atom en/til/feed.ci.xml
feed.tils-by-category.en.git.atom en/til/feed.git.xml
feed.tils-by-category.en.shell.atom en/til/feed.shell.xml
+
+
+# Legacy article permalinks and their code sidecars, from the
+# pre-rewrite URL scheme. Sidecars are aligned in document
+# order, not by index: three articles gained or lost a block
+# in the migration, so the numbering drifts.
+2021/04/29/a-relational-model-of-data-for-large-shared-data-banks-article-review.html en/blog/2021/04/29/relational-review.html
+2021/04/29/a-relational-model-of-data-for-large-shared-data-banks-article-review.html.0.txt en/blog/2021/04/29/relational-review.html.0.txt
+2021/04/29/a-relational-model-of-data-for-large-shared-data-banks-article-review.html.1.txt en/blog/2021/04/29/relational-review.html.1.txt
+2021/04/29/a-relational-model-of-data-for-large-shared-data-banks-article-review.html.2.txt en/blog/2021/04/29/relational-review.html.2.txt
+podcast/2020/12/19/a-test-entry.html en/podcast/2020/12/19/test-entry.html
+podcast/2020/12/19/a-test-entry.html.0.txt en/podcast/2020/12/19/test-entry.html.0.txt
+2021/02/17/ann-fallible-fault-injection-library-for-stress-testing-failure-scenarios.html en/blog/2021/02/17/fallible.html
+2021/02/17/ann-fallible-fault-injection-library-for-stress-testing-failure-scenarios.html.0.txt en/blog/2021/02/17/fallible.html.0.txt
+2021/02/17/ann-fallible-fault-injection-library-for-stress-testing-failure-scenarios.html.1.txt en/blog/2021/02/17/fallible.html.1.txt
+2021/02/17/ann-fallible-fault-injection-library-for-stress-testing-failure-scenarios.html.2.txt en/blog/2021/02/17/fallible.html.2.txt
+2021/02/17/ann-fallible-fault-injection-library-for-stress-testing-failure-scenarios.html.3.txt en/blog/2021/02/17/fallible.html.3.txt
+2021/02/17/ann-fallible-fault-injection-library-for-stress-testing-failure-scenarios.html.4.txt en/blog/2021/02/17/fallible.html.4.txt
+2021/02/17/ann-fallible-fault-injection-library-for-stress-testing-failure-scenarios.html.5.txt en/blog/2021/02/17/fallible.html.5.txt
+2021/02/17/ann-fallible-fault-injection-library-for-stress-testing-failure-scenarios.html.6.txt en/blog/2021/02/17/fallible.html.6.txt
+2021/01/26/ann-remembering-add-memory-to-dmenu-fzf-and-similar-tools.html en/blog/2021/01/26/remembering-ann.html
+2021/01/26/ann-remembering-add-memory-to-dmenu-fzf-and-similar-tools.html.0.txt en/blog/2021/01/26/remembering-ann.html.0.txt
+2021/01/26/ann-remembering-add-memory-to-dmenu-fzf-and-similar-tools.html.1.txt en/blog/2021/01/26/remembering-ann.html.1.txt
+2021/01/26/ann-remembering-add-memory-to-dmenu-fzf-and-similar-tools.html.2.txt en/blog/2021/01/26/remembering-ann.html.2.txt
+2021/01/26/ann-remembering-add-memory-to-dmenu-fzf-and-similar-tools.html.3.txt en/blog/2021/01/26/remembering-ann.html.4.txt
+2021/01/26/ann-remembering-add-memory-to-dmenu-fzf-and-similar-tools.html.4.txt en/blog/2021/01/26/remembering-ann.html.5.txt
+2021/01/26/ann-remembering-add-memory-to-dmenu-fzf-and-similar-tools.html.5.txt en/blog/2021/01/26/remembering-ann.html.6.txt
+2021/01/26/ann-remembering-add-memory-to-dmenu-fzf-and-similar-tools.html.6.txt en/blog/2021/01/26/remembering-ann.html.7.txt
+2021/01/26/ann-remembering-add-memory-to-dmenu-fzf-and-similar-tools.html.7.txt en/blog/2021/01/26/remembering-ann.html.8.txt
+til/2020/08/13/anchor-headers-and-code-lines-in-jekyll.html en/til/2020/08/13/code-jekyll.html
+til/2020/08/13/anchor-headers-and-code-lines-in-jekyll.html.0.txt en/til/2020/08/13/code-jekyll.html.0.txt
+til/2020/08/13/anchor-headers-and-code-lines-in-jekyll.html.1.txt en/til/2020/08/13/code-jekyll.html.1.txt
+til/2020/08/13/anchor-headers-and-code-lines-in-jekyll.html.2.txt en/til/2020/08/13/code-jekyll.html.2.txt
+til/2020/08/13/anchor-headers-and-code-lines-in-jekyll.html.3.txt en/til/2020/08/13/code-jekyll.html.3.txt
+til/2020/08/13/anchor-headers-and-code-lines-in-jekyll.html.4.txt en/til/2020/08/13/code-jekyll.html.4.txt
+til/2020/08/13/anchor-headers-and-code-lines-in-jekyll.html.5.txt en/til/2020/08/13/code-jekyll.html.5.txt
+til/2020/08/13/anchor-headers-and-code-lines-in-jekyll.html.6.txt en/til/2020/08/13/code-jekyll.html.6.txt
+screencast/2021/02/07/autoqemu-automate-installation-and-ssh-setup-of-iso-os-images.html en/screencast/2021/02/07/autoqemu.html
+screencast/2021/02/07/autoqemu-automate-installation-and-ssh-setup-of-iso-os-images.html.0.txt en/screencast/2021/02/07/autoqemu.html.0.txt
+til/2020/12/15/awk-snippet-shellcheck-all-scripts-in-a-repository.html en/til/2020/12/15/shellcheck-repo.html
+til/2020/12/15/awk-snippet-shellcheck-all-scripts-in-a-repository.html.0.txt en/til/2020/12/15/shellcheck-repo.html.0.txt
+til/2020/12/15/awk-snippet-shellcheck-all-scripts-in-a-repository.html.1.txt en/til/2020/12/15/shellcheck-repo.html.1.txt
+til/2020/12/15/awk-snippet-shellcheck-all-scripts-in-a-repository.html.2.txt en/til/2020/12/15/shellcheck-repo.html.2.txt
+til/2020/12/15/awk-snippet-shellcheck-all-scripts-in-a-repository.html.3.txt en/til/2020/12/15/shellcheck-repo.html.3.txt
+til/2020/12/15/awk-snippet-shellcheck-all-scripts-in-a-repository.html.4.txt en/til/2020/12/15/shellcheck-repo.html.4.txt
+til/2020/12/15/awk-snippet-shellcheck-all-scripts-in-a-repository.html.5.txt en/til/2020/12/15/shellcheck-repo.html.5.txt
+til/2020/12/15/awk-snippet-shellcheck-all-scripts-in-a-repository.html.6.txt en/til/2020/12/15/shellcheck-repo.html.6.txt
+til/2021/01/12/awk-snippet-send-email-to-multiple-recipients-with-curl.html en/til/2021/01/12/curl-awk-emails.html
+til/2021/01/12/awk-snippet-send-email-to-multiple-recipients-with-curl.html.0.txt en/til/2021/01/12/curl-awk-emails.html.0.txt
+til/2021/01/12/awk-snippet-send-email-to-multiple-recipients-with-curl.html.1.txt en/til/2021/01/12/curl-awk-emails.html.1.txt
+til/2021/01/12/awk-snippet-send-email-to-multiple-recipients-with-curl.html.2.txt en/til/2021/01/12/curl-awk-emails.html.2.txt
+til/2021/01/12/awk-snippet-send-email-to-multiple-recipients-with-curl.html.3.txt en/til/2021/01/12/curl-awk-emails.html.3.txt
+til/2021/01/12/awk-snippet-send-email-to-multiple-recipients-with-curl.html.4.txt en/til/2021/01/12/curl-awk-emails.html.4.txt
+til/2020/08/14/browse-a-git-repository-at-a-specific-commit.html en/til/2020/08/14/browse-git.html
+til/2020/08/14/browse-a-git-repository-at-a-specific-commit.html.0.txt en/til/2020/08/14/browse-git.html.0.txt
+til/2020/08/14/browse-a-git-repository-at-a-specific-commit.html.1.txt en/til/2020/08/14/browse-git.html.1.txt
+til/2020/08/14/browse-a-git-repository-at-a-specific-commit.html.2.txt en/til/2020/08/14/browse-git.html.3.txt
+til/2020/08/14/browse-a-git-repository-at-a-specific-commit.html.3.txt en/til/2020/08/14/browse-git.html.4.txt
+til/2020/08/14/browse-a-git-repository-at-a-specific-commit.html.4.txt en/til/2020/08/14/browse-git.html.5.txt
+til/2021/04/24/clojure-auto-curry.html en/til/2021/04/24/clojure-autocurry.html
+til/2021/04/24/clojure-auto-curry.html.0.txt en/til/2021/04/24/clojure-autocurry.html.0.txt
+til/2021/04/24/clojure-auto-curry.html.1.txt en/til/2021/04/24/clojure-autocurry.html.1.txt
+til/2021/04/24/clojure-auto-curry.html.2.txt en/til/2021/04/24/clojure-autocurry.html.2.txt
+til/2021/04/24/clojure-auto-curry.html.3.txt en/til/2021/04/24/clojure-autocurry.html.3.txt
+til/2021/04/24/common-lisp-argument-precedence-order-parameterization-of-a-generic-function.html en/til/2021/04/24/cl-generic-precedence.html
+til/2021/04/24/common-lisp-argument-precedence-order-parameterization-of-a-generic-function.html.0.txt en/til/2021/04/24/cl-generic-precedence.html.0.txt
+til/2021/04/24/common-lisp-argument-precedence-order-parameterization-of-a-generic-function.html.1.txt en/til/2021/04/24/cl-generic-precedence.html.1.txt
+til/2021/04/24/common-lisp-argument-precedence-order-parameterization-of-a-generic-function.html.2.txt en/til/2021/04/24/cl-generic-precedence.html.2.txt
+til/2021/04/24/common-lisp-argument-precedence-order-parameterization-of-a-generic-function.html.3.txt en/til/2021/04/24/cl-generic-precedence.html.3.txt
+til/2021/04/24/common-lisp-argument-precedence-order-parameterization-of-a-generic-function.html.4.txt en/til/2021/04/24/cl-generic-precedence.html.4.txt
+2020/11/07/diy-an-offline-bug-tracker-with-text-files-git-and-email.html en/blog/2020/11/07/diy-bugs.html
+til/2020/11/12/diy-bare-bones-ci-server-with-bash-and-nix.html en/til/2020/11/12/diy-nix-bash-ci.html
+til/2020/11/12/diy-bare-bones-ci-server-with-bash-and-nix.html.0.txt en/til/2020/11/12/diy-nix-bash-ci.html.0.txt
+pastebin/2021/06/08/debit-reading-session-sicp-solutions-pt-1.html en/pastebin/2021/06/08/reading-session-pt1.html
+pastebin/2021/06/08/debit-reading-session-sicp-solutions-pt-1.html.0.txt en/pastebin/2021/06/08/reading-session-pt1.html.0.txt
+2020/11/12/durable-persistent-trees-and-parser-combinators-building-a-database.html en/blog/2020/11/12/database-parsers-trees.html
+2020/11/12/durable-persistent-trees-and-parser-combinators-building-a-database.html.0.txt en/blog/2020/11/12/database-parsers-trees.html.0.txt
+2020/11/12/durable-persistent-trees-and-parser-combinators-building-a-database.html.1.txt en/blog/2020/11/12/database-parsers-trees.html.1.txt
+til/2021/08/11/encoding-and-decoding-javascript-bigint-values-with-reviver.html en/til/2021/08/11/js-bigint-reviver.html
+til/2021/08/11/encoding-and-decoding-javascript-bigint-values-with-reviver.html.0.txt en/til/2021/08/11/js-bigint-reviver.html.0.txt
+til/2021/08/11/encoding-and-decoding-javascript-bigint-values-with-reviver.html.1.txt en/til/2021/08/11/js-bigint-reviver.html.1.txt
+til/2021/08/11/encoding-and-decoding-javascript-bigint-values-with-reviver.html.2.txt en/til/2021/08/11/js-bigint-reviver.html.2.txt
+til/2021/08/11/encoding-and-decoding-javascript-bigint-values-with-reviver.html.3.txt en/til/2021/08/11/js-bigint-reviver.html.3.txt
+pastebin/2020/01/04/failure-on-guix-tex-live-importer.html en/pastebin/2020/01/04/guix-import-failure.html
+pastebin/2020/01/04/failure-on-guix-tex-live-importer.html.0.txt en/pastebin/2020/01/04/guix-import-failure.html.0.txt
+pastebin/2020/12/15/failure-with-relocatable-guix-pack-tarball.html en/pastebin/2020/12/15/guix-pack-fail.html
+pastebin/2020/12/15/failure-with-relocatable-guix-pack-tarball.html.0.txt en/pastebin/2020/12/15/guix-pack-fail.html.0.txt
+pastebin/2020/12/15/failure-with-relocatable-guix-pack-tarball.html.1.txt en/pastebin/2020/12/15/guix-pack-fail.html.1.txt
+2020/10/19/feature-flags-differences-between-backend-frontend-and-mobile.html en/blog/2020/10/19/feature-flags.html
+2020/10/19/feature-flags-differences-between-backend-frontend-and-mobile.html.0.txt en/blog/2020/10/19/feature-flags.html.0.txt
+2020/10/19/feature-flags-differences-between-backend-frontend-and-mobile.html.1.txt en/blog/2020/10/19/feature-flags.html.1.txt
+til/2020/11/08/find-broken-symlinks-with-find.html en/til/2020/11/08/find-broken-symlink.html
+til/2020/11/08/find-broken-symlinks-with-find.html.0.txt en/til/2020/11/08/find-broken-symlink.html.0.txt
+til/2020/11/08/find-broken-symlinks-with-find.html.1.txt en/til/2020/11/08/find-broken-symlink.html.1.txt
+pastebin/2023/07/22/funcallable-amop.html en/pastebin/2023/07/22/funcallable-amop.html
+pastebin/2023/07/22/funcallable-amop.html.0.txt en/pastebin/2023/07/22/funcallable-amop.html.0.txt
+pastebin/2018/07/13/gnu-guix-systemd-daemon-for-nixos.html en/pastebin/2018/07/13/guix-nixos-systemd.html
+pastebin/2018/07/13/gnu-guix-systemd-daemon-for-nixos.html.0.txt en/pastebin/2018/07/13/guix-nixos-systemd.html.0.txt
+til/2021/07/23/gpg-verification-of-git-repositories-without-tls.html en/til/2021/07/23/git-tls-gpg.html
+til/2021/07/23/gpg-verification-of-git-repositories-without-tls.html.0.txt en/til/2021/07/23/git-tls-gpg.html.0.txt
+til/2020/11/12/git-bisect-automation.html en/til/2020/11/12/git-bisect-automation.html
+til/2020/11/12/git-bisect-automation.html.0.txt en/til/2020/11/12/git-bisect-automation.html.0.txt
+til/2020/08/28/grep-online-repositories.html en/til/2020/08/28/grep-online.html
+til/2020/08/28/grep-online-repositories.html.0.txt en/til/2020/08/28/grep-online.html.0.txt
+til/2020/08/28/grep-online-repositories.html.1.txt en/til/2020/08/28/grep-online.html.1.txt
+til/2020/08/28/grep-online-repositories.html.2.txt en/til/2020/08/28/grep-online.html.2.txt
+pastebin/2020/11/27/guix-build-local-module.html en/pastebin/2020/11/27/guix-build-local.html
+pastebin/2020/11/27/guix-build-local-module.html.0.txt en/pastebin/2020/11/27/guix-build-local.html.0.txt
+pastebin/2020/11/27/guix-build-local-module.html.1.txt en/pastebin/2020/11/27/guix-build-local.html.1.txt
+pastebin/2020/11/27/guix-build-local-module.html.2.txt en/pastebin/2020/11/27/guix-build-local.html.2.txt
+pastebin/2018/07/13/guix-builder-user-creation-commands.html en/pastebin/2018/07/13/guixbuilder.html
+pastebin/2018/07/13/guix-builder-user-creation-commands.html.0.txt en/pastebin/2018/07/13/guixbuilder.html.0.txt
+2020/08/10/guix-inside-sourcehut-builds-sr-ht-ci.html en/blog/2020/08/10/guix-srht.html
+2020/08/10/guix-inside-sourcehut-builds-sr-ht-ci.html.0.txt en/blog/2020/08/10/guix-srht.html.0.txt
+2020/08/10/guix-inside-sourcehut-builds-sr-ht-ci.html.1.txt en/blog/2020/08/10/guix-srht.html.1.txt
+2020/08/10/guix-inside-sourcehut-builds-sr-ht-ci.html.2.txt en/blog/2020/08/10/guix-srht.html.2.txt
+pastebin/2020/02/14/guix-shebang.html en/pastebin/2020/02/14/guix-shebang.html
+pastebin/2020/02/14/guix-shebang.html.0.txt en/pastebin/2020/02/14/guix-shebang.html.0.txt
+pastebin/2018/07/13/guix-users-in-nixos-system-configuration.html en/pastebin/2018/07/13/guixbuilder-nixos.html
+pastebin/2018/07/13/guix-users-in-nixos-system-configuration.html.0.txt en/pastebin/2018/07/13/guixbuilder-nixos.html.0.txt
+2020/10/20/how-not-to-interview-engineers.html en/blog/2020/10/20/wrong-interviewing.html
+pastebin/2019/06/08/inconsistent-hash-of-buildgomodule.html en/pastebin/2019/06/08/inconsistent-hash.html
+pastebin/2019/06/08/inconsistent-hash-of-buildgomodule.html.0.txt en/pastebin/2019/06/08/inconsistent-hash.html.0.txt
+pastebin/2019/06/08/inconsistent-hash-of-buildgomodule.html.1.txt en/pastebin/2019/06/08/inconsistent-hash.html.1.txt
+pastebin/2019/06/08/inconsistent-hash-of-buildgomodule.html.2.txt en/pastebin/2019/06/08/inconsistent-hash.html.2.txt
+pastebin/2021/04/03/javascript-naive-slugify.html en/pastebin/2021/04/03/naive-slugify-js.html
+pastebin/2021/04/03/javascript-naive-slugify.html.0.txt en/pastebin/2021/04/03/naive-slugify-js.html.0.txt
+pastebin/2018/07/25/nix-exps.html en/pastebin/2018/07/25/nix-exps.html
+pastebin/2018/07/25/nix-exps.html.0.txt en/pastebin/2018/07/25/nix-exps.html.0.txt
+pastebin/2018/07/11/nix-pinning.html en/pastebin/2018/07/11/nix-pinning.html
+pastebin/2018/07/11/nix-pinning.html.0.txt en/pastebin/2018/07/11/nix-pinning.html.0.txt
+pastebin/2018/07/13/nix-string-padding.html en/pastebin/2018/07/13/nix-strpad.html
+pastebin/2018/07/13/nix-string-padding.html.0.txt en/pastebin/2018/07/13/nix-strpad.html.0.txt
+til/2021/01/17/posix-sh-and-shebangs.html en/til/2021/01/17/posix-shebang.html
+til/2021/01/17/posix-sh-and-shebangs.html.0.txt en/til/2021/01/17/posix-shebang.html.0.txt
+til/2021/01/17/posix-sh-and-shebangs.html.1.txt en/til/2021/01/17/posix-shebang.html.1.txt
+til/2021/01/17/posix-sh-and-shebangs.html.2.txt en/til/2021/01/17/posix-shebang.html.2.txt
+til/2020/09/05/pull-requests-with-git-the-old-school-way.html en/til/2020/09/05/oldschool-pr.html
+til/2020/09/05/pull-requests-with-git-the-old-school-way.html.0.txt en/til/2020/09/05/oldschool-pr.html.0.txt
+til/2020/09/05/pull-requests-with-git-the-old-school-way.html.1.txt en/til/2020/09/05/oldschool-pr.html.1.txt
+til/2020/09/05/pull-requests-with-git-the-old-school-way.html.2.txt en/til/2020/09/05/oldschool-pr.html.2.txt
+pastebin/2016/04/05/rpn-macro-setup.html en/pastebin/2016/04/05/rpn.html
+pastebin/2016/04/05/rpn-macro-setup.html.0.txt en/pastebin/2016/04/05/rpn.html.0.txt
+pastebin/2019/12/29/raku-tuple-type-annotation.html en/pastebin/2019/12/29/raku-tuple-type.html
+pastebin/2019/12/29/raku-tuple-type-annotation.html.0.txt en/pastebin/2019/12/29/raku-tuple-type.html.0.txt
+pastebin/2019/12/29/raku-tuple-type-annotation.html.1.txt en/pastebin/2019/12/29/raku-tuple-type.html.1.txt
+2018/07/17/running-guix-on-nixos.html en/blog/2018/07/17/guix-nixos.html
+2018/07/17/running-guix-on-nixos.html.0.txt en/blog/2018/07/17/guix-nixos.html.0.txt
+2018/07/17/running-guix-on-nixos.html.1.txt en/blog/2018/07/17/guix-nixos.html.1.txt
+2018/07/17/running-guix-on-nixos.html.2.txt en/blog/2018/07/17/guix-nixos.html.2.txt
+2018/07/17/running-guix-on-nixos.html.3.txt en/blog/2018/07/17/guix-nixos.html.3.txt
+2018/07/17/running-guix-on-nixos.html.4.txt en/blog/2018/07/17/guix-nixos.html.4.txt
+2018/07/17/running-guix-on-nixos.html.5.txt en/blog/2018/07/17/guix-nixos.html.5.txt
+pastebin/2021/09/02/sicp-exercise-3-19.html en/pastebin/2021/09/02/sicp-3-19.html
+pastebin/2021/09/02/sicp-exercise-3-19.html.0.txt en/pastebin/2021/09/02/sicp-3-19.html.0.txt
+pastebin/2021/09/02/sicp-exercise-3-19.html.1.txt en/pastebin/2021/09/02/sicp-3-19.html.1.txt
+pastebin/2021/09/03/sicp-persistent-amortized-o1-queue.html en/pastebin/2021/09/03/sicp-persistent-queue.html
+pastebin/2021/09/03/sicp-persistent-amortized-o1-queue.html.0.txt en/pastebin/2021/09/03/sicp-persistent-queue.html.0.txt
+pastebin/2021/09/03/sicp-persistent-amortized-o1-queue.html.1.txt en/pastebin/2021/09/03/sicp-persistent-queue.html.1.txt
+til/2020/12/29/svg-favicon.html en/til/2020/12/29/svg.html
+til/2020/12/29/svg-favicon.html.0.txt en/til/2020/12/29/svg.html.0.txt
+til/2020/12/29/svg-favicon.html.1.txt en/til/2020/12/29/svg.html.1.txt
+til/2020/12/29/svg-favicon.html.2.txt en/til/2020/12/29/svg.html.2.txt
+til/2020/10/11/search-changes-to-a-filename-pattern-in-git-history.html en/til/2020/10/11/search-git-history.html
+til/2020/10/11/search-changes-to-a-filename-pattern-in-git-history.html.0.txt en/til/2020/10/11/search-git-history.html.0.txt
+til/2020/10/11/search-changes-to-a-filename-pattern-in-git-history.html.1.txt en/til/2020/10/11/search-git-history.html.1.txt
+til/2020/08/16/search-in-git.html en/til/2020/08/16/git-search.html
+til/2020/08/16/search-in-git.html.0.txt en/til/2020/08/16/git-search.html.0.txt
+til/2020/08/16/search-in-git.html.1.txt en/til/2020/08/16/git-search.html.1.txt
+til/2020/08/16/search-in-git.html.2.txt en/til/2020/08/16/git-search.html.2.txt
+til/2020/09/04/send-emails-using-the-command-line-for-fun-and-profit.html en/til/2020/09/04/cli-email-fun-profit.html
+til/2020/09/04/send-emails-using-the-command-line-for-fun-and-profit.html.0.txt en/til/2020/09/04/cli-email-fun-profit.html.0.txt
+til/2020/09/04/send-emails-using-the-command-line-for-fun-and-profit.html.1.txt en/til/2020/09/04/cli-email-fun-profit.html.1.txt
+til/2020/09/04/send-emails-using-the-command-line-for-fun-and-profit.html.2.txt en/til/2020/09/04/cli-email-fun-profit.html.2.txt
+til/2020/09/04/send-emails-using-the-command-line-for-fun-and-profit.html.3.txt en/til/2020/09/04/cli-email-fun-profit.html.3.txt
+til/2020/08/12/simple-filename-timestamp.html en/til/2020/08/12/filename-timestamp.html
+til/2020/08/12/simple-filename-timestamp.html.0.txt en/til/2020/08/12/filename-timestamp.html.0.txt
+til/2020/08/12/simple-filename-timestamp.html.1.txt en/til/2020/08/12/filename-timestamp.html.1.txt
+pastebin/2021/08/11/spaces-around-h1-tags.html en/pastebin/2021/08/11/h1-spacing.html
+pastebin/2021/08/11/spaces-around-h1-tags.html.0.txt en/pastebin/2021/08/11/h1-spacing.html.0.txt
+pastebin/2021/08/11/spaces-around-h1-tags.html.1.txt en/pastebin/2021/08/11/h1-spacing.html.1.txt
+til/2020/11/30/storing-ci-data-on-git-notes.html en/til/2020/11/30/git-notes-ci.html
+til/2020/11/30/storing-ci-data-on-git-notes.html.0.txt en/til/2020/11/30/git-notes-ci.html.0.txt
+til/2020/11/30/storing-ci-data-on-git-notes.html.1.txt en/til/2020/11/30/git-notes-ci.html.1.txt
+til/2020/11/30/storing-ci-data-on-git-notes.html.2.txt en/til/2020/11/30/git-notes-ci.html.2.txt
+2020/11/08/the-next-paradigm-shift-in-programming-video-review.html en/blog/2020/11/08/paradigm-shift-review.html
+2020/08/31/the-database-i-wish-i-had.html en/blog/2020/08/31/database-i-wish-i-had.html
+til/2021/04/24/three-way-conditional-for-number-signs-on-lisp.html en/til/2021/04/24/scm-nif.html
+til/2021/04/24/three-way-conditional-for-number-signs-on-lisp.html.0.txt en/til/2021/04/24/scm-nif.html.0.txt
+til/2021/04/24/three-way-conditional-for-number-signs-on-lisp.html.1.txt en/til/2021/04/24/scm-nif.html.1.txt
+til/2021/04/24/three-way-conditional-for-number-signs-on-lisp.html.2.txt en/til/2021/04/24/scm-nif.html.2.txt
+til/2020/11/12/useful-bash-variables.html en/til/2020/11/12/useful-bashvars.html
+til/2020/11/12/useful-bash-variables.html.0.txt en/til/2020/11/12/useful-bashvars.html.0.txt
+til/2020/11/12/useful-bash-variables.html.1.txt en/til/2020/11/12/useful-bashvars.html.1.txt
+til/2020/11/12/useful-bash-variables.html.2.txt en/til/2020/11/12/useful-bashvars.html.2.txt
+2018/12/21/using-youtube-dl-to-manage-youtube-subscriptions.html en/blog/2018/12/21/ytdl-subs.html
+2018/12/21/using-youtube-dl-to-manage-youtube-subscriptions.html.0.txt en/blog/2018/12/21/ytdl-subs.html.0.txt
+2018/12/21/using-youtube-dl-to-manage-youtube-subscriptions.html.1.txt en/blog/2018/12/21/ytdl-subs.html.1.txt
+2018/12/21/using-youtube-dl-to-manage-youtube-subscriptions.html.2.txt en/blog/2018/12/21/ytdl-subs.html.2.txt
+2018/12/21/using-youtube-dl-to-manage-youtube-subscriptions.html.3.txt en/blog/2018/12/21/ytdl-subs.html.3.txt
+2018/12/21/using-youtube-dl-to-manage-youtube-subscriptions.html.4.txt en/blog/2018/12/21/ytdl-subs.html.4.txt
+2019/06/02/using-nixos-as-an-stateless-workstation.html en/blog/2019/06/02/nixos-stateless-workstation.html
+2019/06/02/using-nixos-as-an-stateless-workstation.html.0.txt en/blog/2019/06/02/nixos-stateless-workstation.html.0.txt
+2018/08/01/verifying-npm-ci-reproducibility.html en/blog/2018/08/01/npm-ci-reproducibility.html
+2018/08/01/verifying-npm-ci-reproducibility.html.0.txt en/blog/2018/08/01/npm-ci-reproducibility.html.0.txt
+2018/08/01/verifying-npm-ci-reproducibility.html.1.txt en/blog/2018/08/01/npm-ci-reproducibility.html.1.txt
+2018/08/01/verifying-npm-ci-reproducibility.html.2.txt en/blog/2018/08/01/npm-ci-reproducibility.html.2.txt
+2020/10/05/cargo2nix-dramatically-simpler-rust-in-nix.html en/blog/2020/10/05/cargo2nix.html
+2020/10/05/cargo2nix-dramatically-simpler-rust-in-nix.html.0.txt en/blog/2020/10/05/cargo2nix.html.0.txt
+2020/10/05/cargo2nix-dramatically-simpler-rust-in-nix.html.1.txt en/blog/2020/10/05/cargo2nix.html.1.txt
+til/2020/11/14/gpodder-as-a-media-subscription-manager.html en/til/2020/11/14/gpodder-media.html
+pastebin/2022/07/14/git-cleanup-command.html en/pastebin/2022/07/14/git-cleanup.html
+pastebin/2022/07/14/git-cleanup-command.html.0.txt en/pastebin/2022/07/14/git-cleanup.html.0.txt
+pastebin/2018/07/25/nix-show-derivation-sample-output.html en/pastebin/2018/07/25/nix-showdrv.html
+pastebin/2018/07/25/nix-show-derivation-sample-output.html.0.txt en/pastebin/2018/07/25/nix-showdrv.html.0.txt
+2020/10/05/swift2nix-run-swift-inside-nix-builds.html en/blog/2020/10/05/swift2nix.html
+2020/10/05/swift2nix-run-swift-inside-nix-builds.html.0.txt en/blog/2020/10/05/swift2nix.html.0.txt
+2020/10/05/swift2nix-run-swift-inside-nix-builds.html.1.txt en/blog/2020/10/05/swift2nix.html.1.txt
+2020/10/05/swift2nix-run-swift-inside-nix-builds.html.2.txt en/blog/2020/10/05/swift2nix.html.2.txt
+2020/11/14/local-first-software-you-own-your-data-in-spite-of-the-cloud-article-review.html en/blog/2020/11/14/local-first-review.html
+2020/11/14/local-first-software-you-own-your-data-in-spite-of-the-cloud-article-review.html.0.txt en/blog/2020/11/14/local-first-review.html.0.txt
+2020/11/14/local-first-software-you-own-your-data-in-spite-of-the-cloud-article-review.html.1.txt en/blog/2020/11/14/local-first-review.html.1.txt
+2020/11/14/local-first-software-you-own-your-data-in-spite-of-the-cloud-article-review.html.2.txt en/blog/2020/11/14/local-first-review.html.2.txt
+pastebin/2021/06/22/cloc-curl-and-wget.html en/pastebin/2021/06/22/curl-wget.html
+pastebin/2021/06/22/cloc-curl-and-wget.html.0.txt en/pastebin/2021/06/22/curl-wget.html.0.txt
+pastebin/2021/06/22/cloc-curl-and-wget.html.1.txt en/pastebin/2021/06/22/curl-wget.html.1.txt
+til/2020/08/12/nome-de-arquivo-com-timestamp-simplificado.html pt/hea/2020/08/12/arquivo-datado.html
+til/2020/08/12/nome-de-arquivo-com-timestamp-simplificado.html.0.txt pt/hea/2020/08/12/arquivo-datado.html.0.txt
+til/2020/08/12/nome-de-arquivo-com-timestamp-simplificado.html.1.txt pt/hea/2020/08/12/arquivo-datado.html.1.txt
+
+# Asset spellings the old site used
+styles.css style.css
+public.asc public.asc.txt
+music.html en/music.html
+
+
+# Non-English pages at their pre-rewrite slugs
+sobre.html pt/sobre.html
+a-propos.html fr/a-propos.html
+pri.html eo/pri.html
+musica.html pt/musica.html
+musique.html fr/musique.html
+muziko.html eo/muziko.html
+hea.html pt/hea/index.html
+aja.html fr/aja/index.html
+hml.html eo/hml/index.html
+hea-por-categoria.html pt/hea/categorias.html
+aja-par-categorie.html fr/aja/categories.html
+hml-lau-kategorio.html eo/hml/kategorioj.html
+artigos-por-categoria.html pt/blog/categorias.html
+articles-par-categorie.html fr/blog/categories.html
+artikoloj-lau-kategorio.html eo/blog/kategorioj.html
+pastebins.pt.html pt/pastebin/index.html
+pastebins.fr.html fr/pastebin/index.html
+pastebins.eo.html eo/pastebin/index.html
+pastebins-por-categoria.html pt/pastebin/categorias.html
+pastebins-par-categorie.html fr/pastebin/categories.html
+pastebins-lau-kategorio.html eo/pastebin/kategorioj.html
+podcast.pt.html pt/podcast/index.html
+podcast.fr.html fr/podcast/index.html
+podkasto.html eo/podkasto/index.html
+episodios-do-podcast-por-categoria.html pt/podcast/categorias.html
+episodes-de-podcast-par-categorie.html fr/podcast/categories.html
+podkastajoj-lau-kategorio.html eo/podkasto/kategorioj.html
+screencasts.pt.html pt/screencast/index.html
+screencasts.fr.html fr/screencast/index.html
+screencasts.eo.html eo/screencast/index.html
+screencasts-por-categoria.html pt/screencast/categorias.html
+screencasts-par-categorie.html fr/screencast/categories.html
+screencasts-lau-kategorio.html eo/screencast/kategorioj.html
+
+# Non-English feeds
+feed.articles.pt.atom pt/blog/feed.xml
+feed.articles.fr.atom fr/blog/feed.xml
+feed.articles.eo.atom eo/blog/feed.xml
+feed.posts.pt.atom pt/blog/feed.xml
+feed.posts.fr.atom fr/blog/feed.xml
+feed.posts.eo.atom eo/blog/feed.xml
+feed.tils.pt.atom pt/hea/feed.xml
+feed.tils.fr.atom fr/aja/feed.xml
+feed.tils.eo.atom eo/hml/feed.xml
+feed.tils-by-category.pt.shell.atom pt/hea/feed.shell.xml
+feed.pastebins.pt.atom pt/pastebin/feed.xml
+feed.pastebins.fr.atom fr/pastebin/feed.xml
+feed.pastebins.eo.atom eo/pastebin/feed.xml
+feed.podcasts.pt.atom pt/podcast/feed.xml
+feed.podcasts.fr.atom fr/podcast/feed.xml
+feed.podcasts.eo.atom eo/podkasto/feed.xml
+feed.screencasts.pt.atom pt/screencast/feed.xml
+feed.screencasts.fr.atom fr/screencast/feed.xml
+feed.screencasts.eo.atom eo/screencast/feed.xml
+
+# Media at their pre-rewrite paths
+podcast/2020/12/19/a-test-entry.ogg en/podcast/2020/12/19/test-entry.ogg
+podcast/2020/12/19/a-test-entry.ogg.torrent en/podcast/2020/12/19/test-entry.ogg.torrent
+screencast/2021/02/07/autoqemu-automate-installation-and-ssh-setup-of-iso-os-images.webm en/screencast/2021/02/07/autoqemu.webm
+screencast/2021/02/07/autoqemu-automate-installation-and-ssh-setup-of-iso-os-images.webm.torrent en/screencast/2021/02/07/autoqemu.webm.torrent
+
+# Pre-compressed twins the old site served
+index.html.gz en/index.html.gz
+styles.css.gz style.css.gz
+static/atom.svg.gz img/atom.svg.gz
+static/envelope.svg.gz img/envelope/light.svg.gz
+static/link.svg.gz img/link/light.svg.gz
+static/lock.svg.gz img/lock/light.svg.gz
+static/lord-favicon.svg.gz img/favicon.svg.gz
+
+# Slide listings at their pre-rewrite slugs. The decks themselves
+# are PDFs; their pages link to them.
+slides.html en/slide/index.html
+slides-by-category.html en/slide/categories.html
+eslaides.html pt/eslaides/index.html
+eslaides-por-categoria.html pt/eslaides/categorias.html
+diapositives.html fr/diapositives/index.html
+diapositives-par-categorie.html fr/diapositives/categories.html
+lumbildoj.html eo/lumbildoj/index.html
+lumbildoj-lau-kategorio.html eo/lumbildoj/kategorioj.html
+slides/2020/10/19/rollout-feature-flag-experiment-operational-toggle.html en/slide/2020/10/19/feature-flags.html
+slides/2020/11/14/on-local-first-beyond-the-crdt-silver-bullet.html en/slide/2020/11/14/local-first-hype.html
+feed.slides.en.atom en/slide/feed.xml
+feed.slides.pt.atom pt/eslaides/feed.xml
+feed.slides.fr.atom fr/diapositives/feed.xml
+feed.slides.eo.atom eo/lumbildoj/feed.xml
+2019/06/02/stateless-os.html en/blog/2019/06/02/nixos-stateless-workstation.html
+2019/06/02/stateless-os.html.0.txt en/blog/2019/06/02/nixos-stateless-workstation.html.0.txt
+pastebin/raku-tuple-type-annotation.html en/pastebin/2019/12/29/raku-tuple-type.html
+pastebin/raku-tuple-type-annotation.html.0.txt en/pastebin/2019/12/29/raku-tuple-type.html.0.txt
+pastebin/raku-tuple-type-annotation.html.1.txt en/pastebin/2019/12/29/raku-tuple-type.html.1.txt
+pastebin/inconsistent-hash-of-buildgomodule.html en/pastebin/2019/06/08/inconsistent-hash.html
+pastebin/inconsistent-hash-of-buildgomodule.html.0.txt en/pastebin/2019/06/08/inconsistent-hash.html.0.txt
+pastebin/inconsistent-hash-of-buildgomodule.html.1.txt en/pastebin/2019/06/08/inconsistent-hash.html.1.txt
+pastebin/inconsistent-hash-of-buildgomodule.html.2.txt en/pastebin/2019/06/08/inconsistent-hash.html.2.txt
+2021/01/26/ann-remembering-add-memory-to-dmenu-fzf-and-similar-tools.html.8.txt en/blog/2021/01/26/remembering-ann.html.8.txt
+til/2020/08/14/browse-a-git-repository-at-a-specific-commit.html.5.txt en/til/2020/08/14/browse-git.html.5.txt