porcellis.com

commit ca58080db15ecf84d79885621afb1df7eba20cdc

Author: Pedro Lucas Porcellis <porcellis@eletrotupi.com>

wip

 content/blog/a-magia-do-git-rebase.en.md | 129 
 content/blog/anunciando-hidrocor.md | 8 
 content/blog/comunidades-foss-devem-ser-construidas-com-foss.en.md | 83 
 content/blog/comunidades-foss-devem-ser-construidas-com-foss.md | 68 
 content/blog/minitest-a-elegancia-da-simplicidade.md | 7 
 content/blog/protonmail-e-a-falsa-privacidade.en.md | 92 
 content/new-server.html | 214 
 dynlib.html | 369 
 editing.html | 99 


diff --git a/content/blog/a-magia-do-git-rebase.en.md b/content/blog/a-magia-do-git-rebase.en.md
new file mode 100644
index 0000000000000000000000000000000000000000..c04e6b65c4c22763bb922f682e79ac525a71a0e8
--- /dev/null
+++ b/content/blog/a-magia-do-git-rebase.en.md
@@ -0,0 +1,129 @@
+---
+title: "The wonders of Git Rebase"
+date: 2021-04-29
+draft: false
+tags: ["git"]
+---
+
+One of the tools I find more useful on `git`, and which [I love the most][toot]
+is `git-rebase(1)`. It works around the premise that originally bought git to
+control version systems: that you *can and should* rework your repository
+history, to make it more organized and readable.
+
+[toot]: https://alfazema.club/@eletrotupi/105984543604974096
+
+Around the world, each project stablish its one convention of how to work with
+git, some of them use techniques like git-flow, others, just a branch and tags,
+others divide between `master/main` and a `production/release` branch. The
+company I work for convention is to always create a specific branch to work on a
+new feature, bug fix or whatever. Having done that, I adopt the discipline to
+(1) keep it up to date with the parent branch using `git pull --rebase
+origin/parent_branch` and (2) as I work on it, I try to keep it tidy using `git
+rebase -i HEAD~N`.
+
+## Rebase
+
+The rebase command has a very simple behavior, essencially it does not execute a
+merge (or does not generate a commit that introduces the changes upstream unto
+your local copy), rebase temporally remove all your work, pull newer commits
+unto your branch and then sequencially applies your patches on this updated
+branch state. The benefit being: the branch ends up updated and only contains a
+list of your newer commits, on a very coherent and readable way.
+
+The `-i` parameter on the command, is a shortcut for git start a interactive
+rebase, starting from the commit I have asked for [^1]. For example, `git rebase
+-i HEAD~5` will start a rebase up until my last 5 commits. It will proceed to
+show you this:
+
+[^1]: `HEAD~N`, means essencially from my HEAD commit up until N commits before
+  this.
+
+```ini
+pick 9fdd140 hooks: Flesh out the Hook::Destroy service test
+pick ecb296e resource: Flesh out the resource test
+pick bd115a0 hooks: Generate token when subscribing
+pick fcaba9d hooks: Flesh out the handler test
+pick 3926d4b doc: Add document about testing
+
+# Rebase bed2ff9..3926d4b onto bed2ff9 (5 commands)
+#
+# Commands:
+# p, pick <commit> = use commit
+# r, reword <commit> = use commit, but edit the commit message
+# e, edit <commit> = use commit, but stop for amending
+# s, squash <commit> = use commit, but meld into previous commit
+# f, fixup <commit> = like "squash", but discard this commit's log message
+# x, exec <command> = run command (the rest of the line) using shell
+# b, break = stop here (continue rebase later with 'git rebase --continue')
+# d, drop <commit> = remove commit
+# l, label <label> = label current HEAD with a name
+# t, reset <label> = reset HEAD to a label
+# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
+# .       create a merge commit using the original merge commit's
+# .       message (or the oneline, if no original merge commit was
+# .       specified). Use -c <commit> to reword the commit message.
+#
+# These lines can be re-ordered; they are executed from top to bottom.
+#
+# If you remove a line here THAT COMMIT WILL BE LOST.
+#
+# However, if you remove everything, the rebase will be aborted.
+#
+```
+
+That means, it will show my last five commits, and ask me what I want to do with
+them, presenting me the options to pick, remove or edit (`edit` or `reword`) or
+join them (`fixup`, `squash`).
+
+## Discipline
+
+My work plan, usually is the following: as I keep advancing on my work, I commit
+them locally, in a very vague way so I can understand what sort of milestone
+that represents. Whenever I feel I can present this work upstream I use _rebase_
+to rewrite, split or join commits forming a narrative of coherent and readable
+sequential steps.
+
+Seeking to write this narrative, I strive to write atomic changes that themself
+are capable of answer the question of _what has been done?_ and _will this break
+the tests that exists?_ [^2]
+
+[^2]: That means no `fix tests` or `fix typo` commits.
+
+A experience that has helped me a lot on this purpose, was to contribute to
+projects that utilize a public developer mailing list [^3], sending patches
+instead of creating Pull Requests. Specially because the work logic changes with
+that.  The way to work presented by tools like Github, GitLab and other similar
+derivations is to create a PR and keep adding commits _ad infinitum_ until the
+work on it is ready to merge. Yet, the logic behind git via email is of trying
+to be assertive as possible, avoiding sending giant patchsets [^4] with changes
+all over the place. One of the ways to deal with patchsets is that is possible
+to apply only a subset of the overall changes presented, and ask revision for
+specific patches that need some more work.
+
+[^3]: Which is the way git has been designed to work, notable mailing list
+  driven developments examples are the Linux Kernel, git, cgit, dwm and other
+  suckless tools,  Que é o formato de contribuição do qual o git foi feito para
+  funcionar originalmente, coreutils, musl-libc, freedesktop, vim, emacs, *BSD
+  and many others.
+
+[^4]: A patch is a commit, and a patchset is a collection of patches, somewhat
+  akin to a PR.
+
+Applying this logic, I strive to break a long patchset into several different
+patchsets [^5], each one with the most possible atomic patches, with a good
+description, organized by modules or systems [^6].
+
+[^5]: A example here could be organize _includes_, remove white spaces or any
+  other cosmetic changes on the source code that hasn't a direct connection to
+  what I was originally writing.
+
+[^6]: I generally like to use the following format for my commits: `system:
+  subsystem, description of what this commit does` and describe why this change
+  is necessary and my approach on the commit message.
+
+Concluding: git is fantastic. Having discipline to utilize it and learn how it
+works is a must for any programmer. One of the main differences between git and
+other VCS before him is that the history of the repository is not written in
+stone, is not read-only. Besides that, git has an extensive offline
+documentation on its manpages available to you to study it. I would strong
+recommend you to read git-rebase(1), giteveryday(7) and gitrevisions(7).




diff --git a/content/blog/anunciando-hidrocor.md b/content/blog/anunciando-hidrocor.md
new file mode 100644
index 0000000000000000000000000000000000000000..3f347ba33e6c2e65f72d533950fa29f72aecc0c5
--- /dev/null
+++ b/content/blog/anunciando-hidrocor.md
@@ -0,0 +1,8 @@
+---
+title: "Anunciando: hidrocor"
+date: 2021-10-03
+draft: false
+tags: []
+---
+
+




diff --git a/content/blog/comunidades-foss-devem-ser-construidas-com-foss.en.md b/content/blog/comunidades-foss-devem-ser-construidas-com-foss.en.md
new file mode 100644
index 0000000000000000000000000000000000000000..5dc625af36bfbd17bfc5352ab4ad864425ee2574
--- /dev/null
+++ b/content/blog/comunidades-foss-devem-ser-construidas-com-foss.en.md
@@ -0,0 +1,83 @@
+---
+title: "Comunidades Foss Devem Ser Construidas Com Foss"
+date: 2021-06-17T00:19:43-03:00
+draft: true
+tags: []
+---
+
+Recently, I've been noticed that some free software communities have been set a
+foot unto proprietary platforms [^1].
+
+<details>
+  <summary>Disclaimer</summary>
+
+  <p>
+    In this article I'll be using interchambeable the terms <strong>free
+    software</strong> and <strong>open source</strong>. The reason is to use a
+    single word, even though I comprehend they represent different concepts.
+  </p>
+</details>
+
+[^1]: O [libera.chat]() por exemplo, se tornou o lar de projetos FOSS, mas mantém
+  seus perfis oficiais em lugares como o Facebook e Twitter
+
+This annoys me for a very obvious reason: I don't expect that a free software be
+anchored on a proprietary platform. It seems to me undoubtedly incoherent. Few
+people perceive that, but each free software brought unto this world, serves as
+a contra-point to it's proprietary peer, a contra-point that believes that the
+commoning and the collective is better than individual and property.
+
+With that in mind, it's at least strange when a community of a free
+software/hardware is organized around or participate of a proprietary platform.
+
+Some makers, will argue about lacking of good free software to built
+communities, and that proprietary software are more friendly, or even that "it's
+where our community is". Look, according to the FSF's definition of free
+software or even OSI, one of the most crucial points is the fact that free
+software empowers people to collaborate, it allows you to contribute directly
+with this software. Using this path, you can not only directly build and improve
+this so called "bad platforms", the mere act of using them, can help to validate
+and boost whoever built or maintain it.
+
+So, if I'm a FOSS developer and whish that the options of free software
+proliferate, and that the quality improve and that the ecosystem as a whole
+flourishes, it is necessary that I bet my horses on the options that exists
+today, and that I build together as well. This bet is only _part of the process_
+and can't be see as a mere accessory.
+
+There are dozens of tools built on open protocols, standardized and portable
+between different architectures, OSes, etc. Tools that are not isolated into
+some proprietary software, surrounded with walls, harvestening everyones data
+and even inacessible for a part of people that wishes to be part of your
+community.
+
+Some tools, like Discord are well known to
+
+proliferate, you have to cast your lot with more open source tools. They're
+equally suited to the task as, say, Discord.
+
+Choosing proprietary tools and platforms can also easily serve to reduce your
+pool of potential contributors who don't want to use proprietary tools, and
+especially those who don't want to install proprietary software on their own
+computers, like the Discord client.
+
+Some people simply cannot use something like Discord at all - it has a high
+hardware requirements floor, poor to mediocre Linux support and completely
+absent support for systems like BSD, not to mention grave accessibility
+problems. Only an open protocol enables anyone to build the clients that suit
+their needs, and are not dependent on the whims of some corporate board of
+director's determination of your merit (i.e. financial exploitability) as a
+participant.
+
+Muitas dessas ferramentas, como por exemplo Discord são conhecidos por exigirem
+muitos recursos para pouco benefícios que de fato entregam, lugares como
+Facebook também são mundialmente conhecidos por minerar dados e violar a
+privacidade dos seus usuários. Qual a justificativa de um software livre
+construído como um contra-ponto estar utilizando essas plataformas?
+
+Escolher participar e construir comunidades em cima de softwares proprietários é
+uma escolha contraditória por si só, e ela ameaça justamente afastar as pessoas
+dos ideais do pelo qual você construiu o seu software em primeiro lugar, dê a
+chance e construa sua comunidade em cima do que é aberto, livre e público. E
+contribua para que esse ecosistema como um todo cresça ainda mais, esse é o
+espírito do software livre.




diff --git a/content/blog/comunidades-foss-devem-ser-construidas-com-foss.md b/content/blog/comunidades-foss-devem-ser-construidas-com-foss.md
new file mode 100644
index 0000000000000000000000000000000000000000..63377958df017ff9da90bd491f10b593d8bc653a
--- /dev/null
+++ b/content/blog/comunidades-foss-devem-ser-construidas-com-foss.md
@@ -0,0 +1,68 @@
+---
+title: "Comunidades FOSS devem ser Construidas com FOSS"
+date: 2021-06-17
+draft: false
+tags: []
+---
+
+Recentemente tenho notado que diversas comunidades de software livre [^1] tem se
+ancorado em cima de softwares que não são livres tampouco de código aberto.
+
+<details>
+  <summary>Aviso</summary>
+
+  <p>
+    Nesse texto vou mencionar de forma intercambeável software
+    <strong>livre</strong> e <strong>código aberto</strong>. As razões são
+    apenas por trabalhar como um único conceito, mesmo conhecendo e
+    compreendendo as diferenças éticas e conceituais de cada termo.
+  </p>
+</details>
+
+[^1]: O [libera.chat]() por exemplo, se tornou o lar de projetos FOSS, mas mantém
+  seus perfis oficiais em lugares como o Facebook e Twitter
+
+Isso me incomoda por uma razão obvia, eu não espero que um software livre esteja
+ancorado em cima de uma plataforma proprietária. Me parece indubitavelmente
+incoerente. Poucas pessoas percebem isso, mas a cada software livre trazido
+nesse mundo, ele serve como um contra-ponto ao seu par proprietário, um
+contraponto que acredita que o _comum_ e _coletivo_ é melhor do que a
+individualidade e a propriedade.
+
+Pensando nisso, é no mínimo estranho quando uma comunidade de um
+software/hardware aberto está organizada em torno ou participa de uma plataforma
+proprietária.
+
+Alguns criadores, argumentam da falta de bons software livres para construir
+comunidades, que os softwares proprietários são mais amigáveis, ou que é "onde a
+comunidade está". Veja bem, de acordo com a definição de software livre da FSF
+ou da OSI, um dos pontos mais cruciais é o fato de que permite que as pessoas
+possam colaborar, eu posso diretamente contribuir com a construção desse
+software. Tendo isso em mente, eu não só posso ajudar a construir e melhorar
+essas ditas plataformas ruins, como eu utilizar elas pode inclusive impulsionar
+e validar o trabalho feito por quem a criou/mantém.
+
+Então veja, se eu sou um desenvolvedor de software livre e desejo que as opções
+de softwares livres se proliferem, que a qualidade do mesmo melhore e que o
+ecosistema como um todo floreie, é necessário que eu aposte meus cavalos nas
+opções que existem hoje, e que eu construa junto também. Essa aposta é _parte do
+processo_ e não pode ser vista como um mero acessório.
+
+Existem dezenas de ferramentas construídas em cima de protocolos abertos,
+padronizados e portáveis em diferentes arquiteturas, sistemas operacionais, etc.
+Ferramentas essas que não estão isoladas em algum software proprietário, rodeado
+de muros, lucrando com os dados de todo mundo e inacessíveis para uma parte da
+comunidade que deseja poder participar.
+
+Muitas dessas ferramentas, como por exemplo Discord são conhecidos por exigirem
+muitos recursos para pouco benefícios que de fato entregam, lugares como
+Facebook também são mundialmente conhecidos por minerar dados e violar a
+privacidade dos seus usuários. Qual a justificativa de um software livre
+construído como um contra-ponto estar utilizando essas plataformas?
+
+Escolher participar e construir comunidades em cima de softwares proprietários é
+uma escolha contraditória por si só, e ela ameaça justamente afastar as pessoas
+dos ideais do pelo qual você construiu o seu software em primeiro lugar, dê a
+chance e construa sua comunidade em cima do que é aberto, livre e público. E
+contribua para que esse ecosistema como um todo cresça ainda mais, esse é o
+espírito do software livre.




diff --git a/content/blog/minitest-a-elegancia-da-simplicidade.md b/content/blog/minitest-a-elegancia-da-simplicidade.md
new file mode 100644
index 0000000000000000000000000000000000000000..e9cb28838cecc71b2c99e0c5ada547a67de0dcd9
--- /dev/null
+++ b/content/blog/minitest-a-elegancia-da-simplicidade.md
@@ -0,0 +1,7 @@
+---
+title: "Minitest a Elegancia Da Simplicidade"
+date: 2021-04-09T09:40:31-03:00
+draft: true
+tags: []
+---
+




diff --git a/content/blog/protonmail-e-a-falsa-privacidade.en.md b/content/blog/protonmail-e-a-falsa-privacidade.en.md
new file mode 100644
index 0000000000000000000000000000000000000000..0bcc46cb98a0b14cb1add59295a9c137273ed072
--- /dev/null
+++ b/content/blog/protonmail-e-a-falsa-privacidade.en.md
@@ -0,0 +1,92 @@
+---
+title: "Protonmail and the false sense of privacy"
+date: 2021-09-24
+draft: false
+tags: ["opinion", "privacy"]
+---
+
+I like e-mail as a means of communication, specially for three factors: (1) is
+built around open protocols (2) is, at a certain degree, universal and the
+foundation of having a identity online while being accessible and simple (3) it
+exists at least 50 years. With that being said, some necessities arise that
+weren't given too much thought when it were created originally: the desire for
+privately communicate with others.
+
+There's a some options to obtain private communication when using email, but
+none of those are really simple and when they do present as simple, I usually
+get suspicious. For instance, some weeks ago, there was a headline about a
+activist going to jail after Protonmail ~~leaked~~ delivered the IP address (and
+by which you can obtain the localization of someone) of this activist. It's at
+least odd, given that Protonmail advertise itself as if it's **the** way of
+"securely sending emails", "regain your privacy", "don't let anyone spy the
+content of your emails". In a certain way, posing as a magnificent and
+innovative tecnology that with one stroke magically solves the hairy and ugly
+problem that is sending emails privately. This is pure marketing, as it seems.
+
+Some people would even argue that this is a strategy known as _Embrace, Extend,
+Extinguish_ and perhaps it is. This strategy works by adopting a technology that
+exists already in the wild, "fixing" it and eventually when a large portion of
+its users are using the "improved" version, they kill the old one, efectually
+locking users at the new, proprietary one.
+
+The best weapon for this strategy is marketing. You need it to introject into
+users the idea that in order to communicate privately, you'll need a Proton mail
+account — after all, the rest is arcaic and complex — and barely mentions that
+they use open standards and technologies. That anyone with a computer can use:
+all you need is to generate a PGP key-pair [^1] and distribute your public key
+to other people so they can send you encrypted emails.
+
+Not only marketing do, of course, there's also the lack of transparent
+communication. Instead of doing like Gmail, which practically makes impossible
+to send emails using your personal PGP key, Protonmail allows you, but they dont
+make it too easy. The only documentation about it, is hidden on a FAQ, and I
+only discovered by poking at the docs, which by the way is not even translated.
+Not only this, but Proton tricks you into feeling very insecured about doing
+this whole process, showing you messages that yell "don't do this, invite this
+contact to join our beautiful Proton family instead".
+
+Além disso, fazer falsas promessas de que eles não guardam nenhuma informação,
+contudo esse caso nos mostra que, sim eles guardam seu IP, sabe-se lá o que
+mais.
+
+E o pior problema de todos, é que essas pessoas que utilizam esse serviço, já
+estão com seus emails sequestrados, afinal para utilizar o Protonmail você
+precisa criar uma conta que te disponibiliza um email `fulano@protonmail.com`.
+
+Ou seja, se você quiser e precisar migrar para outro domínio, ou outro serviço,
+tens que garantir que todos os teus contatos e todos os lugares que
+possívelmente queiram entrar em contato com você saibam do seu novo email, o que
+não aconteceria se você utiliza um domínio próprio para email
+(`fulano@meudominio.com.br`).
+
+Esse último ponto eu considero grave, porque te priva de conseguir migrar entre
+provedores. Se você se sentir ameaçado ou achar que não quer mais usar o
+Protonmail, você tem um trabalho longo pela frente para conseguir sair do
+serviço, o que em geral faz as pessoas permanecerem, mesmo que não queiram mais
+utilizar.
+
+A solução, infelizmente, não é simples. Privacidade e criptografia é um direito
+fundamental, e ele com certeza não deve estar nas mãos de empresas e
+corporações. A forma mais simples, mesmo que pareça um absurdo, é de
+organizações hospedarem seu próprio serviço de email, dentro do seu domínio e
+usuários criarem e distribuirem as suas chaves públicas.
+
+Sim, você pode hospedar seu próprio servidor de email, basta ter um servidor e
+um domínio na internet. Sei que isso parece uma tarefa herculiana, mas não é. A
+escolha aqui está mais ligada a decisão entre praticidade versus segurança.
+Conforto versus privacidade.
+
+Se organizações querem lidar com a questão de email, a melhor forma é trabalhar
+em conjunto para construir ferramentas abertas que permitam o ecosistema
+florescer, trabalhar para criar processos, protocolos e utilitários que tornem
+mais simples alguém hospedar seu próprio email. A lógica é construir pontes para
+independência e não levantar muros "seguros".
+
+[^1]: Diversos clientes de email, como o mutt, Mozilla Thunderbird, oferecem de
+  forma transparente acesso à criptografia PGP, afinal PGP já existe há 30 anos,
+  e seu formato padronizado e aberto pode ser lido no RFC 4880 (em 88 páginas),
+  implementações existem à rodo e sua variante mais famosa é o GNU PGP, ou `gpg`
+  que provavelmente já está instalado em seu computador, seja ele Linux, BSD ou
+  macOS. O único que como sempre, fica de fora da festa é o Windows, que
+  necessita do programa `gpg4win` instalado, apesar que ele tem uma interface
+  gráfica simples e intuitiva ao contrário dos seus irmãos do Unix.




diff --git a/content/new-server.html b/content/new-server.html
new file mode 100644
index 0000000000000000000000000000000000000000..2850859d3dbf430ad05a8ca54b23150f03e12876
--- /dev/null
+++ b/content/new-server.html
@@ -0,0 +1,214 @@
+<h1>New Server Checklist</h1>
+<label>
+  <input type="checkbox" /> Set root password
+</label>
+<label>
+  <input type="checkbox" /> Generate fresh sshd host keys
+</label>
+<label>
+  <input type="checkbox" /> Create admin user
+</label>
+<label>
+  <input type="checkbox" /> Add admin to doas group and test doas
+</label>
+<label>
+  <input type="checkbox" /> SSH key added to admin's authorized_keys
+</label>
+<label>
+  <input type="checkbox" /> Disable root login via ssh
+</label>
+<label>
+  <input type="checkbox" /> Disable password login via ssh
+</label>
+<label>
+  <input type="checkbox" /> Set hostname
+</label>
+<label>
+  <input type="checkbox" /> Run system updates
+</label>
+<label>
+  <input type="checkbox" /> Reboot
+</label>
+<label>
+  <input type="checkbox" /> Install &amp; test postfix
+</label>
+
+<style>
+label { display: block; }
+pre { background: #eee; max-width: 720px; padding: 0.5rem; }
+</style>
+
+<h1>doas.conf</h1>
+
+<pre>
+# see doas.conf(5) for configuration details
+
+# Uncomment to allow group "admin" to become root
+# permit :admin
+permit nopass :admin
+permit nopass deploy cmd apk args upgrade -U
+permit nopass deploy cmd service args SERVICE restart
+permit nopass acme cmd nginx args -s reload
+</pre>
+
+<h1>acme setup</h1>
+<p>
+TODO: a package could be made to automate many of these steps
+</p>
+<label>
+	<input type="checkbox" />
+	<code>doas apk add uacme openssl moreutils</code>
+</label>
+
+<label>
+	<input type="checkbox" />
+	<code>doas useradd -md /var/lib/acme -s /sbin/nologin acme</code>
+</label>
+
+<label>
+	<input type="checkbox" />
+	<code>doas mkdir -p /etc/ssl/uacme/private /var/www/.well-known/acme-challenge</code>
+</label>
+
+<label>
+	<input type="checkbox" />
+	<code>doas chown acme:acme /etc/ssl/uacme /etc/ssl/uacme/private</code>
+</label>
+
+<label>
+	<input type="checkbox" />
+	<code>doas chmod g+rX /etc/ssl/uacme /etc/ssl/uacme/private</code>
+</label>
+
+<label>
+	<input type="checkbox" />
+	<code>doas chown acme:acme /var/www/.well-known/acme-challenge</code>
+</label>
+
+<label>
+	<input type="checkbox" />
+	<code>doas touch /var/log/acme.log</code>
+</label>
+
+<label>
+	<input type="checkbox" />
+	<code>doas chown acme:acme /var/log/acme.log</code>
+</label>
+
+<label>
+	<input type="checkbox" />
+	<code>doas vim /usr/local/bin/acme-update-certs</code>
+<pre style="margin-left: 1.5rem">#!/bin/sh -eu
+exec &gt;&gt;/var/log/acme.log 2>&1
+date
+
+stats() {
+	cert="/etc/ssl/uacme/$1/cert.pem"
+	if ! [ -e "$cert" ]
+	then
+		return
+	fi
+	expiration=$(date -d"$(openssl x509 -enddate -noout -in "$cert" \
+		| cut -d= -f2)" -D'%b %d %H:%M:%S %Y GMT' +'%s')
+	printf '# TYPE certificate_expiration gauge\n'
+	printf '# HELP certificate_expiration Timestamp when SSL certificate will expire\n'
+	printf 'certificate_expiration{instance="%s"} %s\n' "$1" "$expiration"
+}
+
+acme() {
+	site=$1
+	shift
+	/usr/bin/uacme -v -h /usr/share/uacme/uacme.sh issue $site $* || true
+	stats $site | curl --data-binary @- https://push.metrics.sr.ht/metrics/job/$site
+}
+
+acme DOMAIN SUBDOMAIN...
+doas nginx -s reload</pre>
+</label>
+
+<label>
+	<input type="checkbox" />
+	<code>doas chmod +x /usr/local/bin/acme-update-certs</code>
+</label>
+
+<label>
+	<input type="checkbox" />
+	<code>doas usermod -aG acme nginx</code>
+</label>
+
+<label>
+	<input type="checkbox" />
+	<code>doas -u acme uacme new sir@cmpwn.com</code>
+</label>
+
+<label>
+	<input type="checkbox" />
+	<code>doas -u acme crontab -e</code>
+<pre style="margin-left: 1.5rem">MAILTO=sir@cmpwn.com
+0 0 * * * chronic /usr/local/bin/acme-update-certs</pre>
+	</code>
+</label>
+
+<label>
+	<input type="checkbox" /> Update nginx configuration
+	(<code>ssl_certificate{,_key}</code> commented)
+</label>
+
+<label>
+	<input type="checkbox" />
+	<code>doas -u acme /usr/local/bin/acme-update-certs</code>
+</label>
+
+<label>
+	<input type="checkbox" />
+	<code>cat /var/log/acme.log # verify success</code>
+</label>
+
+<label>
+	<input type="checkbox" /> Update nginx configuration
+</label>
+
+<label>
+	<input type="checkbox" />
+	<code>doas chmod -R g+rX /etc/ssl/uacme /etc/ssl/uacme/private</code>
+</label>
+
+<label>
+	<input type="checkbox" />
+	<code>doas nginx -s reload</code>
+</label>
+
+<label>
+	<input type="checkbox" /> Verify website has working SSL
+</label>
+
+<h2>nginx config</h2>
+
+<pre>
+server {
+	listen 80;
+	listen [::]:80;
+	server_name DOMAIN;
+
+	location / {
+		return 302 https://$server_name$request_uri;
+	}
+
+	location ^~ /.well-known {
+		root /var/www;
+	}
+}
+
+server {
+	listen 443 ssl http2;
+	listen [::]:443 ssl http2;
+	server_name DOMAIN;
+	ssl_certificate /etc/ssl/uacme/DOMAIN/cert.pem;
+	ssl_certificate_key /etc/ssl/uacme/private/DOMAIN/key.pem;
+
+	gzip on;
+	gzip_types text/css text/html;
+
+	# ...
+}
+</pre>




diff --git a/dynlib.html b/dynlib.html
new file mode 100644
index 0000000000000000000000000000000000000000..81febd660c3fd27cd7771c7bc69ff60cacf4624b
--- /dev/null
+++ b/dynlib.html
@@ -0,0 +1,369 @@
+<!doctype html>
+<html lang="en">
+<meta charset="utf-8" />
+<title>Dynamic linking</title>
+<style>
+body { max-width: 720px; margin: 0 auto }
+img { display: block; margin: 0 auto }
+small { display: block; text-align: center }
+th, td { padding-right: 4rem; text-align: left }
+</style>
+<h1>Dynamic linking</h1>
+<h2>Do your installed programs share dynamic libraries?</h2>
+
+<p>
+Findings: <strong>not really</strong>
+
+<p>
+Over half of your libraries are used by fewer than 0.1% of your executables.
+
+<img src="https://l.sr.ht/PSEG.svg" alt="A plot showing that the number of times a dynamic library is used shows exponential decay" />
+<small>Number of times each dynamic library is required by a program</small>
+
+<p>
+<strong>libs.awk</strong>
+<pre>
+/\t.*\.so.*/ {
+	n=split($1, p, "/")
+	split(p[n], l, ".")
+	lib=l[1]
+	if (libs[lib] == "") {
+		libs[lib] = 0
+	}
+	libs[lib] += 1
+}
+END {
+	for (lib in libs) {
+		print libs[lib] "\t" lib
+	}
+}
+</pre>
+
+<p>
+<strong>Usage</strong>
+
+<pre>
+$ find /usr/bin -type f -executable -print \
+  | xargs ldd 2&gt;/dev/null \
+  | awk -f libs.awk \
+  | sort -rn &gt; results.txt
+$ awk '{ print NR "\t" $1 }' &lt; results.txt &gt; nresults.txt
+$ gnuplot
+gnuplot&gt; plot 'nresults.txt'
+</pre>
+
+<p>
+<a href="/dynlib.txt">my results</a>
+
+<p>
+<pre>
+$ find /usr/bin -type f -executable -print | wc -l
+5688
+$ head -n20 &lt; results.txt
+4496	libc
+4484	linux-vdso
+4483	ld-linux-x86-64
+2654	libm
+2301	libdl
+2216	libpthread
+1419	libgcc_s
+1301	libz
+1144	libstdc++
+805	liblzma
+785	librt
+771	libXdmcp
+771	libxcb
+771	libXau
+755	libX11
+703	libpcre
+667	libglib-2
+658	libffi
+578	libresolv
+559	libXext
+</pre>
+
+<h2>Is loading dynamically linked programs faster?</h2>
+
+<p>
+Findings: <strong>definitely not</strong>
+
+<table>
+  <thead>
+    <tr>
+      <th>Linkage</th>
+      <th>Avg. startup time</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>Dynamic</td>
+      <td style="text-align: right">137263 ns</td>
+    </tr>
+    <tr>
+      <td>Static</td>
+      <td style="text-align: right">64048 ns</td>
+    </tr>
+  </tbody>
+</table>
+
+<p>
+<strong>ex.c</strong>
+<pre>
+#include &lt;stdio.h&gt;
+#include &lt;stdlib.h&gt;
+#include &lt;time.h&gt;
+#include &lt;unistd.h&gt;
+
+int main(int argc, char *argv[]) {
+	struct timespec ts;
+	clock_gettime(CLOCK_MONOTONIC, &amp;ts);
+	fprintf(stdout, "%ld\t", ts.tv_nsec);
+	fflush(stdout);
+	if (argc == 1) {
+		char *args[] = { "", "", NULL };
+		execvp(argv[0], args);
+	} else {
+		fprintf(stdout, "\n");
+	}
+	return 0;
+}
+</pre>
+
+<p>
+<strong>test.sh</strong>
+<pre>
+#!/bin/sh
+i=0
+while [ $i -lt 1000 ]
+do
+	./ex
+	i=$((i+1))
+done
+</pre>
+
+<p>
+<strong>My results</strong>
+<pre>
+$ musl-gcc -o ex ex.c
+$ ./test.sh | awk 'BEGIN { sum = 0 } { sum += $2-$1 } END { print sum / NR }'
+137263
+$ musl-gcc -static -o ex ex.c
+$ ./test.sh | awk 'BEGIN { sum = 0 } { sum += $2-$1 } END { print sum / NR }'
+64048
+</pre>
+
+<h2>Wouldn't statically linked executables be huge?</h2>
+
+<p>
+Findings: <strong>not really</strong>
+
+<p>
+On average, dynamically linked executables use only 4.6% of the symbols on
+offer from their dependencies. A good linker will remove unused symbols.
+
+<img src="https://l.sr.ht/WzUp.svg" alt="A box plot showing most results are &lt;5%, with outliers evenly distributed up to 100%" />
+<small>% of symbols requested by dynamically linked programs from the libraries that it depends on</small>
+
+<p>
+<strong>nsyms.go</strong>
+<pre>
+package main
+
+import (
+	"bufio"
+	"fmt"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+)
+
+func main() {
+	ldd := exec.Command("ldd", os.Args[1])
+	rc, err := ldd.StdoutPipe()
+	if err != nil {
+		panic(err)
+	}
+	ldd.Start()
+
+	var libpaths []string
+	scan := bufio.NewScanner(rc)
+	for scan.Scan() {
+		line := scan.Text()[1:] /* \t */
+		sp := strings.Split(line, " ")
+		var lib string
+		if strings.Contains(line, "=&gt;") {
+			lib = sp[2]
+		} else {
+			lib = sp[0]
+		}
+		if !filepath.IsAbs(lib) {
+			lib = "/usr/lib/" + lib
+		}
+		libpaths = append(libpaths, lib)
+	}
+	ldd.Wait()
+	rc.Close()
+
+	syms := make(map[string]interface{})
+	for _, path := range libpaths {
+		objdump := exec.Command("objdump", "-T", path)
+		rc, err := objdump.StdoutPipe()
+		if err != nil {
+			panic(err)
+		}
+		objdump.Start()
+		scan := bufio.NewScanner(rc)
+		for i := 0; scan.Scan(); i++ {
+			if i &lt; 4 {
+				continue
+			}
+			line := scan.Text()
+			sp := strings.Split(line, " ")
+			if len(sp) &lt; 5 {
+				continue
+			}
+			sym := sp[len(sp)-1]
+			syms[sym] = nil
+		}
+		objdump.Wait()
+		rc.Close()
+	}
+
+	objdump := exec.Command("objdump", "-R", os.Args[1])
+	rc, err = objdump.StdoutPipe()
+	if err != nil {
+		panic(err)
+	}
+	objdump.Start()
+	used := make(map[string]interface{})
+	scan = bufio.NewScanner(rc)
+	for i := 0; scan.Scan(); i++ {
+		if i &lt; 5 {
+			continue
+		}
+		sp := strings.Split(scan.Text(), " ")
+		if len(sp) &lt; 3 {
+			continue
+		}
+		sym := sp[len(sp)-1]
+		used[sym] = nil
+	}
+	objdump.Wait()
+	rc.Close()
+
+	if len(syms) != 0 &amp;&amp; len(used) != 0 &amp;&amp; len(used) &lt;= len(syms) {
+		fmt.Printf("%50s\t%d\t%d\t%f\n", os.Args[1], len(syms), len(used),
+			float64(len(used)) / float64(len(syms)))
+	}
+}
+</pre>
+
+<p>
+<strong>Usage</strong>
+<pre>
+$ find /usr/bin -type f -executable -print | xargs -n1 ./nsyms &gt; results.txt
+$ awk '{ n += $4 } END { print n / NR }' &lt; results.txt
+</pre>
+
+<p>
+<a href="/nsyms.txt">my results</a>
+
+<h2>Will security vulnerabilities in libraries that have been statically linked
+  cause large or unmanagable updates?</h2>
+
+<p>
+Findings: <strong>not really</strong>
+
+<p>
+Not including libc, the only libraries which had "critical" or "high" severity
+vulnerabilities in 2019 which affected over 100 binaries on my system were dbus,
+gnutls, cairo, libssh2, and curl. 265 binaries were affected by the rest.
+
+<p>
+The total download cost to upgrade all binaries on my system which were affected
+by CVEs in 2019 is 3.8 GiB. This is reduced to 1.0 GiB if you eliminate glibc.
+
+<p>
+It is also unknown if any of these vulnerabilities would have been introduced
+<em>after</em> the last build date for a given statically linked binary; if so
+that binary would not need to be updated. Many vulnerabilities are also limited
+to a specific code path or use-case, and binaries which do not invoke that code
+path in their dependencies will not be affected. A process to ascertain this
+information in the wake of a vulnerability could be automated.
+
+<p>
+<a href="https://lists.archlinux.org/pipermail/arch-security/">arch-security</a>
+
+<p>
+<strong>extractcves.py</strong>
+<pre>
+import email.utils
+import mailbox
+import re
+import shlex
+import time
+
+pacman_re = re.compile(r'pacman -Syu .*')
+severity_re = re.compile(r'Severity: (.*)')
+
+mbox = mailbox.mbox("arch-security.mbox")
+for m in mbox.items():
+    m = m[1]
+    date = m["Date"]
+    for part in m.walk():
+        if part.is_multipart():
+            continue
+        content_type = part.get_content_type()
+        [charset] = part.get_charsets("utf-8")
+        if content_type == 'text/plain':
+            body = part.get_payload(decode=True).decode(charset)
+            break
+    pkgs = pacman_re.findall(body)
+    severity = severity_re.findall(body)
+    date = email.utils.parsedate(date)
+    if len(pkgs) == 0 or date is None:
+        continue
+    if date[0] &lt;= 2018 or date[0] &gt; 2019:
+        continue
+    severity = severity[0]
+    args = shlex.split(pkgs[0])
+    pkg = args[2].split("&gt;=")[0]
+    print(pkg, severity)
+</pre>
+
+<pre>
+$ python3 extractcves.py | grep Critical &gt; cves.txt
+$ xargs pacman -Ql &lt; cves.txt | grep \\.so | awk '{print $1}' | sort -u&gt;affected.txt
+# Manually remove packages like Firefox, Thunderbird, etc; write remainder.txt
+$ xargs pacman -Ql &lt; remainder.txt | grep '/usr/lib/.*.so$' | awk '{ print $2 }' &gt; libs.txt
+$ ldd /usr/bin/* &gt;ldd.txt
+$ ./scope.sh &lt;libs.txt | sort -nr &gt;sobjects.txt
+</pre>
+
+<p>
+<a href="/sobjects.txt">sobjects.txt</a> is a sorted list of shared objects and
+the number of executables that link to them. To find the total size of affected
+binaries, I ran the following command:
+
+<pre style="overflow-x: scroll">
+# With libc
+$ egrep -la 'libc.so|libm.so|libdl.so|libpthread.so|librt.so|libresolv.so|libdbus-1.so|libgnutls.so|libcairo.so|libutil.so|libssh2.so|libcurl.so|libcairo-gobject.so|libcrypt.so|libspice-server.so|libarchive.so|libSDL2-2.0.so|libmvec.so|libmagic.so|libtextstyle.so|libgettextlib-0.20.2.so|libgettextsrc-0.20.2.so|libMagickWand-7.Q16HDRI.so|libMagickCore-7.Q16HDRI.so|libbfd-2.34.0.so|libpolkit-gobject-1.so|libwebkit2gtk-4.0.so|libjavascriptcoregtk-4.0.so|libpolkit-agent-1.so|libgs.so|libctf.so|libSDL.so|libopcodes-2.34.0.so|libQt5WebEngine.so|libQt5WebEngineCore.so|libctf-nobfd.so|libcairo-script-interpreter.so' /usr/bin/* | xargs wc -c
+# Without libc
+$ egrep -la 'libdbus-1.so|libgnutls.so|libcairo.so|libssh2.so|libcurl.so|libcairo-gobject.so|libcrypt.so|libspice-server.so|libarchive.so|libSDL2-2.0.so|libmvec.so|libmagic.so|libtextstyle.so|libgettextlib-0.20.2.so|libgettextsrc-0.20.2.so|libMagickWand-7.Q16HDRI.so|libMagickCore-7.Q16HDRI.so|libbfd-2.34.0.so|libpolkit-gobject-1.so|libwebkit2gtk-4.0.so|libjavascriptcoregtk-4.0.so|libpolkit-agent-1.so|libgs.so|libctf.so|libSDL.so|libopcodes-2.34.0.so|libQt5WebEngine.so|libQt5WebEngineCore.so|libctf-nobfd.so|libcairo-script-interpreter.so' /usr/bin/* | xargs wc -c
+</pre>
+
+<h2>Doesn't static linking prevent <abbr title="address space layout randomization, a security technique">ASLR</abbr> from working?</h2>
+
+<p>
+<strong>No</strong>. 
+
+<p>
+We've had ASLR for statically linked binaries for some time now. It's called <a href="https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81498">static PIE</a>.
+
+<h2>Test environment</h2>
+<ul>
+  <li>Arch Linux, up-to-date as of 2020-06-25</li>
+  <li>2188 packages installed</li>
+  <li>gcc 10.1.0</li>
+</ul>




diff --git a/editing.html b/editing.html
new file mode 100644
index 0000000000000000000000000000000000000000..7b08430bb35877b3a6a1bef2eeac8818c3918aa6
--- /dev/null
+++ b/editing.html
@@ -0,0 +1,99 @@
+<!doctype html>
+<html lang="en">
+<meta charset="utf-8" />
+<title>Videos of people editing text</title>
+<style>
+body {
+  max-width: 720px;
+  margin: 0 auto;
+}
+
+video {
+  display: block;
+  width: 720px;
+  margin-top: 1rem;
+}
+</style>
+<h1>Videos of people editing text</h1>
+<p>
+Videos of people editing text, explaining the shortcuts they use, and
+recordings of their keyboards as they work. The purpose of this page is to
+provide a reference for people writing new software which involves text editing
+tasks.
+
+<p>
+These videos are licensed with
+<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, which
+permits re-distribution and modification so long as attribution is provided and
+derivative works use the same license.
+
+<p>
+<a href="#contribute">Can you contribute your own?</a>
+
+<h2>Resources</h2>
+<ul>
+  <li>
+    <a href="https://lord.io/blog/2019/text-editing-hates-you-too/">
+      Text Editing Hates You Too
+    </a>
+  </li>
+  <li>
+    <a href="https://gankra.github.io/blah/text-hates-you/">
+      Text Rendering Hates You
+    </a>
+  </li>
+</ul>
+
+<h2>Videos</h2>
+
+<video src="https://yukari.sr.ht/drew-devault-keyboard.webm" controls>
+  Your browser must support HTML5 video and webm to play this video.
+</video>
+
+<dl>
+  <dt>Source</dt>
+  <dd>Drew DeVault</dd>
+  <dt>Language</dt>
+  <dd>English</dd>
+  <dt>Keyboard</dt>
+  <dd>Kinesis Freestyle Pro; ANSI layout; QWERTY</dd>
+  <dt>Operating system</dt>
+  <dd>Linux 5.7.6</dd>
+  <dt>Software</dt>
+  <dd>gedit 3.36.2 (GTK+ 3.24.20), alacritty 0.5.0.1564.g3233ede0, vim 8.2.0814</dd>
+  <dt>Tags</dt>
+  <dd>#english #en #en-us #ansi #qwerty #modal-editing</dd>
+</dl>
+
+<h2 id="contribute">Contribute</h2>
+<p>
+I would be interested in adding your video to this page, provided that it
+demonstrates one of the following qualities which is not yet represented by
+another video:
+
+<ul>
+  <li>A natural language not yet represented</li>
+  <li>An operating system not yet represented</li>
+  <li>A keyboard layout or design not yet represented</li>
+  <li>Software with novel editing features</li>
+  <li>A novel workflow, e.g. screen reader or dictation software</li>
+</ul>
+
+<p>
+When preparing your video, please use a camera to capture a video of your
+keyboard. I recommend <a href="https://obsproject.com/">OBS</a> to capture a
+recording of both your screen and keyboard, as well as audio from your
+microphone. Please narrate your workflow in English, and include details which
+seem "obvious" to you; they might not be obvious to someone else. Contact
+<a href="mailto:sir@cmpwn.com">Drew DeVault &lt;sir@cmpwn.com&gt;</a>
+to list your video. Please include the following details:
+
+<ul>
+  <li>Your name, for attribution</li>
+  <li>The natural language you are demonstrating input for</li>
+  <li>Your keyboard model and layout</li>
+  <li>Your operating system and the names &amp; version numbers of your software</li>
+</ul>
+
+<p>
+Thank you!