<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Evilham's Blog</title><link href="https://evilham.eu/en/blog/" rel="alternate"></link><link href="https://evilham.eu/en/blog/feed.xml" rel="self"></link><id>urn:uuid:69ca9647-272f-3b00-a395-f1e74d47d509</id><updated>2025-06-25T00:00:00Z</updated><author><name></name></author><entry><title>FreeBSD adopting a port: maomao</title><link href="https://evilham.eu/en/blog/2025-FreeBSD-adopting-a-port-maomaowm/" rel="alternate"></link><updated>2025-06-25T00:00:00Z</updated><author><name></name></author><id>urn:uuid:e3a6051d-0f4d-3361-a21c-88bc21451c29</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;The &lt;a href="https://jointhefediverse.net/learn" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Fediverse&lt;/a&gt; is a wonderful place,
and the &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;#FreeBSD&lt;/a&gt; community over there is rather active :-).&lt;/p&gt;
&lt;p&gt;&lt;a href="https://snac.smithies.me.uk/justine/p/1750438853.942123" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Justine Smithies&lt;/a&gt; is one those people with whom we have had
&lt;a href="https://chaos.social/@evilham/111358518050155771" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;multiple&lt;/a&gt;
&lt;a href="https://chaos.social/@evilham/111365634983238736" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;constructive interactions&lt;/a&gt;,
and she has made lovely contributions to FreeBSD&amp;rsquo;s community, starting with her
enthusiasm!&lt;/p&gt;
&lt;p&gt;Recently, &lt;a href="https://snac.smithies.me.uk/justine/p/1750438853.942123" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;she&amp;rsquo;s been toying around&lt;/a&gt; with &lt;a href="https://github.com/DreamMaoMao/maomaowm" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;maomao&lt;/a&gt;, a Wayland
compositor.&lt;/p&gt;
&lt;p&gt;And &lt;a href="https://chaos.social/@evilham/114732177241015737" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;I promised&lt;/a&gt; to create a port if she committed to keeping it up to date
(neat trick to get new contributors!)&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s document both that process, and how she can keep those patches coming in
to the &lt;a href="https://cgit.freebsd.org/ports/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;ports tree&lt;/a&gt;!&lt;/p&gt;
&lt;h1 id="table-of-contents"&gt;Table of Contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#freebsd-and-the-ports-tree"&gt;FreeBSD and the ports tree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#wayland-on-freebsd"&gt;Wayland on FreeBSD?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#maomao"&gt;Maomao&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#getting-started-with-ports"&gt;Getting started with ports&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#setting-up-poudriere8"&gt;Setting up poudriere(8)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#getting-the-ports-tree"&gt;Getting the ports tree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#creating-the-port"&gt;Creating the port&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#building-the-port"&gt;Building the port&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#testing-the-port"&gt;Testing the port&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#updating-the-port"&gt;Updating the port&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#creating-the-patch"&gt;Creating the patch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#submitting-the-patch"&gt;Submitting the patch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusions"&gt;Conclusions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="freebsd-and-the-ports-tree"&gt;FreeBSD and the ports tree&lt;/h1&gt;
&lt;p&gt;You may want to read my (now outdated) post on
&lt;a href="/en/blog/2020-FreeBSD-updating-a-port-twisted-python/"&gt;updating a FreeBD port&lt;/a&gt;, which goes into detail about the
Source trees, but most technical bits are outdated by now, given that
&lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; has moved away from &lt;code&gt;svn&lt;/code&gt; and now uses &lt;code&gt;git&lt;/code&gt; to manage code.&lt;/p&gt;
&lt;p&gt;The short version about &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;&amp;lsquo;s Source trees (plural!).
Without going into much detail: &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; has a
&lt;a href="https://cgit.freebsd.org/src/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;base (or src) tree&lt;/a&gt; and a &lt;a href="https://cgit.freebsd.org/ports/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;ports tree&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://cgit.freebsd.org/src/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;base tree&lt;/a&gt; contains the whole Operating System along with any
necessary system utilities and they are developed as a unit.&lt;/p&gt;
&lt;p&gt;While the &lt;a href="https://cgit.freebsd.org/ports/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;ports tree&lt;/a&gt; contains a &lt;em&gt;description&lt;/em&gt; of how to compile
third-party software under &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; and how to install it manually or
how to build an installable binary package, which can later on be built on
&lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;&amp;lsquo;s infrastructure and served by official &lt;code&gt;pkg&lt;/code&gt; repositories.&lt;/p&gt;
&lt;h1 id="wayland-on-freebsd"&gt;Wayland on FreeBSD?&lt;/h1&gt;
&lt;p&gt;Contrary to popular belief, Wayland is not something that is exclusive to Linux.
While it &lt;em&gt;is&lt;/em&gt; Linux-centric, it does support FreeBSD, even as a
&lt;a href="https://gitlab.freedesktop.org/wayland/wayland/-/blob/main/.gitlab-ci.yml?ref_type=heads#L306" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;test target on its CI&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Some things that are worth checking on this topic (though some might be
outdated, they are still worth it):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://papers.freebsd.org/2020/fosdem/raichoo-x11_and_wayland-a_tale_of_two_implementations/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;X11 and Wayland: a tale of two implementations&lt;/a&gt;, a talk by raichoo on writing hikari&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hub.darcs.net/raichoo/hikari" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;hikari&lt;/a&gt;: a FreeBSD-first Wayland composer&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.freebsd.org/en/books/handbook/wayland/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD Handbook: Chapter 6. Wayland&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://forums.freebsd.org/threads/example-tutorial-pure-wayland-desktop.85930/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Example/Tutorial: Pure wayland desktop&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are in fact right now 140 ports with &amp;lsquo;Wayland&amp;rsquo; on their description
(source: quick &lt;code&gt;pkg(8)&lt;/code&gt; magic).&lt;/p&gt;
&lt;h1 id="maomao"&gt;Maomao&lt;/h1&gt;
&lt;p&gt;As usual with Wayland things, it is written with Linux in mind, but as it often
happens, that does not mean the authors are hostile towards supporting
&lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;, they just might not be aware that the OS can be a target.&lt;/p&gt;
&lt;p&gt;In fact, the author recently &lt;a href="https://github.com/DreamMaoMao/mmsg/commit/5f89a00fdefa97b9defb8504eb06df7fedeff831" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;committed some&lt;/a&gt; code that helps &lt;code&gt;mmsg&lt;/code&gt;
(a companion tool to maomao) bulid on FreeBSD without patches.&lt;/p&gt;
&lt;p&gt;So if we run into issues, we can be reasonably sure that a patch will be
welcome.&lt;/p&gt;
&lt;h1 id="getting-started-with-ports"&gt;Getting started with ports&lt;/h1&gt;
&lt;p&gt;I have previously created several ports and update some from time to time, so
I was able to take shortcuts and creating the ports was relatively quick.&lt;/p&gt;
&lt;h2 id="setting-up-poudriere8"&gt;Setting up &lt;code&gt;poudriere(8)&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;With &lt;code&gt;poudriere(8)&lt;/code&gt;, we&amp;rsquo;ll be able to manage building the ports with jails
and producing a local repository of packages, that we can easily install, as
well as manage the ports tree.&lt;/p&gt;
&lt;p&gt;Once &lt;code&gt;poudriere(8)&lt;/code&gt; is installed with &lt;code&gt;pkg install poudriere&lt;/code&gt;, we can check
its configuration under &lt;code&gt;/usr/local/etc/poudriere.conf&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here is what I like to adapt from the sample file:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;# Use the ZFS dataset zroot/poudriere&lt;/span&gt;
&lt;span class="nv"&gt;ZPOOL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;zroot
&lt;span class="nv"&gt;ZROOTFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/poudriere
&lt;span class="c"&gt;# Which is mounted on /poudriere&lt;/span&gt;
&lt;span class="nv"&gt;BASEFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/poudriere
&lt;span class="c"&gt;# Suggested value&lt;/span&gt;
&lt;span class="nv"&gt;FREEBSD_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://download.FreeBSD.org
&lt;span class="c"&gt;# DNS resolution for jails&lt;/span&gt;
&lt;span class="nv"&gt;RESOLV_CONF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/resolv.conf
&lt;span class="c"&gt;# Use tempfs(5) for wrdir and data&lt;/span&gt;
&lt;span class="nv"&gt;USE_TMPFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;yes
&lt;span class="c"&gt;# Adapt this to your CPU count, leaving some for your regular use&lt;/span&gt;
&lt;span class="nv"&gt;PARALLEL_JOBS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;
&lt;span class="c"&gt;# I have found timestamps to be useful&lt;/span&gt;
&lt;span class="nv"&gt;TIMESTAMP_LOGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;yes
&lt;span class="c"&gt;# I like to keep old versions around&lt;/span&gt;
&lt;span class="nv"&gt;KEEP_OLD_PACKAGES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;yes
&lt;span class="nv"&gt;KEEP_OLD_PACKAGES_COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="c"&gt;# Use the &amp;#39;latest&amp;#39; ports branch when fetching pre-buit binaries&lt;/span&gt;
&lt;span class="nv"&gt;PACKAGE_FETCH_BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;latest&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="getting-the-ports-tree"&gt;Getting the ports tree&lt;/h2&gt;
&lt;p&gt;This can be done in multiple ways, the most convenient one is getting the
code from the same repository that is used by developers over anonymous git
with &lt;code&gt;poudriere(8)&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Fetch the ports tree&lt;/span&gt;
&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;poudriere&lt;span class="w"&gt; &lt;/span&gt;ports&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;default
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;UPDATE 2025-07-01:&lt;/strong&gt; 
If we want to keep the ports tree up to date, we will want to run
&lt;code&gt;poudriere ports -u -p default&lt;/code&gt; every once in a while.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="creating-the-port"&gt;Creating the port&lt;/h2&gt;
&lt;p&gt;Luckily, this is a fairly standard port, we start by identifying a somewhat
similar port, like
&lt;a href="https://cgit.freebsd.org/ports/tree/x11-wm/swayfx" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;&lt;code&gt;x11-wm/swayfx&lt;/code&gt;&lt;/a&gt;,
and using it as a starting point.&lt;/p&gt;
&lt;p&gt;Here is roughly what that looks like:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;PORTNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;maomao
&lt;span class="nv"&gt;PORTVERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.7.0

&lt;span class="nv"&gt;CATEGORIES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;x11-wm&lt;span class="w"&gt; &lt;/span&gt;wayland

&lt;span class="nv"&gt;MAINTAINER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ports@FreeBSD.org
&lt;span class="nv"&gt;COMMENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;A&lt;span class="w"&gt; &lt;/span&gt;streamlined&lt;span class="w"&gt; &lt;/span&gt;but&lt;span class="w"&gt; &lt;/span&gt;feature-rich&lt;span class="w"&gt; &lt;/span&gt;Wayland&lt;span class="w"&gt; &lt;/span&gt;compositor
&lt;span class="nv"&gt;WWW&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;https://github.com/DreamMaoMao/maomaowm

&lt;span class="c"&gt;# See: https://github.com/DreamMaoMao/maomaowm/issues/127&lt;/span&gt;
&lt;span class="nv"&gt;LICENSE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;GPLv3+
&lt;span class="nv"&gt;LICENSE_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WRKSRC&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;/LICENSE

&lt;span class="nv"&gt;USES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;meson&lt;span class="w"&gt; &lt;/span&gt;pkgconfig&lt;span class="w"&gt; &lt;/span&gt;desktop-file-utils

&lt;span class="nv"&gt;OPTIONS_DEFINE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;X11
&lt;span class="nv"&gt;OPTIONS_DEFAULT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;X11

&lt;span class="nv"&gt;X11_MESON_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;xwayland

&lt;span class="nv"&gt;USE_GITHUB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;yes
&lt;span class="nv"&gt;GH_ACCOUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;DreamMaoMao
&lt;span class="nv"&gt;GH_PROJECT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;maomaowm

&lt;span class="cp"&gt;.include &amp;lt;bsd.port.mk&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We then check the &lt;code&gt;meson.build&lt;/code&gt; file, which
&lt;a href="https://github.com/DreamMaoMao/maomaowm/blob/f202a16abe5bd0c79d698ad6090009c624330c60/meson.build#L74" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;lists all its dependencies&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And adapt entries acordingly:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;BUILD_DEPENDS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;wayland-scanner:graphics/wayland&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;wayland-protocols&amp;gt;0:graphics/wayland-protocols&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;evdev-proto&amp;gt;0:devel/evdev-proto
&lt;span class="nv"&gt;LIB_DEPENDS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;libscenefx-0.4.so:x11-toolkits/scenefx04&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;libwlroots-0.19.so:x11-toolkits/wlroots019

&lt;span class="nv"&gt;X11_LIB_DEPENDS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;libxcb-icccm.so:x11/xcb-util-wm
&lt;span class="nv"&gt;X11_USE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;XORG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;xcb,pixman
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I also identify that we need the same sample file, version and C flag patches
that &lt;code&gt;x11-wm/swayfx&lt;/code&gt; implements, so I copy and adapt those:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;post-patch&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="c"&gt;# Extract (snapshot) version from the port instead of meson.build&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;@&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REINPLACE_CMD&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;.nogit&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;s/git.found()/false/&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/unknown/s/@0@/${DISTVERSIONFULL}/&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WRKSRC&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;/meson.build
&lt;span class="c"&gt;# Fix C flags&lt;/span&gt;
&lt;span class="c"&gt;# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=275328&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;@&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REINPLACE_CMD&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;s/_POSIX_C_SOURCE=200809L/_XOPEN_SOURCE=700/&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WRKSRC&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;/meson.build
&lt;span class="c"&gt;# Install config file as a sample&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;@&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REINPLACE_CMD&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;s/&amp;#39;config.conf&amp;#39;/&amp;#39;config.conf&amp;#39;, rename: &amp;#39;config.conf.sample&amp;#39;/&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WRKSRC&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;/meson.build
&lt;span class="c"&gt;# Respect PREFIX for system-wide config&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;@&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REINPLACE_CMD&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;s,format(&amp;#39;/etc&amp;#39;),format(&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PREFIX&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/etc&amp;#39;),&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WRKSRC&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;/meson.build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We update / generate the &lt;code&gt;distinfo&lt;/code&gt; file with &lt;code&gt;make makesum&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then the &lt;code&gt;pkg-plist&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;maomao&lt;/span&gt;
&lt;span class="nv"&gt;@sample&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%%&lt;/span&gt;&lt;span class="n"&gt;ETCDIR&lt;/span&gt;&lt;span class="o"&gt;%%/&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;
&lt;span class="n"&gt;share&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;wayland&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sessions&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;maomao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;desktop&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And also create the &lt;code&gt;pkg-descr&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;streamlined&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;but&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;rich&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Wayland&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compositor&lt;/span&gt;

&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="nx"&gt;Lightweight&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Fast&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Build&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;

&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_Maomao_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lightweight&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_dwl_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;its&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;can&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;completed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;within&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;few&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Despite&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_maomao_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;does&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compromise&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;functionality&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="nx"&gt;Feature&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Highlights&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;In&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;addition&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;basic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;WM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;functionality&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Maomao&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;provides&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;supports&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;separate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;window&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;layouts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Smooth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;customizable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;complete&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;animations&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;window&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nx"&gt;enter&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;leave&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Excellent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;support&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v2&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;v3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Flexible&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;window&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;layouts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;easy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;switching&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scroller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;master&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;monocle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nx"&gt;spiral&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;etc&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Rich&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;window&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;states&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;swallow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;minimize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;maximize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;unglobal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;global&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nx"&gt;fakefullscreen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;etc&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Simple&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;yet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;powerful&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;external&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;configuration&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Sway&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;like&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;scratchpad&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;named&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;scratchpad&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Minimize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;window&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;scratchpad&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Hycov&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;like&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;overview&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Window&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;effects&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;scenefx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;corner&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And the &lt;code&gt;pkg-message&lt;/code&gt; file, which gets shown upon install:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;EOM&lt;/span&gt;
&lt;span class="nx"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;configuration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;works&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;best&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;installing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;foot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rofi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;alongside&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;maomao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="nx"&gt;Also&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;keep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;these&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;shortcuts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;foot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;terminal&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kill&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;up&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;down&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;focus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;super&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;quit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;maomao&lt;/span&gt;
&lt;span class="nx"&gt;EOM&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="building-the-port"&gt;Building the port&lt;/h2&gt;
&lt;p&gt;We do this with &lt;code&gt;poudriere(8)&lt;/code&gt;, which uses jails and ZFS snapshots.&lt;/p&gt;
&lt;p&gt;We first have to create the jail, if we haven&amp;rsquo;t already:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Create a builder jail for amd64 based on 14.3-RELEASE&lt;/span&gt;
&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;poudriere&lt;span class="w"&gt; &lt;/span&gt;jail&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;-j&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;143&lt;/span&gt;-amd64&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;14&lt;/span&gt;.3-RELEASE
&lt;span class="c1"&gt;# We also can keep the jail up to date with:&lt;/span&gt;
&lt;span class="c1"&gt;# poudriere jail -u -j 143-amd64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And we can now instruct &lt;code&gt;poudriere(8)&lt;/code&gt; to use our ports tree with this jail
to build this port:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Start jail 143-amd64, with the default ports tree, to build maomao&lt;/span&gt;
&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;poudriere&lt;span class="w"&gt; &lt;/span&gt;bulk&lt;span class="w"&gt; &lt;/span&gt;-j&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;143&lt;/span&gt;-amd64&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;default&lt;span class="w"&gt; &lt;/span&gt;x11-wm/maomao
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will fetch all needed packages along with their dependencies, so we only
have to build locally the port we need.&lt;/p&gt;
&lt;h2 id="testing-the-port"&gt;Testing the port&lt;/h2&gt;
&lt;p&gt;Once the port builds (which required a small code patch), we can install it
with:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Install the resulting maomao binary pkg&lt;/span&gt;
&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;pkg&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;/poudriere/data/packages/143Ramd64-default/All/maomao-.pkg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And we can then run it as usual.&lt;/p&gt;
&lt;h2 id="updating-the-port"&gt;Updating the port&lt;/h2&gt;
&lt;p&gt;The recurring work of updating the port lies in updating the &lt;code&gt;Makefile&lt;/code&gt; with
each release.
Usually dependencies are pretty stable, other than that we just have to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;change the &lt;code&gt;PORTVERSION&lt;/code&gt; to match the latest release&lt;ul&gt;
&lt;li&gt;In the case of &lt;code&gt;mmsg&lt;/code&gt;, that&amp;rsquo;d be &lt;code&gt;DISTVERSION&lt;/code&gt; and &lt;code&gt;GH_TAGNAME&lt;/code&gt;, this is
  because it does not currently have actual releases&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;update the checksums with &lt;code&gt;make makesum&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;test that the port builds and works&lt;/li&gt;
&lt;li&gt;submit a patch to &lt;a href="https://bugs.FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;bugs.FreeBSD.org&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="creating-the-patch"&gt;Creating the patch&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;UPDATE 2025-07-01:&lt;/strong&gt; 
This guide was missing how to create the patch files in order to submit them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;By default &lt;code&gt;poudriere-ports(8)&lt;/code&gt; uses the &lt;code&gt;git+https&lt;/code&gt; method of creating and
managing the ports tree, which means that the ports tree will itself be a
cloned copy of the &lt;code&gt;git(1)&lt;/code&gt; Ports repository!&lt;/p&gt;
&lt;p&gt;That means that we can use standard tooling to see the diffs, and to format
patches!&lt;/p&gt;
&lt;p&gt;In order to see everything that we have changed, we can use &lt;code&gt;git diff&lt;/code&gt; from
any directory in the repository.&lt;/p&gt;
&lt;p&gt;Now, let&amp;rsquo;s use &lt;code&gt;x11-wm/maomao&lt;/code&gt; as our &lt;strong&gt;current working directory&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We can see the changes to our port with &lt;code&gt;git diff .&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And we can also &amp;ldquo;stage&amp;rdquo; any changes to the port with &lt;code&gt;git add .&lt;/code&gt;
(or &lt;code&gt;git add x11-wm/maomao&lt;/code&gt; if running from the root of the Ports Tree).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Staging&amp;rdquo; in git-speak means, that we are marking some changes as something
that we want to commit to the repository.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In order to check what we are about to commit, we can run &lt;code&gt;git diff --staged&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once that looks OK, we can add a commit to our ports tree with &lt;code&gt;git commit&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And in order to generate a valid &lt;code&gt;.diff&lt;/code&gt; file, we ought to &lt;code&gt;cd&lt;/code&gt; into the
root of the Ports Tree, and run &lt;code&gt;git format-patch HEAD^..&lt;/code&gt;, this means:
&amp;ldquo;please generate the diff that only includes the very last commit&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;This will generate a file called &lt;code&gt;0001-ShortNameOfCommit.patch&lt;/code&gt;, which we can
then submit as an attachment to [bugs.FreeBSD.org][bugs.FreeBSD.org].&lt;/p&gt;
&lt;h2 id="submitting-the-patch"&gt;Submitting the patch&lt;/h2&gt;
&lt;p&gt;Now that we created two ports: &lt;code&gt;x11-wm/maomao&lt;/code&gt; and &lt;code&gt;deskutils/mmsg&lt;/code&gt;, we can
submit them and hope that they get merged soon.&lt;/p&gt;
&lt;p&gt;Since
&lt;a href="https://bsd.network/@DianeBruce/114717436550314844" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Diane Bruce offered&lt;/a&gt;
to help commit this, we add her as a reviewer.
And since further maintainership is going to &lt;a href="https://snac.smithies.me.uk/justine/p/1750438853.942123" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Justine&lt;/a&gt;, we add her
on CC.&lt;/p&gt;
&lt;p&gt;You can check the patch and its state &lt;a href="https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=287919" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;here&lt;/a&gt; for &lt;code&gt;x11-wm/maomao&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;deskutils/mmsg&lt;/code&gt;, we are waiting for the &lt;a href="https://github.com/DreamMaoMao/mmsg/issues/13" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;LICENSE to be clarified&lt;/a&gt;;
once that&amp;rsquo;s done, we&amp;rsquo;ll be able to submit the patch to FreeBSD&amp;rsquo;s ports.&lt;/p&gt;
&lt;p&gt;[pr_mmsg]: &lt;/p&gt;
&lt;h1 id="conclusions"&gt;Conclusions&lt;/h1&gt;
&lt;p&gt;Generally speaking, I would advise people who want to get into contributing to
the ports tree, to start by &lt;em&gt;updating&lt;/em&gt; ports that they use and care about.&lt;/p&gt;
&lt;p&gt;Committers and maintainers are usually happy to have a patch to quickly review
and test as opposed to having to do the whole work.&lt;/p&gt;
&lt;p&gt;In this case, since the port did not exist, it might be easier to
&lt;a href="https://xkcd.com/356/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;nerdsnipe&lt;/a&gt; someone into creating the ports, and then helping
out with the maintenance ;-) which is always super needed and appreciated!&lt;/p&gt;</content></entry><entry><title>Trying out Kubernetes: etcd</title><link href="https://evilham.eu/en/blog/2023-trying-out-kubernetes-3-etcd/" rel="alternate"></link><updated>2023-11-07T00:00:00Z</updated><author><name></name></author><id>urn:uuid:656e1f17-cb2d-3e0c-ad7a-5baecf290e6e</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;As part of &lt;a href="/en/blog/2023-trying-out-kubernetes-1-planning/"&gt;trying out Kubernetes&lt;/a&gt;, we are going to set
up &lt;code&gt;etcd&lt;/code&gt;!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;etcd&lt;/code&gt; is a &amp;ldquo;distributed reliable key-value store for the most critical
data of a distributed system &amp;ldquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is where Kubernetes &lt;a href="/en/blog/2023-trying-out-kubernetes-1-planning/#etcd"&gt;state lives&lt;/a&gt;, given how important that
is and how it seems to make sense to run it separated from the Kubernetes
cluster for High Availability, we will be doing just that, using
&lt;a href="https://wikitech.wikimedia.org/wiki/Etcd" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;WMF&amp;rsquo;s etcd guide&lt;/a&gt; as inspiration for decisions that are important.&lt;/p&gt;
&lt;h1 id="table-of-contents"&gt;Table of contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#vm-resources"&gt;VM Resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#installing"&gt;Installing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#setting-up-the-cluster"&gt;Setting up the cluster&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#dns-discovery"&gt;DNS discovery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#provisioning-parenthesis"&gt;Provisioning parenthesis&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#testing-the-cluster"&gt;Testing the cluster&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#basic-functioning"&gt;Basic functioning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#killing-cluster-members"&gt;Killing cluster members&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="vm-resources"&gt;VM Resources&lt;/h1&gt;
&lt;p&gt;According to
&lt;a href="https://etcd.io/docs/v3.5/op-guide/hardware/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;etcd&amp;rsquo;s Operations documentation&lt;/a&gt;
at least 8G RAM is recommended for each node,
and 2G should be the bare minimum, so we&amp;rsquo;ll change that for etcd hosts to
something in between:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# Changing in VM_BHYVE/k8s-etcd-1/k8s-etcd-1.conf
memory=4G
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The limiting factor here will likely be the mechanical hard drives :-), but this
is the hardware we have.&lt;/p&gt;
&lt;h1 id="installing"&gt;Installing&lt;/h1&gt;
&lt;p&gt;Since we are using Debian and there is (so far?) no good reason here to use
other binaries, we&amp;rsquo;ll just: &lt;code&gt;apt-get install etcd-server&lt;/code&gt;
(currently v3.4.23, that&amp;rsquo;s new enough, it defaults to &lt;code&gt;v3&lt;/code&gt; of the protocol).&lt;/p&gt;
&lt;h1 id="setting-up-the-cluster"&gt;Setting up the cluster&lt;/h1&gt;
&lt;p&gt;Since this is a small experiment, we could start the cluster
with statically-defined members and that would be a breeze to set up as it is
just a matter of passing the right CLI arguments / environment variables.&lt;/p&gt;
&lt;p&gt;But we want to try out
&lt;a href="https://etcd.io/docs/v3.5/op-guide/clustering/#dns-discovery" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;DNS discovery&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="dns-discovery"&gt;DNS discovery&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ll use &lt;code&gt;discovery-srv=lab.evilham.com&lt;/code&gt; and &lt;code&gt;discovery-srv-name=c1&lt;/code&gt;.
The &lt;code&gt;c1&lt;/code&gt; serves to tell this cluster apart from others which could exist
in the future :-).&lt;/p&gt;
&lt;p&gt;Which means we need to set up following DNS entries for this cluster with my
&lt;a href="https://git.sr.ht/~evilham/cdist-evilham/tree/main/item/type/__evilham_knot_dns/man.rst" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;cdist types&lt;/a&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;#&lt;span class="w"&gt; &lt;/span&gt;This&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;knot&lt;span class="w"&gt; &lt;/span&gt;client&lt;span class="w"&gt; &lt;/span&gt;syntax
zone-unset&lt;span class="w"&gt; &lt;/span&gt;evilham.com&lt;span class="w"&gt; &lt;/span&gt;lab

##&lt;span class="w"&gt; &lt;/span&gt;Server&lt;span class="w"&gt; &lt;/span&gt;SRV&lt;span class="w"&gt; &lt;/span&gt;records
zone-set&lt;span class="w"&gt;   &lt;/span&gt;evilham.com&lt;span class="w"&gt;     &lt;/span&gt;_etcd-server-ssl-c1._tcp.lab&lt;span class="w"&gt; &lt;/span&gt;300&lt;span class="w"&gt; &lt;/span&gt;SRV&lt;span class="w"&gt; &lt;/span&gt;0&lt;span class="w"&gt; &lt;/span&gt;0&lt;span class="w"&gt; &lt;/span&gt;2380&lt;span class="w"&gt; &lt;/span&gt;k8s-etcd-1.c1.lab
zone-set&lt;span class="w"&gt;   &lt;/span&gt;evilham.com&lt;span class="w"&gt;     &lt;/span&gt;_etcd-server-ssl-c1._tcp.lab&lt;span class="w"&gt; &lt;/span&gt;300&lt;span class="w"&gt; &lt;/span&gt;SRV&lt;span class="w"&gt; &lt;/span&gt;0&lt;span class="w"&gt; &lt;/span&gt;0&lt;span class="w"&gt; &lt;/span&gt;2380&lt;span class="w"&gt; &lt;/span&gt;k8s-etcd-2.c1.lab
zone-set&lt;span class="w"&gt;   &lt;/span&gt;evilham.com&lt;span class="w"&gt;     &lt;/span&gt;_etcd-server-ssl-c1._tcp.lab&lt;span class="w"&gt; &lt;/span&gt;300&lt;span class="w"&gt; &lt;/span&gt;SRV&lt;span class="w"&gt; &lt;/span&gt;0&lt;span class="w"&gt; &lt;/span&gt;0&lt;span class="w"&gt; &lt;/span&gt;2380&lt;span class="w"&gt; &lt;/span&gt;k8s-etcd-3.c1.lab

##&lt;span class="w"&gt; &lt;/span&gt;Client&lt;span class="w"&gt; &lt;/span&gt;SRV&lt;span class="w"&gt; &lt;/span&gt;records
zone-set&lt;span class="w"&gt;   &lt;/span&gt;evilham.com&lt;span class="w"&gt;     &lt;/span&gt;_etcd-client-c1._tcp.lab&lt;span class="w"&gt; &lt;/span&gt;300&lt;span class="w"&gt; &lt;/span&gt;SRV&lt;span class="w"&gt; &lt;/span&gt;0&lt;span class="w"&gt; &lt;/span&gt;0&lt;span class="w"&gt; &lt;/span&gt;2379&lt;span class="w"&gt; &lt;/span&gt;k8s-etcd-1.c1.lab
zone-set&lt;span class="w"&gt;   &lt;/span&gt;evilham.com&lt;span class="w"&gt;     &lt;/span&gt;_etcd-client-c1._tcp.lab&lt;span class="w"&gt; &lt;/span&gt;300&lt;span class="w"&gt; &lt;/span&gt;SRV&lt;span class="w"&gt; &lt;/span&gt;0&lt;span class="w"&gt; &lt;/span&gt;0&lt;span class="w"&gt; &lt;/span&gt;2379&lt;span class="w"&gt; &lt;/span&gt;k8s-etcd-2.c1.lab
zone-set&lt;span class="w"&gt;   &lt;/span&gt;evilham.com&lt;span class="w"&gt;     &lt;/span&gt;_etcd-client-c1._tcp.lab&lt;span class="w"&gt; &lt;/span&gt;300&lt;span class="w"&gt; &lt;/span&gt;SRV&lt;span class="w"&gt; &lt;/span&gt;0&lt;span class="w"&gt; &lt;/span&gt;0&lt;span class="w"&gt; &lt;/span&gt;2379&lt;span class="w"&gt; &lt;/span&gt;k8s-etcd-3.c1.lab

##&lt;span class="w"&gt; &lt;/span&gt;Actual&lt;span class="w"&gt; &lt;/span&gt;host&lt;span class="w"&gt; &lt;/span&gt;definitions
zone-set&lt;span class="w"&gt;   &lt;/span&gt;evilham.com&lt;span class="w"&gt;     &lt;/span&gt;k8s-etcd-1.c1.lab&lt;span class="w"&gt;       &lt;/span&gt;3600&lt;span class="w"&gt;    &lt;/span&gt;AAAA&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;_k8s_v6&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;:1::21
zone-set&lt;span class="w"&gt;   &lt;/span&gt;evilham.com&lt;span class="w"&gt;     &lt;/span&gt;k8s-etcd-1.c1.lab&lt;span class="w"&gt;       &lt;/span&gt;3600&lt;span class="w"&gt;    &lt;/span&gt;A&lt;span class="w"&gt;       &lt;/span&gt;10.2.1.21
zone-set&lt;span class="w"&gt;   &lt;/span&gt;evilham.com&lt;span class="w"&gt;     &lt;/span&gt;k8s-etcd-2.c1.lab&lt;span class="w"&gt;       &lt;/span&gt;3600&lt;span class="w"&gt;    &lt;/span&gt;AAAA&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;_k8s_v6&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;:1::22
zone-set&lt;span class="w"&gt;   &lt;/span&gt;evilham.com&lt;span class="w"&gt;     &lt;/span&gt;k8s-etcd-2.c1.lab&lt;span class="w"&gt;       &lt;/span&gt;3600&lt;span class="w"&gt;    &lt;/span&gt;A&lt;span class="w"&gt;       &lt;/span&gt;10.2.1.22
zone-set&lt;span class="w"&gt;   &lt;/span&gt;evilham.com&lt;span class="w"&gt;     &lt;/span&gt;k8s-etcd-3.c1.lab&lt;span class="w"&gt;       &lt;/span&gt;3600&lt;span class="w"&gt;    &lt;/span&gt;AAAA&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;_k8s_v6&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;:1::23
zone-set&lt;span class="w"&gt;   &lt;/span&gt;evilham.com&lt;span class="w"&gt;     &lt;/span&gt;k8s-etcd-3.c1.lab&lt;span class="w"&gt;       &lt;/span&gt;3600&lt;span class="w"&gt;    &lt;/span&gt;A&lt;span class="w"&gt;       &lt;/span&gt;10.2.1.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;I was &lt;strong&gt;not&lt;/strong&gt; going to set up TLS for the cluster peers yet because traffic
was internal and I run my own DNS.
However! Life happened, computers happened.
Apparently documentation for 3.4.X is
&lt;a href="https://github.com/etcd-io/etcd/issues/11321" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;actually wrong&lt;/a&gt;
and this is not really supported.
In theory TLS SRV entries should be tried, then non-TLS entries, in reality
cluster members bail out as soon as fetching TLS SRV records fails.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="provisioning-parenthesis"&gt;Provisioning parenthesis&lt;/h2&gt;
&lt;p&gt;An advantage of systematically allocating IPs and hostnames is that our
provisioning (with &lt;a href="https://cdi.st" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;cdist&lt;/a&gt; here) gets easier:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/bin/sh -eu&lt;/span&gt;

&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;__target_host&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Expeted to be: peer-name.cluster-name.lab.evilham.com&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;k8s-etcd-*.lab.evilham.com&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;__hostname&lt;span class="w"&gt; &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;__target_host&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;ETC_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/etc&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# targeting linux only atm&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# Install etcd package&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;__package&lt;span class="w"&gt; &lt;/span&gt;etcd-server
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# Prepare data from hostname&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;discovery_srv_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;__target_host&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;cut&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;discovery_srv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;__target_host&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;cut&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;-&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;#   Will look like: eh-etcd-c1&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;initial_cluster_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;eh-etcd-&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;discovery_srv_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;require&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;__package/etcd-server&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;__file&lt;span class="w"&gt; &lt;/span&gt;/etc/default/etcd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;--onchange&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;service etcd restart&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;--source&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt;EOF&lt;/span&gt;
&lt;span class="s"&gt;# Managed remotely, changes will be lost!&lt;/span&gt;

&lt;span class="s"&gt;ETCD_NAME=&amp;#39;${__target_host}&amp;#39;&lt;/span&gt;

&lt;span class="s"&gt;# DNS discovery&lt;/span&gt;
&lt;span class="s"&gt;ETCD_DISCOVERY_SRV=&amp;#39;${discovery_srv}&amp;#39;&lt;/span&gt;
&lt;span class="s"&gt;ETCD_DISCOVERY_SRV_NAME=&amp;#39;${discovery_srv_name}&amp;#39;&lt;/span&gt;

&lt;span class="s"&gt;# Peers use TLS&lt;/span&gt;
&lt;span class="s"&gt;ETCD_INITIAL_ADVERTISE_PEER_URLS=&amp;#39;https://${__target_host}:2380&amp;#39;&lt;/span&gt;
&lt;span class="s"&gt;ETCD_LISTEN_PEER_URLS=&amp;#39;https://[::]:2380&amp;#39;&lt;/span&gt;

&lt;span class="s"&gt;# Clients do not&lt;/span&gt;
&lt;span class="s"&gt;ETCD_LISTEN_CLIENT_URLS=&amp;#39;http://[::]:2379&amp;#39;&lt;/span&gt;
&lt;span class="s"&gt;ETCD_ADVERTISE_CLIENT_URLS=&amp;#39;http://${__target_host}:2379&amp;#39;&lt;/span&gt;

&lt;span class="s"&gt;# Setup TLS for peers&lt;/span&gt;
&lt;span class="s"&gt;#   This provides encryption but not authentication of peers!&lt;/span&gt;
&lt;span class="s"&gt;ETCD_PEER_AUTO_TLS=&amp;quot;true&amp;quot;&lt;/span&gt;

&lt;span class="s"&gt;# We deploy this first with _ETCD_STATE defined as &amp;#39;new&amp;#39;, so the&lt;/span&gt;
&lt;span class="s"&gt;# cluster gets created. On successive runs, the state will be &amp;#39;existing&amp;#39;.&lt;/span&gt;
&lt;span class="s"&gt;DAEMON_ARGS=&amp;quot;--initial-cluster-token &amp;#39;${initial_cluster_token}&amp;#39; \&lt;/span&gt;
&lt;span class="s"&gt;             --initial-cluster-state &amp;#39;${_ETCD_STATE:-existing}&amp;#39;&amp;#39;&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So, now we apply this manifest on all VMs on the cluster:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;env&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;_ETCD_STATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;new&lt;span class="w"&gt; &lt;/span&gt;cdist&lt;span class="w"&gt; &lt;/span&gt;config&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;manifest/etcd&lt;span class="w"&gt; &lt;/span&gt;k8s-etcd-&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;,2,3&lt;span class="o"&gt;}&lt;/span&gt;.c1.lab.evilham.com
&lt;span class="o"&gt;[&lt;/span&gt;...&lt;span class="o"&gt;]&lt;/span&gt;
VERBOSE:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;5341&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;config:&lt;span class="w"&gt; &lt;/span&gt;Total&lt;span class="w"&gt; &lt;/span&gt;processing&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;host&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;33&lt;/span&gt;.292810678482056
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1 id="testing-the-cluster"&gt;Testing the cluster&lt;/h1&gt;
&lt;p&gt;We will install &lt;code&gt;etcd-client&lt;/code&gt; on the control-plane VM, which has access to the
network, and see how things behave.&lt;/p&gt;
&lt;h2 id="basic-functioning"&gt;Basic functioning&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;etcdctl&lt;span class="w"&gt; &lt;/span&gt;--discovery-srv&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lab.evilham.com&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--discovery-srv-name&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;c1&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;put&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Gabon&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Bona nit&amp;quot;&lt;/span&gt;
OK
$&lt;span class="w"&gt; &lt;/span&gt;etcdctl&lt;span class="w"&gt; &lt;/span&gt;--discovery-srv&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lab.evilham.com&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--discovery-srv-name&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;c1&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Gabon&amp;quot;&lt;/span&gt;
Gabon
Bona&lt;span class="w"&gt; &lt;/span&gt;nit

$&lt;span class="w"&gt; &lt;/span&gt;etcdctl&lt;span class="w"&gt; &lt;/span&gt;--discovery-srv&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lab.evilham.com&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--discovery-srv-name&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;c1&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--write-out&lt;span class="o"&gt;=&lt;/span&gt;fields&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Gabon&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;ClusterID&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;17681054088137536936&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;MemberID&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;9829860431962910583&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;Revision&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;26834&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;RaftTerm&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;61940&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;Key&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Gabon&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;CreateRevision&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;26833&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;ModRevision&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;26834&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;Version&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;Value&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Bona nit&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;Lease&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;More&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;Count&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Cute! It seems to be working.&lt;/p&gt;
&lt;h2 id="killing-cluster-members"&gt;Killing cluster members&lt;/h2&gt;
&lt;p&gt;Since there is no data, I want to see what happens when I kill the leader,
first I have to find out who it is:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;etcdctl&lt;span class="w"&gt; &lt;/span&gt;--discovery-srv&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lab.evilham.com&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--discovery-srv-name&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;c1&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--write-out&lt;span class="o"&gt;=&lt;/span&gt;table&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;endpoint&lt;span class="w"&gt; &lt;/span&gt;status&lt;span class="w"&gt; &lt;/span&gt;--cluster
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It seems like &lt;code&gt;k8s-etcd-2&lt;/code&gt; is the chosen one (&lt;code&gt;IS LEADER&lt;/code&gt;) right now, let&amp;rsquo;s power it off.&lt;/p&gt;
&lt;p&gt;Repeating the previous command is not as quick as before and I get a warning
&lt;code&gt;context deadline exceeded&lt;/code&gt;, corresponding to &lt;code&gt;k8s-etcd-2&lt;/code&gt; being down.
Interestingly, there is a new leader in town, &lt;code&gt;k8s-etcd-1&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;Not for long, let&amp;rsquo;s kill it too.&lt;/p&gt;
&lt;p&gt;Now, the cluster shows an error &lt;code&gt;etcdserver: no leader&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This makes sense, it knows about two other peers, and it can&amp;rsquo;t decide to be the
leader on its own; there could very well be a network split and that&amp;rsquo;d get messy
when the two parts can talk to each other!
This is why an &lt;code&gt;etcd&lt;/code&gt; cluster has &lt;code&gt;2*n+1&lt;/code&gt; peers!&lt;/p&gt;
&lt;p&gt;What if I try to add something to the cluster again?&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;etcdctl&lt;span class="w"&gt; &lt;/span&gt;--discovery-srv&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lab.evilham.com&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--discovery-srv-name&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;c1&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;put&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Egun on&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Bon dia&amp;quot;&lt;/span&gt;
...&lt;span class="w"&gt; &lt;/span&gt;context&lt;span class="w"&gt; &lt;/span&gt;deadline&lt;span class="w"&gt; &lt;/span&gt;exceeded&lt;span class="w"&gt; &lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So, it turned read-only? good! Can I still query it?&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;etcdctl&lt;span class="w"&gt; &lt;/span&gt;--discovery-srv&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lab.evilham.com&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--discovery-srv-name&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;c1&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Gabon&amp;quot;&lt;/span&gt;
...&lt;span class="w"&gt; &lt;/span&gt;context&lt;span class="w"&gt; &lt;/span&gt;deadline&lt;span class="w"&gt; &lt;/span&gt;exceeded&lt;span class="w"&gt; &lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Nope! This is also good, in this context, it is better to have no answer than
a potentially wrong one.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s revive &lt;code&gt;k8s-etcd-2&lt;/code&gt; and check the cluster status again.&lt;/p&gt;
&lt;p&gt;Decent, with two members an election could happen and &lt;code&gt;k8s-etcd-3&lt;/code&gt; is the new
leader.&lt;/p&gt;
&lt;p&gt;Now we should be able to teach our etcd to say good morning:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;etcdctl&lt;span class="w"&gt; &lt;/span&gt;--discovery-srv&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lab.evilham.com&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--discovery-srv-name&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;c1&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;put&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Egun on&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Bon dia&amp;quot;&lt;/span&gt;
OK
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Writing works again, as does reading; good!&lt;/p&gt;
&lt;p&gt;Now, if we bring back &lt;code&gt;k8s-etcd-1&lt;/code&gt;, it should catch up and magically learn to
say good morning.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;etcdctl&lt;span class="w"&gt; &lt;/span&gt;--endpoints&lt;span class="o"&gt;=&lt;/span&gt;http://k8s-etcd-1.c1...:2379&lt;span class="w"&gt;  &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Egun on&amp;quot;&lt;/span&gt;
Egun&lt;span class="w"&gt; &lt;/span&gt;on
Bon&lt;span class="w"&gt; &lt;/span&gt;dia
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Wonderful!&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Deploying &lt;code&gt;etcd&lt;/code&gt; in a cluster is not terribly difficult (modulo misleading docs
the implied inherent pain of running our own internal CA in production),
and the effort of running the cluster outside of kubernetes is probably worth
it because our kubernetes cluster state can be safer.&lt;/p&gt;
&lt;p&gt;Further things to check here are backups and restore from the cluster.
On that note, I like this quote from
&lt;a href="https://wikitech.wikimedia.org/wiki/Etcd#Recover_a_cluster_after_a_disaster" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;WMF&amp;rsquo;s wikitech&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In the sad case when RAFT consensus is lost and there is no quorum anymore, the only way to recover the cluster is to recover the data from a backup, which are regularly performed every night in /srv/backups/etcd. The procedure to bring back the cluster is roughly as follows [&amp;hellip;]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Aka: do not panic, breath in, know your emergency plans, check the emergency plan, and apply it.&lt;/p&gt;
&lt;p&gt;Now that &lt;code&gt;etcd&lt;/code&gt; is set up, we can actually get on with kubernetes!&lt;/p&gt;
&lt;p&gt;These were the steps according to &lt;a href="/en/blog/2023-trying-out-kubernetes-1-planning/"&gt;the plan&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/en/blog/2023-trying-out-kubernetes-2-network/"&gt;[X]&lt;/a&gt; we start by &lt;a href="/en/blog/2023-trying-out-kubernetes-2-network/"&gt;setting up the network segments&lt;/a&gt; as bridges on
      the physical host, taking care of NAT, firewall and IPv6 routing&lt;/li&gt;
&lt;li&gt;&lt;a href="/en/blog/2023-trying-out-kubernetes-3-etcd/"&gt;[X]&lt;/a&gt; then we set up the &lt;a href="/en/blog/2023-trying-out-kubernetes-3-etcd/"&gt;&lt;code&gt;etcd&lt;/code&gt; cluster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;[ ] finally, we actually setup the Kubernetes cluster&lt;/li&gt;
&lt;/ul&gt;</content></entry><entry><title>Trying out Kubernetes: Network and VMs</title><link href="https://evilham.eu/en/blog/2023-trying-out-kubernetes-2-network/" rel="alternate"></link><updated>2023-11-06T00:00:00Z</updated><author><name></name></author><id>urn:uuid:d8fc4cca-d7a0-311b-b002-7ad07f91cb5a</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;As part of &lt;a href="/en/blog/2023-trying-out-kubernetes-1-planning/"&gt;trying out Kubernetes: planning&lt;/a&gt;, we are going to set
up the network and VMs!&lt;/p&gt;
&lt;p&gt;Quick reminder that this will be virtualised Linux on a FreeBSD physical host.
This part is FreeBSD-specific.&lt;/p&gt;
&lt;h1 id="table-of-contents"&gt;Table of contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#network"&gt;Network&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#ip-assignment"&gt;IP assignment&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#virtual-machines"&gt;Virtual Machines&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#vm-bhyve-switches"&gt;vm-bhyve switches&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#template"&gt;Template&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#os-debian"&gt;OS: Debian&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#creating-the-base-vm"&gt;Creating the base VM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#cloning-the-vms"&gt;Cloning the VMs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="network"&gt;Network&lt;/h1&gt;
&lt;p&gt;Given that &lt;code&gt;bhyve(8)&lt;/code&gt;&amp;lsquo;s networking uses &lt;code&gt;tap(4)&lt;/code&gt; devices, which we can bridge
and route as needed, we will only need basic FreeBSD networking knowledge.&lt;/p&gt;
&lt;p&gt;This actually is somewhat similar to the setup I use for
&lt;a href="/en/blog/2021-freebsd-ipv6-in-vnet-jails/"&gt;IPv6-VNET jails&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That is, we will need a bridge and a tap device for each network segment:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;#&lt;span class="w"&gt; &lt;/span&gt;Adding&lt;span class="w"&gt; &lt;/span&gt;following&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;/etc/rc.conf

#&lt;span class="w"&gt; &lt;/span&gt;k8s&lt;span class="w"&gt; &lt;/span&gt;testing
#&lt;span class="w"&gt; &lt;/span&gt;Create&lt;span class="w"&gt; &lt;/span&gt;tap&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;bridge&lt;span class="w"&gt; &lt;/span&gt;interfaces
cloned_interface=&amp;quot;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;cloned_interfaces&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tap0&lt;span class="w"&gt; &lt;/span&gt;bridge0&lt;span class="w"&gt; &lt;/span&gt;tap1&lt;span class="w"&gt; &lt;/span&gt;bridge1&amp;quot;
#&lt;span class="w"&gt; &lt;/span&gt;Rename&lt;span class="w"&gt; &lt;/span&gt;them
ifconfig_bridge0_name=&amp;quot;etcdbridge&amp;quot;
ifconfig_tap0_name=&amp;quot;etcdtap&amp;quot;
ifconfig_bridge1_name=&amp;quot;k8sbridge&amp;quot;
ifconfig_tap1_name=&amp;quot;k8stap&amp;quot;

#&lt;span class="w"&gt; &lt;/span&gt;Save&lt;span class="w"&gt; &lt;/span&gt;our&lt;span class="w"&gt; &lt;/span&gt;network&lt;span class="w"&gt; &lt;/span&gt;prefix&lt;span class="w"&gt; &lt;/span&gt;data
_k8s_v6=&amp;quot;2XXX:XXXX:XXXX&amp;quot;
_k8s_v4=&amp;quot;10.2&amp;quot;

#&lt;span class="w"&gt; &lt;/span&gt;Define&lt;span class="w"&gt; &lt;/span&gt;etcd&lt;span class="w"&gt; &lt;/span&gt;network
ifconfig_etcdtap=&amp;quot;fib&lt;span class="w"&gt; &lt;/span&gt;1&amp;quot;
ifconfig_etcdbridge_descr=&amp;quot;etcd&lt;span class="w"&gt; &lt;/span&gt;cluster&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;k8s&lt;span class="w"&gt; &lt;/span&gt;control-panel&amp;quot;
ifconfig_etcdbridge=&amp;quot;inet&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;_k8s_v4&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;.1.1/24&lt;span class="w"&gt; &lt;/span&gt;addm&lt;span class="w"&gt; &lt;/span&gt;etcdtap&lt;span class="w"&gt; &lt;/span&gt;up&lt;span class="w"&gt; &lt;/span&gt;fib&lt;span class="w"&gt; &lt;/span&gt;1&amp;quot;
ifconfig_etcdbridge_ipv6=&amp;quot;inet6&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;_k8s_v6&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;:1::/64&lt;span class="w"&gt; &lt;/span&gt;auto_linklocal&amp;quot;

#&lt;span class="w"&gt; &lt;/span&gt;Define&lt;span class="w"&gt; &lt;/span&gt;k8s&lt;span class="w"&gt; &lt;/span&gt;network
ifconfig_k8stap=&amp;quot;fib&lt;span class="w"&gt; &lt;/span&gt;1&amp;quot;
ifconfig_k8sbridge_descr=&amp;quot;k8s&lt;span class="w"&gt; &lt;/span&gt;nodes&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;control-panel&amp;quot;
ifconfig_k8sbridge=&amp;quot;inet&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;_k8s_v4&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;.2.1/24&lt;span class="w"&gt; &lt;/span&gt;addm&lt;span class="w"&gt; &lt;/span&gt;k8stap&lt;span class="w"&gt; &lt;/span&gt;up&lt;span class="w"&gt; &lt;/span&gt;fib&lt;span class="w"&gt; &lt;/span&gt;1&amp;quot;
ifconfig_k8sbridge_ipv6=&amp;quot;inet6&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;_k8s_v6&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;:2::/64&lt;span class="w"&gt; &lt;/span&gt;auto_linklocal&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Remarkable is that we are assigning &lt;code&gt;/64&lt;/code&gt; subnets for IPv6 and &lt;code&gt;/24&lt;/code&gt; for IPv4,
and that we will use static IP assignment since this is just a handful of
machines.&lt;/p&gt;
&lt;p&gt;I am also using a different routing table (&lt;code&gt;fib 1&lt;/code&gt;) in order not to mix
traffic.&lt;/p&gt;
&lt;p&gt;Note that besides valid IPv6 routing, there are NAT rules taking care of IPv4:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;# k8s NAT&lt;/span&gt;
&lt;span class="n"&gt;nat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;ext_if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;10.2.1.0&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;10.2.2.0&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;ext_if&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="ip-assignment"&gt;IP assignment&lt;/h2&gt;
&lt;p&gt;We will perform static IP assignment due to the small scale:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;VM&lt;/th&gt;
&lt;th&gt;IPv4&lt;/th&gt;
&lt;th&gt;IPv6&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;k8s-etcd-1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10.2.1.21&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${_k8s_v6}:1::21&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;k8s-etcd-2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10.2.1.22&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${_k8s_v6}:1::22&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;k8s-etcd-3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10.2.1.23&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${_k8s_v6}:1::23&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;k8s-cp-1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10.2.1.11&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${_k8s_v6}:1::11&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10.2.2.11&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${_k8s_v6}:2::11&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;k8s-node-N&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10.2.2.20+N&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${_k8s_v6}:2::20+N&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Now I should be able to create the Virtual Machines on each network bridge.&lt;/p&gt;
&lt;h1 id="virtual-machines"&gt;Virtual Machines&lt;/h1&gt;
&lt;p&gt;As mentioned before, the virtualisation will be done with &lt;code&gt;bhyve(8)&lt;/code&gt;,
particularly with &lt;code&gt;vm-bhyve(8)&lt;/code&gt;, as it is very lightweight and takes care of
lower level details for us.&lt;/p&gt;
&lt;h2 id="vm-bhyve-switches"&gt;&lt;code&gt;vm-bhyve&lt;/code&gt; switches&lt;/h2&gt;
&lt;p&gt;We add following to &lt;code&gt;VM_BHYVE/.config/system.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;switch_list&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;etcdbridge k8sbridge&amp;quot;&lt;/span&gt;

&lt;span class="nx"&gt;type_etcdbridge&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;manual&amp;quot;&lt;/span&gt;
&lt;span class="nx"&gt;bridge_etcdbridge&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;etcdbridge&amp;quot;&lt;/span&gt;

&lt;span class="nx"&gt;type_k8sbridge&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;manual&amp;quot;&lt;/span&gt;
&lt;span class="nx"&gt;bridge_k8sbridge&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;k8sbridge&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which makes sure that &lt;code&gt;vm-bhyve&lt;/code&gt; will add the network interfaces to the
appropriate bridges.&lt;/p&gt;
&lt;h2 id="template"&gt;Template&lt;/h2&gt;
&lt;p&gt;Then we create the &lt;code&gt;vm-bhyve(8)&lt;/code&gt; template:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# This lives in VM_BHYVE/.templates/k8s.conf&lt;/span&gt;
&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;k8s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;
&lt;span class="n"&gt;loader&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;grub&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;cpu&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;G&lt;/span&gt;

&lt;span class="n"&gt;network0_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;virtio-net&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;network0_switch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;k8sbridge&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# We use sparse zvols for storage, which enables us to snapshot and clone the&lt;/span&gt;
&lt;span class="c1"&gt;# whole disk while only allocating space as we start using it.&lt;/span&gt;
&lt;span class="n"&gt;disk0_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;disk0&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;disk0_dev&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sparse-zvol&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;disk0_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;virtio-blk&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since we will later clone this VM, we&amp;rsquo;ll add network adapters / change bridges.&lt;/p&gt;
&lt;h2 id="os-debian"&gt;OS: Debian&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;ll use Debian&amp;rsquo;s latest release: &lt;code&gt;bookworm&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-L&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.2.0-amd64-netinst.iso&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;VM_BHYVE/.iso/debian-12.2.0-amd64-netinst.iso
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="creating-the-base-vm"&gt;Creating the base VM&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vm&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;k8s&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;240G&lt;span class="w"&gt; &lt;/span&gt;k8sbase
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we install the OS using the serial console and the downloaded &lt;code&gt;.iso&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vm&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;k8sbase&lt;span class="w"&gt; &lt;/span&gt;debian-12.2.0-amd64-netinst.iso
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I use an unassigned IP for the base VM, so I can apply provisioning for the
final touches.&lt;/p&gt;
&lt;p&gt;Still over serial, we finish up basic things like enabling SSH but only with
public keys, and installing my keys.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# In /etc/ssh/sshd_config
PermitRootLogin prohibit-password
PasswordAuthentication no
UsePAM no
KbdInteractiveAuthentication no
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;service&lt;span class="w"&gt; &lt;/span&gt;sshd&lt;span class="w"&gt; &lt;/span&gt;restart
$&lt;span class="w"&gt; &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;.ssh
$&lt;span class="w"&gt; &lt;/span&gt;chmod&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;750&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.ssh
$&lt;span class="w"&gt; &lt;/span&gt;wget&lt;span class="w"&gt; &lt;/span&gt;https://evilham.com/ssh.pubkey&lt;span class="w"&gt; &lt;/span&gt;-O&lt;span class="w"&gt; &lt;/span&gt;.ssh/authorized_keys
$&lt;span class="w"&gt; &lt;/span&gt;chmod&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;640&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="cloning-the-vms"&gt;Cloning the VMs&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# We create a snapshot first&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;vm&lt;span class="w"&gt; &lt;/span&gt;snapshot&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;k8sbase@clean
&lt;span class="c1"&gt;# And then actually clone it, we start with one VM of each type&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;vm&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;k8sbase@clean&lt;span class="w"&gt; &lt;/span&gt;k8s-etcd-1
$&lt;span class="w"&gt; &lt;/span&gt;vm&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;k8sbase@clean&lt;span class="w"&gt; &lt;/span&gt;k8s-cp-1
$&lt;span class="w"&gt; &lt;/span&gt;vm&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;k8sbase@clean&lt;span class="w"&gt; &lt;/span&gt;k8s-node-1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, we can edit each VM&amp;rsquo;s &lt;code&gt;VM_BHYVE/VM/VM.conf&lt;/code&gt; file in order to assign it to
the corresponding network(s):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;In&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;VM_BHYVE&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;k8s&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;etcd&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;k8s&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;etcd&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="nx"&gt;network0_type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;virtio-net&amp;quot;&lt;/span&gt;
&lt;span class="nx"&gt;network0_switch&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;etcdbridge&amp;quot;&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;In&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;VM_BHYVE&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;k8s&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;cp&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;k8s&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;cp&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="nx"&gt;network0_type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;virtio-net&amp;quot;&lt;/span&gt;
&lt;span class="nx"&gt;network0_switch&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;etcdbridge&amp;quot;&lt;/span&gt;
&lt;span class="nx"&gt;network1_type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;virtio-net&amp;quot;&lt;/span&gt;
&lt;span class="nx"&gt;network1_switch&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;k8sbridge&amp;quot;&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;In&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;VM_BHYVE&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;k8s&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;k8s&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="nx"&gt;network0_type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;virtio-net&amp;quot;&lt;/span&gt;
&lt;span class="nx"&gt;network0_switch&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;k8snode1&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And using provisioning against the unassigned IP one VM at a time, we:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reset the SSH keys and &lt;code&gt;hostid&lt;/code&gt; so they are not shared (this is important
  for &lt;code&gt;etcd&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Apply the assigned static IP configuration on all interfaces&lt;/li&gt;
&lt;li&gt;Reboot and check that SSH on the new VM is possible&lt;/li&gt;
&lt;li&gt;Snapshot the VM&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;FreeBSD&amp;rsquo;s networking and virtualisation stack are great and it often surprises
me how effortless it is to get things done.&lt;/p&gt;
&lt;p&gt;Networking is set up, one of each VM type is set up with outgoing dual stack
internet and two separated network segments.&lt;/p&gt;
&lt;p&gt;Next up, &lt;a href="/en/blog/2023-trying-out-kubernetes-3-etcd/"&gt;setting up &lt;code&gt;etcd&lt;/code&gt;&lt;/a&gt;.
These were the steps according to &lt;a href="/en/blog/2023-trying-out-kubernetes-1-planning/"&gt;the plan&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/en/blog/2023-trying-out-kubernetes-2-network/"&gt;[X]&lt;/a&gt; we start by &lt;a href="/en/blog/2023-trying-out-kubernetes-2-network/"&gt;setting up the network segments&lt;/a&gt; as bridges on
      the physical host, taking care of NAT, firewall and IPv6 routing&lt;/li&gt;
&lt;li&gt;&lt;a href="/en/blog/2023-trying-out-kubernetes-3-etcd/"&gt;[X]&lt;/a&gt; then we set up the &lt;a href="/en/blog/2023-trying-out-kubernetes-3-etcd/"&gt;&lt;code&gt;etcd&lt;/code&gt; cluster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;[ ] finally, we actually setup the Kubernetes cluster&lt;/li&gt;
&lt;/ul&gt;</content></entry><entry><title>Trying out Kubernetes: planning</title><link href="https://evilham.eu/en/blog/2023-trying-out-kubernetes-1-planning/" rel="alternate"></link><updated>2023-11-05T00:00:00Z</updated><author><name></name></author><id>urn:uuid:d3a2aa6c-9150-3ab1-afee-b27aefe267ad</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Kubernetes" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Kubernetes&lt;/a&gt; turned 9 years old a couple months ago, so it&amp;rsquo;s about
time I took a more serious look at it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Kubernetes" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Kubernetes&lt;/a&gt; is an open-source container orchestration system
for automating software deployment, scaling, and management.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have followed its development somewhat closely and kept up with the concepts
and architecture, even if I haven&amp;rsquo;t jumped to actually using it.&lt;/p&gt;
&lt;p&gt;First it was due to waiting for things to get stable,
then I decided to let the hype wear off.&lt;/p&gt;
&lt;p&gt;Now, hype hasn&amp;rsquo;t worn off, but it is at least a topic treated with some
level of nuance: Kubernetes is great, but it comes with some complexity;
complexity that is not necessary in many cases.
And so we have to make sure
that the benefits actually outweigh the overhead for our particular use-case.&lt;/p&gt;
&lt;p&gt;As that point nears and I have some days off, I jump in at learning the best
way I know how to: &lt;em&gt;by actually doing things&lt;/em&gt; and, this time around, by
writing them down for future reference / to better ask for clarification.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s create a &amp;ldquo;High Availability Kubernetes cluster&amp;rdquo;!
(I guess we&amp;rsquo;ll [re]define what HA means here a couple times while doing the
deed)&lt;/p&gt;
&lt;p&gt;Note I do this from a FreeBSD laptop, against a FreeBSD physical host, but the
actual Control Plane and Nodes will be running on bhyve Virtual Machines
running Linux in that remote FreeBSD physical host.
Should not be too relevant, it just makes networking easier =D
(debatable, yes).&lt;/p&gt;
&lt;p&gt;This will likely take several posts :-).&lt;/p&gt;
&lt;h1 id="table-of-contents"&gt;Table of contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#goals"&gt;Goals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#quick-resources"&gt;Quick resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-components"&gt;The components&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#control-plane"&gt;Control plane&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#etcd"&gt;etcd&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#node-components"&gt;Node components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#addons"&gt;Addons&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#necessary-resources"&gt;Necessary resources&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#network"&gt;Network&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#virtual-machines"&gt;Virtual Machines&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="goals"&gt;Goals&lt;/h1&gt;
&lt;p&gt;The main goal here is my own learning and exploring from an Operations
perspective, with future quick-reference as a secondary goal.&lt;/p&gt;
&lt;p&gt;Particularly: duplicating documentation or getting familiar from a
user&amp;rsquo;s perspective are non-goals right now :-).&lt;/p&gt;
&lt;p&gt;It is for these reason that some things are deliberately made more difficult,
by e.g. aiming for High Availability and &amp;ldquo;production-ready&amp;rdquo; configurations
from the beginning, instead of starting with something more simple and
self-contained like
&lt;a href="https://kubernetes.io/docs/tasks/tools/#kind" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;kind&lt;/a&gt;
and
&lt;a href="https://kubernetes.io/docs/tasks/tools/#minikube" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;minicube&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This addresses &lt;strong&gt;my way of learning&lt;/strong&gt;, which includes: reading all/most of the
documentation once, gaining a broad overview of what can be done, and what
should be done, then implement things as close to production as practical
and possible.&lt;/p&gt;
&lt;p&gt;Your way of learning may differ, and you may need more
incremental progress and immediate feedback; &lt;strong&gt;use whatever works for you&lt;/strong&gt;,
if this post helps you, I&amp;rsquo;ll be happy if you let me know!
I do not know many people that share my way of learning :-).&lt;/p&gt;
&lt;h1 id="quick-resources"&gt;Quick resources&lt;/h1&gt;
&lt;p&gt;First, let&amp;rsquo;s compile a list of resources that will help us:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/home/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Kubernetes docs&lt;/a&gt;: what I&amp;rsquo;ve used periodically to stay on top of
  the project to keep a general overview of the system.
  It is fairly complete and nicely written, certainly a good reference and
  also a decent starting point.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wikitech.wikimedia.org/wiki/Kubernetes/Clusters/New" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;WMF&amp;rsquo;s SRE guide to creating a new cluster&lt;/a&gt;: turns out, the
  &lt;a href="https://wikimediafoundation.org/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Wikimedia Foundation&lt;/a&gt;&amp;lsquo;s SRE team does everything in the open, and they do
  very interesting things.
  Relevant for this context: they run their own HA Kubernetes clusters and
  theirs is guaranteed to be a scalable setup.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.howtogeek.com/devops/how-to-start-a-kubernetes-cluster-from-scratch-with-kubeadm-and-kubectl/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;How-To Geek: how to start a Kubernetes cluster from scratch&lt;/a&gt;:
  a somewhat complete, simple and to-the-point guide which helps an
  experienced &lt;em&gt;Mensch&lt;/em&gt; plan ahead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="the-components"&gt;The components&lt;/h1&gt;
&lt;p&gt;Since we care about the Operations perspective, we jump straight to
&lt;a href="https://kubernetes.io/docs/concepts/overview/components/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Kubernetes&amp;rsquo; components&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Worker machines or &lt;strong&gt;Nodes&lt;/strong&gt;: where &lt;strong&gt;Pods&lt;/strong&gt; (set of running
  containers) actually live.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Control plane&lt;/strong&gt;: manages worker Nodes and Pods in the cluster.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;As a temporary simplification: we will run the &lt;strong&gt;Control plane&lt;/strong&gt; on a single
Virtual Machine, that will not be allowed to run user containers.&lt;/p&gt;
&lt;p&gt;This makes the cluster less redundant, but we get somewhere faster.
It also &lt;strong&gt;feels&lt;/strong&gt; like adding this redundancy at a later stage shouldn&amp;rsquo;t be
too difficult; it certainly isn&amp;rsquo;t our main goal at this stage.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="control-plane"&gt;Control plane&lt;/h2&gt;
&lt;p&gt;It includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;kube-apiserver&lt;/code&gt;: which exposes the Kubernetes API. Can be traffic balanced.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;etcd&lt;/code&gt;: HA key value store, this is where &lt;strong&gt;all cluster data lives&lt;/strong&gt;.
  It is a different piece of software used outside of the Kubernetes context.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kube-scheduler&lt;/code&gt;: picks up new &lt;strong&gt;Pods&lt;/strong&gt; and assigns them a &lt;strong&gt;Node&lt;/strong&gt; to run
  on based on the Pod&amp;rsquo;s specifications and the cluster&amp;rsquo;s availability.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kube-controller-manager&lt;/code&gt;: performs multiple jobs, abstractly it seems like
  it is in charge of reacting to changes in the cluster of various kinds
  (like a Pod going down, running one-off tasks by creating their Pods, etc.),
  the documentation doesn&amp;rsquo;t provide an exhaustive list, so this is an
  extrapolation of the meaning, and could be somewhat wrong or incomplete.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cloud-controller-manager&lt;/code&gt;: this seems to be provider-specific, since we are
  running Kubernetes ourselves (the whole point), we will probably not see it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="etcd"&gt;&lt;code&gt;etcd&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The documentation for a &lt;a href="https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/ha-topology/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Highly Available Kubernetes cluster&lt;/a&gt; gives us
two options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stacked Control Plane nodes, which include &lt;code&gt;etcd&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;External &lt;code&gt;etcd&lt;/code&gt; Nodes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My systems intuition tells me that mixing Control Plane and state storage may
not be the best of decisions, and I have heard horror stories.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d be inclined to use an external &lt;code&gt;etcd&lt;/code&gt; for anything put in production.
This is ratified by &lt;a href="https://wikitech.wikimedia.org/wiki/Kubernetes/Clusters/New#etcd" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;WMF&amp;rsquo;s Kubernetes&lt;/a&gt; deployment.&lt;/p&gt;
&lt;h2 id="node-components"&gt;Node components&lt;/h2&gt;
&lt;p&gt;These run on each Node:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;kubelet&lt;/code&gt;: this makes sure containers from Pods are running and healthy&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kube-proxy&lt;/code&gt;: manages the network rules to be able to provide Services.
  Something tells me we&amp;rsquo;ll spend a lot of time here.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Container runtime:
  It seems like this is a place where we have to make a choice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://containerd.io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;containerd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cri-o.io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;CRI-O&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;I notice on &lt;a href="https://wikitech.wikimedia.org/wiki/Kubernetes/Clusters/New#Node_(worker)" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;WMF techwiki&lt;/a&gt;:
&amp;ldquo;docker is mean to be used as the CRE. Other runtime engines aren&amp;rsquo;t currently supported.&amp;rdquo;
which is compatible with &lt;a href="https://wikitech.wikimedia.org/wiki/Kubernetes/Clusters/New#Versions" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;the statement&lt;/a&gt;
&amp;ldquo;This guide currently covers kubernetes 1.23&amp;rdquo; and
the way the &lt;a href="https://gerrit.wikimedia.org/r/plugins/gitiles/operations/debs/kubernetes/+/9e353148720850f2a269243d6b7a89b476ff9e79" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;deb packages&lt;/a&gt; are created.
(&lt;a href="https://wikitech.wikimedia.org/wiki/Kubernetes/Kubernetes_Infrastructure_upgrade_policy" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;the Kubernetes Infrastructure upgrade policy&lt;/a&gt; differs, it likely only needs an update)&lt;/p&gt;
&lt;p&gt;This makes sense because Docker as a runtime stopped being supported by
Kubernetes in &lt;a href="https://github.com/kubernetes/kubernetes/blob/cf4d031dbb002d50096a4a17cb7111ecbcfc0662/CHANGELOG/CHANGELOG-1.24.md#dockershim-removed-from-kubelet" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;v1.24&lt;/a&gt;!
Jumping that minor version may be tricky.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Given we will start differing from WMF&amp;rsquo;s documentation, I will pick
&lt;a href="https://cri-o.io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;CRI-O&lt;/a&gt; for no particular reason and target the latest version of
Kubernetes (1.28.3).&lt;/p&gt;
&lt;h2 id="addons"&gt;Addons&lt;/h2&gt;
&lt;p&gt;These provide cluster-level features.
At first I found this confusing, but after giving it some thought:
it makes sense.&lt;/p&gt;
&lt;p&gt;This means that there can be multiple implementations (and it happens) of
similar cluster services, addressing different needs and priorities.&lt;/p&gt;
&lt;p&gt;These are the ones I think we&amp;rsquo;ll need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DNS: else we&amp;rsquo;ll have to deal with IPs and running services gets tricky&lt;/li&gt;
&lt;li&gt;Dashboard (UI): want to see how it works&lt;/li&gt;
&lt;li&gt;Network plugin: this deals with Pod networking, we&amp;rsquo;ll want to check
  &lt;a href="https://docs.tigera.io/calico/latest/about" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Calico&lt;/a&gt; as used at &lt;a href="https://wikitech.wikimedia.org/wiki/Calico" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;WMF&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="necessary-resources"&gt;Necessary resources&lt;/h1&gt;
&lt;p&gt;With that, we have most information to start planning and allocating resources.&lt;/p&gt;
&lt;h2 id="network"&gt;Network&lt;/h2&gt;
&lt;p&gt;Given the choice to run &lt;code&gt;etcd&lt;/code&gt; externally, we will need two separate networks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;etcd&lt;/code&gt;: access will be network and firewall-controlled&lt;/li&gt;
&lt;li&gt;Kubernetes:&lt;ul&gt;
&lt;li&gt;Control Plane:
  these must have access to &lt;code&gt;etcd&lt;/code&gt; too and &lt;strong&gt;won&amp;rsquo;t&lt;/strong&gt; run user containers&lt;/li&gt;
&lt;li&gt;Nodes: these will be excluded from accessing &lt;code&gt;etcd&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a total of: 2 networks, where one class of machines can access both.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This &lt;strong&gt;is&lt;/strong&gt; a learning environment, so we&amp;rsquo;ll NAT these IPv4 networks and
assign each segment a &lt;code&gt;/64&lt;/code&gt; IPv6 subnet without blocking outgoing traffic.&lt;/p&gt;
&lt;p&gt;Probably in production we wouldn&amp;rsquo;t allow that directly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="virtual-machines"&gt;Virtual Machines&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;etcd&lt;/code&gt;: following &lt;a href="https://wikitech.wikimedia.org/wiki/Kubernetes/Clusters/New#etcd" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;WMF&amp;rsquo;s etcd recommendations for etcd&lt;/a&gt; we will use 3 VMs &lt;em&gt;dedicated&lt;/em&gt; to etcd&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Kubernetes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;control-plane: 1 VM as a starting point. These have 2 network interfaces.&lt;/li&gt;
&lt;li&gt;Nodes: 1 VM as a starting point&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a total of: 5 VMs&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Aaand, this is why Kubernetes felt like overhead for some time.&lt;/p&gt;
&lt;p&gt;Until the cost (effort, time, energy, money) of setting up and maintaining all
this is not somewhat offset by the benefits, it is the wrong tool for our scale.&lt;/p&gt;
&lt;p&gt;Now that we are planning ahead though, these are next steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/en/blog/2023-trying-out-kubernetes-2-network/"&gt;[X]&lt;/a&gt; we start by &lt;a href="/en/blog/2023-trying-out-kubernetes-2-network/"&gt;setting up the network segments&lt;/a&gt; as bridges on
      the physical host, taking care of NAT, firewall and IPv6 routing&lt;/li&gt;
&lt;li&gt;&lt;a href="/en/blog/2023-trying-out-kubernetes-3-etcd/"&gt;[X]&lt;/a&gt; then we set up the &lt;a href="/en/blog/2023-trying-out-kubernetes-3-etcd/"&gt;&lt;code&gt;etcd&lt;/code&gt; cluster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;[ ] finally, we actually setup the Kubernetes cluster&lt;/li&gt;
&lt;/ul&gt;</content></entry><entry><title>YubiKey: PGP and SSH authentication</title><link href="https://evilham.eu/en/blog/2023-yubikey-PGP-SSH-auth/" rel="alternate"></link><updated>2023-10-06T00:00:00Z</updated><author><name></name></author><id>urn:uuid:d5ee6226-3bfd-3540-a6da-7ecc2d1fda9b</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;I own a few YubiKeys and use them, a lot.&lt;/p&gt;
&lt;p&gt;If not familiar with YubiKeys, they are hardware tokens that help improve security in multiple ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They can do WebAuthn (modern web-based two factor authentication)&lt;/li&gt;
&lt;li&gt;They can hold secret keys in a way that cannot be extracted, supporting these operations:&lt;ul&gt;
&lt;li&gt;Signature&lt;/li&gt;
&lt;li&gt;Encryption&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The most common use, and what is already very useful, is using these hardware tokens for two-factor authentication.
For that there are plenty of online resources (though, people certainly can use help understanding and setting that up).&lt;/p&gt;
&lt;p&gt;What I &lt;strong&gt;really&lt;/strong&gt; care about is the latter bit: securing secret keys, particularly when it comes to securing SSH access to servers, and how that fits with PGP.&lt;/p&gt;
&lt;p&gt;Incidentally, this is where I see most online documentation falling short.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There are similar hardware tokens, YubiKeys are what I&amp;rsquo;m familiar with, and what I&amp;rsquo;ll assume here.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="table-of-contents"&gt;Table of contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#pgp-subkeys-and-ssh"&gt;PGP, subkeys and SSH&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#common-state"&gt;Common state&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#what-we-achieve-here"&gt;What we achieve here&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#roughly-why-we-do-it"&gt;(Roughly) why we do it&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#pgp-key-generation"&gt;PGP key generation&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#software-requisites"&gt;Software requisites&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#setup-the-directory"&gt;Setup the directory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#configuring-gpghome"&gt;Configuring gpghome&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#generating-the-key"&gt;Generating the key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#generating-the-key_1"&gt;Generating the key&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#putting-the-key-into-the-yubikey"&gt;Putting the Key into the YubiKey&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#copy-the-public-key"&gt;Copy the public key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#using-the-yubikey"&gt;Using the YubiKey&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#ssh-authentication"&gt;SSH authentication&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#managing-expiry-dates-and-the-certification-key"&gt;Managing expiry dates and the [C]ertification key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="pgp-subkeys-and-ssh"&gt;PGP, subkeys and SSH&lt;/h1&gt;
&lt;p&gt;Keys (and subkeys, more below) in PGP may be marked as having one or multiple uses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[C]&lt;/code&gt;ertification: a key marked like this acts as a source of truth key that can modify &lt;em&gt;subkeys&lt;/em&gt; and the key itself&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[S]&lt;/code&gt;ignature: a key marked like this can produce valid signatures that will be recognised as coming from the main key&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[E]&lt;/code&gt;encryption: a key marked like this will be associated with the main key, and it will be used by other programs to encrypt things, so only the owner of the main key can read them. Note though: this is worded in a somewhat complicated fashion, because should the Encryption key be changed, messages encrypted for the older key cannot be read&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[A]&lt;/code&gt;uthentication: a key marked like this will be associated with the main key, will be able to perform authentication e.g. against OpenSSH servers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The bit about PGP being usable for SSH &lt;code&gt;[A]&lt;/code&gt;uthentication is not very widely known, and neither is the concept of subkeys.&lt;/p&gt;
&lt;p&gt;Instead of thinking as a PGP key as: a single key for all things, we should think about it as: a &lt;code&gt;[C]&lt;/code&gt;ertification key that I keep offline except when necessary, that is associated with multiple subkeys, one for each functionality.&lt;/p&gt;
&lt;h2 id="common-state"&gt;Common state&lt;/h2&gt;
&lt;p&gt;When it comes to PGP, most advice results in keys with attributes &lt;code&gt;[SCEA]&lt;/code&gt; or &lt;code&gt;[SCA]&lt;/code&gt; with a &lt;code&gt;[E]&lt;/code&gt; subkey.&lt;/p&gt;
&lt;h2 id="what-we-achieve-here"&gt;What we achieve here&lt;/h2&gt;
&lt;p&gt;What we will achieve here is an offline &lt;code&gt;[C]&lt;/code&gt; key with multiple subkeys: &lt;code&gt;[S]&lt;/code&gt;, &lt;code&gt;[E]&lt;/code&gt;, &lt;code&gt;[A]&lt;/code&gt;, with the secret parts loaded in a YubiKey (or possibly other hardware tokens).&lt;/p&gt;
&lt;h2 id="roughly-why-we-do-it"&gt;(Roughly) why we do it&lt;/h2&gt;
&lt;p&gt;Commonly people have their PGP keys locally in a file that is secured by a password, or even separated PGP and SSH keys, protected in a similar fashion (sometimes even without a password).&lt;/p&gt;
&lt;p&gt;When there are so many &lt;a href="https://www.scmagazine.com/analysis/a-third-of-pypi-software-packages-contains-flaw-to-execute-code-when-downloaded" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;sneaky attack vectors&lt;/a&gt;, making a mistake once, can mean secrets being exfiltrated and possibly systems being compromised.&lt;/p&gt;
&lt;p&gt;By not having such files anywhere but instead relying on the hardware token, we mitigate many parts of such risks.&lt;/p&gt;
&lt;h1 id="pgp-key-generation"&gt;PGP key generation&lt;/h1&gt;
&lt;p&gt;There are a bunch of security considerations as to the system used for this, many people have written better about it.&lt;/p&gt;
&lt;p&gt;We will focus on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using a separated directory for the keyring and settings&lt;/li&gt;
&lt;li&gt;Considering the directory as a temporary a in-memory thing or equivalent&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="software-requisites"&gt;Software requisites&lt;/h2&gt;
&lt;p&gt;TODO: Check debian
- gnupg2
-&lt;/p&gt;
&lt;h2 id="setup-the-directory"&gt;Setup the directory&lt;/h2&gt;
&lt;p&gt;On FreeBSD, to use an in-memory temporary directory, we can use following:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Create directory&lt;/span&gt;
mkdir&lt;span class="w"&gt; &lt;/span&gt;pgptmp
&lt;span class="c1"&gt;# Mount in-memory filesystem&lt;/span&gt;
mount&lt;span class="w"&gt; &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;tmpfs&lt;span class="w"&gt; &lt;/span&gt;tmpfs&lt;span class="w"&gt; &lt;/span&gt;pgptmp
&lt;span class="c1"&gt;# Secure permissions&lt;/span&gt;
chmod&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;700&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pgptmp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Nothing that gets written to &lt;code&gt;pgptmp&lt;/code&gt;, will be saved to disk, instead, it is temporary and is held in RAM.&lt;/p&gt;
&lt;h2 id="configuring-gpghome"&gt;Configuring &lt;code&gt;gpghome&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;We must export the &lt;code&gt;GNUPGHOME&lt;/code&gt; variable accordingly:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pgptmp
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GNUPGHOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And it is convenient to set up certain configurations:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt;EOF &amp;gt; scdaemon.conf&lt;/span&gt;
&lt;span class="s"&gt;# This is sometimes necessary&lt;/span&gt;
&lt;span class="s"&gt;# TODO: Add mention fo scdaemon&lt;/span&gt;
&lt;span class="s"&gt;disable-ccid&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="generating-the-key"&gt;Generating the key&lt;/h2&gt;
&lt;p&gt;We use &lt;code&gt;gpg --expert --full-generate-key&lt;/code&gt;, as it is the only way we get asked all the questions.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;gpg&lt;span class="w"&gt; &lt;/span&gt;--expert&lt;span class="w"&gt; &lt;/span&gt;--full-generate-key

Please&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;what&lt;span class="w"&gt; &lt;/span&gt;kind&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;you&lt;span class="w"&gt; &lt;/span&gt;want:
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;RSA&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;RSA
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;DSA&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;Elgamal
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;DSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;sign&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;RSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;sign&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;DSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;own&lt;span class="w"&gt; &lt;/span&gt;capabilities&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;RSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;own&lt;span class="w"&gt; &lt;/span&gt;capabilities&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ECC&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;sign&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;encrypt&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;*default*
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ECC&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;sign&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;11&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ECC&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;own&lt;span class="w"&gt; &lt;/span&gt;capabilities&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;13&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Existing&lt;span class="w"&gt; &lt;/span&gt;key
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;14&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Existing&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;card
Your&lt;span class="w"&gt; &lt;/span&gt;selection?
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first question we get asked is, which kind of key we want to create.&lt;/p&gt;
&lt;p&gt;Unless we are cryptography with good reasons (please share!), I&amp;rsquo;d pick the ECC, which is the default, but setting our own capabilities (Option &lt;code&gt;11&lt;/code&gt;), that is:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Your&lt;span class="w"&gt; &lt;/span&gt;selection?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;11&lt;/span&gt;

Possible&lt;span class="w"&gt; &lt;/span&gt;actions&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;this&lt;span class="w"&gt; &lt;/span&gt;ECC&lt;span class="w"&gt; &lt;/span&gt;key:&lt;span class="w"&gt; &lt;/span&gt;Sign&lt;span class="w"&gt; &lt;/span&gt;Certify&lt;span class="w"&gt; &lt;/span&gt;Authenticate
Current&lt;span class="w"&gt; &lt;/span&gt;allowed&lt;span class="w"&gt; &lt;/span&gt;actions:&lt;span class="w"&gt; &lt;/span&gt;Sign&lt;span class="w"&gt; &lt;/span&gt;Certify

&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;S&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Toggle&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;sign&lt;span class="w"&gt; &lt;/span&gt;capability
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;A&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Toggle&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;authenticate&lt;span class="w"&gt; &lt;/span&gt;capability
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;Q&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Finished

Your&lt;span class="w"&gt; &lt;/span&gt;selection?
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We will want to generate a key with &lt;strong&gt;only&lt;/strong&gt; &lt;code&gt;[C]&lt;/code&gt;ertification capabilities, so we will ensure &lt;code&gt;[S]&lt;/code&gt; and &lt;code&gt;[A]&lt;/code&gt; are disabled:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Your&lt;span class="w"&gt; &lt;/span&gt;selection?&lt;span class="w"&gt; &lt;/span&gt;S

Possible&lt;span class="w"&gt; &lt;/span&gt;actions&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;this&lt;span class="w"&gt; &lt;/span&gt;ECC&lt;span class="w"&gt; &lt;/span&gt;key:&lt;span class="w"&gt; &lt;/span&gt;Sign&lt;span class="w"&gt; &lt;/span&gt;Certify&lt;span class="w"&gt; &lt;/span&gt;Authenticate
Current&lt;span class="w"&gt; &lt;/span&gt;allowed&lt;span class="w"&gt; &lt;/span&gt;actions:&lt;span class="w"&gt; &lt;/span&gt;Certify

&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;S&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Toggle&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;sign&lt;span class="w"&gt; &lt;/span&gt;capability
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;A&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Toggle&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;authenticate&lt;span class="w"&gt; &lt;/span&gt;capability
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;Q&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Finished

Your&lt;span class="w"&gt; &lt;/span&gt;selection?&lt;span class="w"&gt; &lt;/span&gt;Q
Please&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;which&lt;span class="w"&gt; &lt;/span&gt;elliptic&lt;span class="w"&gt; &lt;/span&gt;curve&lt;span class="w"&gt; &lt;/span&gt;you&lt;span class="w"&gt; &lt;/span&gt;want:
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Curve&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;25519&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;*default*
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Curve&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;448&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;NIST&lt;span class="w"&gt; &lt;/span&gt;P-256
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;NIST&lt;span class="w"&gt; &lt;/span&gt;P-384
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;NIST&lt;span class="w"&gt; &lt;/span&gt;P-521
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Brainpool&lt;span class="w"&gt; &lt;/span&gt;P-256
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Brainpool&lt;span class="w"&gt; &lt;/span&gt;P-384
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Brainpool&lt;span class="w"&gt; &lt;/span&gt;P-512
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;secp256k1
Your&lt;span class="w"&gt; &lt;/span&gt;selection?
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As with the key type selection, unless we have cryptography knowledge that says otherwise, we will go with Curve 25519 (Option &lt;code&gt;1&lt;/code&gt;):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Your&lt;span class="w"&gt; &lt;/span&gt;selection?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
Please&lt;span class="w"&gt; &lt;/span&gt;specify&lt;span class="w"&gt; &lt;/span&gt;how&lt;span class="w"&gt; &lt;/span&gt;long&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;should&lt;span class="w"&gt; &lt;/span&gt;be&lt;span class="w"&gt; &lt;/span&gt;valid.
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;does&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;expire
&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;n&amp;gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;expires&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="w"&gt; &lt;/span&gt;days
&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;n&amp;gt;w&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;expires&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="w"&gt; &lt;/span&gt;weeks
&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;n&amp;gt;m&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;expires&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="w"&gt; &lt;/span&gt;months
&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;n&amp;gt;y&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;expires&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="w"&gt; &lt;/span&gt;years
Key&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;valid&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The use-case with which I am documenting this, requires a key that does not expire, but my usual key expires usually ~2 years in the future, which I try to extend every year.
This choice is yours, more often might increase maintenance burden.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Key&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;valid&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
Key&lt;span class="w"&gt; &lt;/span&gt;does&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;expire&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;all
Is&lt;span class="w"&gt; &lt;/span&gt;this&lt;span class="w"&gt; &lt;/span&gt;correct?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;y/N&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;y

GnuPG&lt;span class="w"&gt; &lt;/span&gt;needs&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;construct&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;user&lt;span class="w"&gt; &lt;/span&gt;ID&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;identify&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;key.

Real&lt;span class="w"&gt; &lt;/span&gt;name:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, PGP has a weird concept of identity, and signatures are bound to the combination of Name, Email and Comment, it is usually not convenient to change this.&lt;/p&gt;
&lt;p&gt;After thinking about these decisions (usually Comment is left blank):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Real&lt;span class="w"&gt; &lt;/span&gt;name:&lt;span class="w"&gt; &lt;/span&gt;YourName
Email&lt;span class="w"&gt; &lt;/span&gt;address:&lt;span class="w"&gt; &lt;/span&gt;YourEmail@example.org
Comment:
You&lt;span class="w"&gt; &lt;/span&gt;selected&lt;span class="w"&gt; &lt;/span&gt;this&lt;span class="w"&gt; &lt;/span&gt;USER-ID:
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;YourName &amp;lt;YourEmail@Example.org&amp;gt;&amp;quot;&lt;/span&gt;

Change&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;N&lt;span class="o"&gt;)&lt;/span&gt;ame,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;C&lt;span class="o"&gt;)&lt;/span&gt;omment,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;E&lt;span class="o"&gt;)&lt;/span&gt;mail&lt;span class="w"&gt; &lt;/span&gt;or&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;O&lt;span class="o"&gt;)&lt;/span&gt;kay/&lt;span class="o"&gt;(&lt;/span&gt;Q&lt;span class="o"&gt;)&lt;/span&gt;uit?
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, when we confirm the identity, the key will be generated after we enter a passphrase to protect it.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;We&lt;span class="w"&gt; &lt;/span&gt;need&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;generate&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;lot&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;random&lt;span class="w"&gt; &lt;/span&gt;bytes.&lt;span class="w"&gt; &lt;/span&gt;It&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;good&lt;span class="w"&gt; &lt;/span&gt;idea&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;perform
some&lt;span class="w"&gt; &lt;/span&gt;other&lt;span class="w"&gt; &lt;/span&gt;action&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;on&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;keyboard,&lt;span class="w"&gt; &lt;/span&gt;move&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;mouse,&lt;span class="w"&gt; &lt;/span&gt;utilize&lt;span class="w"&gt; &lt;/span&gt;the
disks&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;during&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;prime&lt;span class="w"&gt; &lt;/span&gt;generation&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;this&lt;span class="w"&gt; &lt;/span&gt;gives&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;random&lt;span class="w"&gt; &lt;/span&gt;number
generator&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;better&lt;span class="w"&gt; &lt;/span&gt;chance&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;gain&lt;span class="w"&gt; &lt;/span&gt;enough&lt;span class="w"&gt; &lt;/span&gt;entropy.
gpg:&lt;span class="w"&gt; &lt;/span&gt;GNUPGHOME/trustdb.gpg:&lt;span class="w"&gt; &lt;/span&gt;trustdb&lt;span class="w"&gt; &lt;/span&gt;created
gpg:&lt;span class="w"&gt; &lt;/span&gt;directory&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;GNUPGHOME/openpgp-revocs.d&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;created
gpg:&lt;span class="w"&gt; &lt;/span&gt;revocation&lt;span class="w"&gt; &lt;/span&gt;certificate&lt;span class="w"&gt; &lt;/span&gt;stored&lt;span class="w"&gt; &lt;/span&gt;as&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;GNUPGHOME/openpgp-revocs.d/KEY_ID.rev&amp;#39;&lt;/span&gt;
public&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;secret&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;created&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;signed.

pub&lt;span class="w"&gt;   &lt;/span&gt;ed25519&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;C&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;KEY_ID
uid&lt;span class="w"&gt;                      &lt;/span&gt;YourName&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;YourEmail@Example.org&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This KEY_ID will uniquely identify our PGP key.&lt;/p&gt;
&lt;h2 id="generating-the-key_1"&gt;Generating the key&lt;/h2&gt;
&lt;p&gt;Since our key only has the &lt;code&gt;[C]&lt;/code&gt;ertification capability, we want to add some subkeys so we can take advantage of all options.&lt;/p&gt;
&lt;p&gt;For this, we start editing the key in &lt;code&gt;--expert&lt;/code&gt; mode, else we won&amp;rsquo;t be able to access the &lt;code&gt;[A]&lt;/code&gt;uthentication capability:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;gpg&lt;span class="w"&gt; &lt;/span&gt;--expert&lt;span class="w"&gt; &lt;/span&gt;--edit-key&lt;span class="w"&gt; &lt;/span&gt;KEY_ID
sec&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;C
&lt;span class="w"&gt;     &lt;/span&gt;trust:&lt;span class="w"&gt; &lt;/span&gt;ultimate&lt;span class="w"&gt;      &lt;/span&gt;validity:&lt;span class="w"&gt; &lt;/span&gt;ultimate
&lt;span class="o"&gt;[&lt;/span&gt;ultimate&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;YourName&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;YourEmail@example.org&amp;gt;

gpg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And now we create one key for each of the missing capabilities (&lt;code&gt;[S]&lt;/code&gt;ignature, &lt;code&gt;[E]&lt;/code&gt;ncryption, &lt;code&gt;[A]&lt;/code&gt;uthentication), trusting the current cryptography recommendations unless we have very good reasons not to, and setting the expiry according to our use-case.&lt;/p&gt;
&lt;p&gt;Note too that the &lt;code&gt;[A]&lt;/code&gt;uthentication capability is not available directly, but rather through the &lt;code&gt;ECC (set your own capabilities)&lt;/code&gt; option:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;gpg&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;addkey
Please&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;what&lt;span class="w"&gt; &lt;/span&gt;kind&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;you&lt;span class="w"&gt; &lt;/span&gt;want:
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;DSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;sign&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;RSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;sign&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Elgamal&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;encrypt&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;RSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;encrypt&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;DSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;own&lt;span class="w"&gt; &lt;/span&gt;capabilities&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;RSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;own&lt;span class="w"&gt; &lt;/span&gt;capabilities&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ECC&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;sign&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;11&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ECC&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;own&lt;span class="w"&gt; &lt;/span&gt;capabilities&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ECC&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;encrypt&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;13&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Existing&lt;span class="w"&gt; &lt;/span&gt;key
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;14&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Existing&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;card
Your&lt;span class="w"&gt; &lt;/span&gt;selection?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;
Please&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;which&lt;span class="w"&gt; &lt;/span&gt;elliptic&lt;span class="w"&gt; &lt;/span&gt;curve&lt;span class="w"&gt; &lt;/span&gt;you&lt;span class="w"&gt; &lt;/span&gt;want:
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Curve&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;25519&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;*default*
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Curve&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;448&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;NIST&lt;span class="w"&gt; &lt;/span&gt;P-256
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;NIST&lt;span class="w"&gt; &lt;/span&gt;P-384
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;NIST&lt;span class="w"&gt; &lt;/span&gt;P-521
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Brainpool&lt;span class="w"&gt; &lt;/span&gt;P-256
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Brainpool&lt;span class="w"&gt; &lt;/span&gt;P-384
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Brainpool&lt;span class="w"&gt; &lt;/span&gt;P-512
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;secp256k1
Your&lt;span class="w"&gt; &lt;/span&gt;selection?
Please&lt;span class="w"&gt; &lt;/span&gt;specify&lt;span class="w"&gt; &lt;/span&gt;how&lt;span class="w"&gt; &lt;/span&gt;long&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;should&lt;span class="w"&gt; &lt;/span&gt;be&lt;span class="w"&gt; &lt;/span&gt;valid.
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;does&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;expire
&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;n&amp;gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;expires&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="w"&gt; &lt;/span&gt;days
&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;n&amp;gt;w&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;expires&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="w"&gt; &lt;/span&gt;weeks
&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;n&amp;gt;m&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;expires&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="w"&gt; &lt;/span&gt;months
&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;n&amp;gt;y&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;expires&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="w"&gt; &lt;/span&gt;years
Key&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;valid&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
Key&lt;span class="w"&gt; &lt;/span&gt;does&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;expire&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;all
Is&lt;span class="w"&gt; &lt;/span&gt;this&lt;span class="w"&gt; &lt;/span&gt;correct?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;y/N&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;y
Really&lt;span class="w"&gt; &lt;/span&gt;create?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;y/N&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;y
We&lt;span class="w"&gt; &lt;/span&gt;need&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;generate&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;lot&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;random&lt;span class="w"&gt; &lt;/span&gt;bytes.&lt;span class="w"&gt; &lt;/span&gt;It&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;good&lt;span class="w"&gt; &lt;/span&gt;idea&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;perform
some&lt;span class="w"&gt; &lt;/span&gt;other&lt;span class="w"&gt; &lt;/span&gt;action&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;on&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;keyboard,&lt;span class="w"&gt; &lt;/span&gt;move&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;mouse,&lt;span class="w"&gt; &lt;/span&gt;utilize&lt;span class="w"&gt; &lt;/span&gt;the
disks&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;during&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;prime&lt;span class="w"&gt; &lt;/span&gt;generation&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;this&lt;span class="w"&gt; &lt;/span&gt;gives&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;random&lt;span class="w"&gt; &lt;/span&gt;number
generator&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;better&lt;span class="w"&gt; &lt;/span&gt;chance&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;gain&lt;span class="w"&gt; &lt;/span&gt;enough&lt;span class="w"&gt; &lt;/span&gt;entropy.

sec&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;C
&lt;span class="w"&gt;     &lt;/span&gt;trust:&lt;span class="w"&gt; &lt;/span&gt;ultimate&lt;span class="w"&gt;      &lt;/span&gt;validity:&lt;span class="w"&gt; &lt;/span&gt;ultimate
ssb&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID_S
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;S
&lt;span class="o"&gt;[&lt;/span&gt;ultimate&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;YourName&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;YourEmail@example.org&amp;gt;

gpg&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;addkey
Please&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;what&lt;span class="w"&gt; &lt;/span&gt;kind&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;you&lt;span class="w"&gt; &lt;/span&gt;want:
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;DSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;sign&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;RSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;sign&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Elgamal&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;encrypt&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;RSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;encrypt&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;DSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;own&lt;span class="w"&gt; &lt;/span&gt;capabilities&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;RSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;own&lt;span class="w"&gt; &lt;/span&gt;capabilities&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ECC&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;sign&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;11&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ECC&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;own&lt;span class="w"&gt; &lt;/span&gt;capabilities&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ECC&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;encrypt&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;13&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Existing&lt;span class="w"&gt; &lt;/span&gt;key
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;14&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Existing&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;card
Your&lt;span class="w"&gt; &lt;/span&gt;selection?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;
Please&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;which&lt;span class="w"&gt; &lt;/span&gt;elliptic&lt;span class="w"&gt; &lt;/span&gt;curve&lt;span class="w"&gt; &lt;/span&gt;you&lt;span class="w"&gt; &lt;/span&gt;want:
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Curve&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;25519&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;*default*
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Curve&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;448&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;NIST&lt;span class="w"&gt; &lt;/span&gt;P-256
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;NIST&lt;span class="w"&gt; &lt;/span&gt;P-384
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;NIST&lt;span class="w"&gt; &lt;/span&gt;P-521
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Brainpool&lt;span class="w"&gt; &lt;/span&gt;P-256
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Brainpool&lt;span class="w"&gt; &lt;/span&gt;P-384
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Brainpool&lt;span class="w"&gt; &lt;/span&gt;P-512
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;secp256k1
Your&lt;span class="w"&gt; &lt;/span&gt;selection?
Please&lt;span class="w"&gt; &lt;/span&gt;specify&lt;span class="w"&gt; &lt;/span&gt;how&lt;span class="w"&gt; &lt;/span&gt;long&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;should&lt;span class="w"&gt; &lt;/span&gt;be&lt;span class="w"&gt; &lt;/span&gt;valid.
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;does&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;expire
&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;n&amp;gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;expires&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="w"&gt; &lt;/span&gt;days
&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;n&amp;gt;w&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;expires&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="w"&gt; &lt;/span&gt;weeks
&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;n&amp;gt;m&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;expires&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="w"&gt; &lt;/span&gt;months
&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;n&amp;gt;y&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;expires&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="w"&gt; &lt;/span&gt;years
Key&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;valid&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
Key&lt;span class="w"&gt; &lt;/span&gt;does&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;expire&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;all
Is&lt;span class="w"&gt; &lt;/span&gt;this&lt;span class="w"&gt; &lt;/span&gt;correct?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;y/N&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;y
Really&lt;span class="w"&gt; &lt;/span&gt;create?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;y/N&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;y
We&lt;span class="w"&gt; &lt;/span&gt;need&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;generate&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;lot&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;random&lt;span class="w"&gt; &lt;/span&gt;bytes.&lt;span class="w"&gt; &lt;/span&gt;It&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;good&lt;span class="w"&gt; &lt;/span&gt;idea&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;perform
some&lt;span class="w"&gt; &lt;/span&gt;other&lt;span class="w"&gt; &lt;/span&gt;action&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;on&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;keyboard,&lt;span class="w"&gt; &lt;/span&gt;move&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;mouse,&lt;span class="w"&gt; &lt;/span&gt;utilize&lt;span class="w"&gt; &lt;/span&gt;the
disks&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;during&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;prime&lt;span class="w"&gt; &lt;/span&gt;generation&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;this&lt;span class="w"&gt; &lt;/span&gt;gives&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;random&lt;span class="w"&gt; &lt;/span&gt;number
generator&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;better&lt;span class="w"&gt; &lt;/span&gt;chance&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;gain&lt;span class="w"&gt; &lt;/span&gt;enough&lt;span class="w"&gt; &lt;/span&gt;entropy.

sec&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;C
&lt;span class="w"&gt;     &lt;/span&gt;trust:&lt;span class="w"&gt; &lt;/span&gt;ultimate&lt;span class="w"&gt;      &lt;/span&gt;validity:&lt;span class="w"&gt; &lt;/span&gt;ultimate
ssb&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID_S
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;S
ssb&lt;span class="w"&gt;  &lt;/span&gt;cv25519/KEY_ID_E
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;E
&lt;span class="o"&gt;[&lt;/span&gt;ultimate&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;YourName&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;YourEmail@example.org&amp;gt;

gpg&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;addkey
Please&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;what&lt;span class="w"&gt; &lt;/span&gt;kind&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;you&lt;span class="w"&gt; &lt;/span&gt;want:
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;DSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;sign&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;RSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;sign&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Elgamal&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;encrypt&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;RSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;encrypt&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;DSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;own&lt;span class="w"&gt; &lt;/span&gt;capabilities&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;RSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;own&lt;span class="w"&gt; &lt;/span&gt;capabilities&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ECC&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;sign&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;11&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ECC&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;own&lt;span class="w"&gt; &lt;/span&gt;capabilities&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ECC&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;encrypt&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;13&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Existing&lt;span class="w"&gt; &lt;/span&gt;key
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;14&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Existing&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;card
Your&lt;span class="w"&gt; &lt;/span&gt;selection?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;11&lt;/span&gt;

Possible&lt;span class="w"&gt; &lt;/span&gt;actions&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;this&lt;span class="w"&gt; &lt;/span&gt;ECC&lt;span class="w"&gt; &lt;/span&gt;key:&lt;span class="w"&gt; &lt;/span&gt;Sign&lt;span class="w"&gt; &lt;/span&gt;Authenticate
Current&lt;span class="w"&gt; &lt;/span&gt;allowed&lt;span class="w"&gt; &lt;/span&gt;actions:&lt;span class="w"&gt; &lt;/span&gt;Sign

&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;S&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Toggle&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;sign&lt;span class="w"&gt; &lt;/span&gt;capability
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;A&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Toggle&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;authenticate&lt;span class="w"&gt; &lt;/span&gt;capability
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;Q&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Finished

Your&lt;span class="w"&gt; &lt;/span&gt;selection?&lt;span class="w"&gt; &lt;/span&gt;S

Possible&lt;span class="w"&gt; &lt;/span&gt;actions&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;this&lt;span class="w"&gt; &lt;/span&gt;ECC&lt;span class="w"&gt; &lt;/span&gt;key:&lt;span class="w"&gt; &lt;/span&gt;Sign&lt;span class="w"&gt; &lt;/span&gt;Authenticate
Current&lt;span class="w"&gt; &lt;/span&gt;allowed&lt;span class="w"&gt; &lt;/span&gt;actions:

&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;S&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Toggle&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;sign&lt;span class="w"&gt; &lt;/span&gt;capability
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;A&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Toggle&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;authenticate&lt;span class="w"&gt; &lt;/span&gt;capability
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;Q&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Finished

Your&lt;span class="w"&gt; &lt;/span&gt;selection?&lt;span class="w"&gt; &lt;/span&gt;A

Possible&lt;span class="w"&gt; &lt;/span&gt;actions&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;this&lt;span class="w"&gt; &lt;/span&gt;ECC&lt;span class="w"&gt; &lt;/span&gt;key:&lt;span class="w"&gt; &lt;/span&gt;Sign&lt;span class="w"&gt; &lt;/span&gt;Authenticate
Current&lt;span class="w"&gt; &lt;/span&gt;allowed&lt;span class="w"&gt; &lt;/span&gt;actions:&lt;span class="w"&gt; &lt;/span&gt;Authenticate

&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;S&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Toggle&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;sign&lt;span class="w"&gt; &lt;/span&gt;capability
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;A&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Toggle&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;authenticate&lt;span class="w"&gt; &lt;/span&gt;capability
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;Q&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Finished

Your&lt;span class="w"&gt; &lt;/span&gt;selection?&lt;span class="w"&gt; &lt;/span&gt;Q
Please&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;which&lt;span class="w"&gt; &lt;/span&gt;elliptic&lt;span class="w"&gt; &lt;/span&gt;curve&lt;span class="w"&gt; &lt;/span&gt;you&lt;span class="w"&gt; &lt;/span&gt;want:
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Curve&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;25519&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;*default*
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Curve&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;448&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;NIST&lt;span class="w"&gt; &lt;/span&gt;P-256
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;NIST&lt;span class="w"&gt; &lt;/span&gt;P-384
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;NIST&lt;span class="w"&gt; &lt;/span&gt;P-521
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Brainpool&lt;span class="w"&gt; &lt;/span&gt;P-256
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Brainpool&lt;span class="w"&gt; &lt;/span&gt;P-384
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Brainpool&lt;span class="w"&gt; &lt;/span&gt;P-512
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;secp256k1
Your&lt;span class="w"&gt; &lt;/span&gt;selection?
Please&lt;span class="w"&gt; &lt;/span&gt;specify&lt;span class="w"&gt; &lt;/span&gt;how&lt;span class="w"&gt; &lt;/span&gt;long&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;should&lt;span class="w"&gt; &lt;/span&gt;be&lt;span class="w"&gt; &lt;/span&gt;valid.
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;does&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;expire
&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;n&amp;gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;expires&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="w"&gt; &lt;/span&gt;days
&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;n&amp;gt;w&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;expires&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="w"&gt; &lt;/span&gt;weeks
&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;n&amp;gt;m&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;expires&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="w"&gt; &lt;/span&gt;months
&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;n&amp;gt;y&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;expires&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="w"&gt; &lt;/span&gt;years
Key&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;valid&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
Key&lt;span class="w"&gt; &lt;/span&gt;does&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;expire&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;all
Is&lt;span class="w"&gt; &lt;/span&gt;this&lt;span class="w"&gt; &lt;/span&gt;correct?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;y/N&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;y
Really&lt;span class="w"&gt; &lt;/span&gt;create?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;y/N&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;y
We&lt;span class="w"&gt; &lt;/span&gt;need&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;generate&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;lot&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;random&lt;span class="w"&gt; &lt;/span&gt;bytes.&lt;span class="w"&gt; &lt;/span&gt;It&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;good&lt;span class="w"&gt; &lt;/span&gt;idea&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;perform
some&lt;span class="w"&gt; &lt;/span&gt;other&lt;span class="w"&gt; &lt;/span&gt;action&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;on&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;keyboard,&lt;span class="w"&gt; &lt;/span&gt;move&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;mouse,&lt;span class="w"&gt; &lt;/span&gt;utilize&lt;span class="w"&gt; &lt;/span&gt;the
disks&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;during&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;prime&lt;span class="w"&gt; &lt;/span&gt;generation&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;this&lt;span class="w"&gt; &lt;/span&gt;gives&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;random&lt;span class="w"&gt; &lt;/span&gt;number
generator&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;better&lt;span class="w"&gt; &lt;/span&gt;chance&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;gain&lt;span class="w"&gt; &lt;/span&gt;enough&lt;span class="w"&gt; &lt;/span&gt;entropy.

sec&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;C
&lt;span class="w"&gt;     &lt;/span&gt;trust:&lt;span class="w"&gt; &lt;/span&gt;ultimate&lt;span class="w"&gt;      &lt;/span&gt;validity:&lt;span class="w"&gt; &lt;/span&gt;ultimate
ssb&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID_S
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;S
ssb&lt;span class="w"&gt;  &lt;/span&gt;cv25519/KEY_ID_E
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;E
ssb&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID_A
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;A
&lt;span class="o"&gt;[&lt;/span&gt;ultimate&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;YourName&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;YourEmail@example.org&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And finally we save the modified key&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;gpg&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;save
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1 id="putting-the-key-into-the-yubikey"&gt;Putting the &lt;em&gt;Key&lt;/em&gt; into the YubiKey&lt;/h1&gt;
&lt;p&gt;This is done by editing the key we just created:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;gpg&lt;span class="w"&gt; &lt;/span&gt;--expert&lt;span class="w"&gt; &lt;/span&gt;--edit-key&lt;span class="w"&gt; &lt;/span&gt;KEY_ID
Secret&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;available.

sec&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;C
&lt;span class="w"&gt;     &lt;/span&gt;trust:&lt;span class="w"&gt; &lt;/span&gt;ultimate&lt;span class="w"&gt;      &lt;/span&gt;validity:&lt;span class="w"&gt; &lt;/span&gt;ultimate
ssb&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID_S
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;S
ssb&lt;span class="w"&gt;  &lt;/span&gt;cv25519/KEY_ID_E
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;E
ssb&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID_A
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;A
&lt;span class="o"&gt;[&lt;/span&gt;ultimate&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;YourName&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;YourEmail@example.org&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We have to select and de-select each subkey and send them to the key with &lt;code&gt;keytocard&lt;/code&gt;, and selecting the destination slot that best matches the capability:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;gpg&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;

sec&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;C
&lt;span class="w"&gt;     &lt;/span&gt;trust:&lt;span class="w"&gt; &lt;/span&gt;ultimate&lt;span class="w"&gt;      &lt;/span&gt;validity:&lt;span class="w"&gt; &lt;/span&gt;ultimate
ssb*&lt;span class="w"&gt; &lt;/span&gt;ed25519/KEY_ID_S
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;S
ssb&lt;span class="w"&gt;  &lt;/span&gt;cv25519/KEY_ID_E
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;E
ssb&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID_A
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;A
&lt;span class="o"&gt;[&lt;/span&gt;ultimate&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;YourName&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;YourEmail@example.org&amp;gt;

gpg&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;keytocard
Please&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;where&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;store&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;key:
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Signature&lt;span class="w"&gt; &lt;/span&gt;key
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Authentication&lt;span class="w"&gt; &lt;/span&gt;key
Your&lt;span class="w"&gt; &lt;/span&gt;selection?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;

sec&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;C
&lt;span class="w"&gt;     &lt;/span&gt;trust:&lt;span class="w"&gt; &lt;/span&gt;ultimate&lt;span class="w"&gt;      &lt;/span&gt;validity:&lt;span class="w"&gt; &lt;/span&gt;ultimate
ssb*&lt;span class="w"&gt; &lt;/span&gt;ed25519/KEY_ID_S
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;S
ssb&lt;span class="w"&gt;  &lt;/span&gt;cv25519/KEY_ID_E
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;E
ssb&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID_A
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;A
&lt;span class="o"&gt;[&lt;/span&gt;ultimate&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;YourName&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;YourEmail@example.org&amp;gt;

Note:&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;copy&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;secret&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;will&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="w"&gt; &lt;/span&gt;be&lt;span class="w"&gt; &lt;/span&gt;deleted&lt;span class="w"&gt; &lt;/span&gt;with&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;save&amp;quot;&lt;/span&gt;.
gpg&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;

sec&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;C
&lt;span class="w"&gt;     &lt;/span&gt;trust:&lt;span class="w"&gt; &lt;/span&gt;ultimate&lt;span class="w"&gt;      &lt;/span&gt;validity:&lt;span class="w"&gt; &lt;/span&gt;ultimate
ssb&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID_S
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;S
ssb&lt;span class="w"&gt;  &lt;/span&gt;cv25519/KEY_ID_E
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;E
ssb&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID_A
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;A
&lt;span class="o"&gt;[&lt;/span&gt;ultimate&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;YourName&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;YourEmail@example.org&amp;gt;

gpg&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;

sec&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;C
&lt;span class="w"&gt;     &lt;/span&gt;trust:&lt;span class="w"&gt; &lt;/span&gt;ultimate&lt;span class="w"&gt;      &lt;/span&gt;validity:&lt;span class="w"&gt; &lt;/span&gt;ultimate
ssb&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID_S
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;S
ssb*&lt;span class="w"&gt; &lt;/span&gt;cv25519/KEY_ID_E
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;E
ssb&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID_A
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;A
&lt;span class="o"&gt;[&lt;/span&gt;ultimate&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;YourName&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;YourEmail@example.org&amp;gt;

gpg&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;keytocard
Please&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;where&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;store&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;key:
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Encryption&lt;span class="w"&gt; &lt;/span&gt;key
Your&lt;span class="w"&gt; &lt;/span&gt;selection?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;

sec&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;C
&lt;span class="w"&gt;     &lt;/span&gt;trust:&lt;span class="w"&gt; &lt;/span&gt;ultimate&lt;span class="w"&gt;      &lt;/span&gt;validity:&lt;span class="w"&gt; &lt;/span&gt;ultimate
ssb&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID_S
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;S
ssb*&lt;span class="w"&gt; &lt;/span&gt;cv25519/KEY_ID_E
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;E
ssb&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID_A
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;A
&lt;span class="o"&gt;[&lt;/span&gt;ultimate&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;YourName&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;YourEmail@example.org&amp;gt;

Note:&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;copy&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;secret&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;will&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="w"&gt; &lt;/span&gt;be&lt;span class="w"&gt; &lt;/span&gt;deleted&lt;span class="w"&gt; &lt;/span&gt;with&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;save&amp;quot;&lt;/span&gt;.

gpg&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;

sec&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;C
&lt;span class="w"&gt;     &lt;/span&gt;trust:&lt;span class="w"&gt; &lt;/span&gt;ultimate&lt;span class="w"&gt;      &lt;/span&gt;validity:&lt;span class="w"&gt; &lt;/span&gt;ultimate
ssb&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID_S
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;S
ssb&lt;span class="w"&gt;  &lt;/span&gt;cv25519/KEY_ID_E
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;E
ssb&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID_A
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;A
&lt;span class="o"&gt;[&lt;/span&gt;ultimate&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;YourName&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;YourEmail@example.org&amp;gt;

gpg&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;

sec&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;C
&lt;span class="w"&gt;     &lt;/span&gt;trust:&lt;span class="w"&gt; &lt;/span&gt;ultimate&lt;span class="w"&gt;      &lt;/span&gt;validity:&lt;span class="w"&gt; &lt;/span&gt;ultimate
ssb&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID_S
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;S
ssb&lt;span class="w"&gt;  &lt;/span&gt;cv25519/KEY_ID_E
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;E
ssb*&lt;span class="w"&gt; &lt;/span&gt;ed25519/KEY_ID_A
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;A
&lt;span class="o"&gt;[&lt;/span&gt;ultimate&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;YourName&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;YourEmail@example.org&amp;gt;

gpg&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;keytocard
Please&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;where&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;store&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;key:
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Authentication&lt;span class="w"&gt; &lt;/span&gt;key
Your&lt;span class="w"&gt; &lt;/span&gt;selection?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;

sec&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;C
&lt;span class="w"&gt;     &lt;/span&gt;trust:&lt;span class="w"&gt; &lt;/span&gt;ultimate&lt;span class="w"&gt;      &lt;/span&gt;validity:&lt;span class="w"&gt; &lt;/span&gt;ultimate
ssb&lt;span class="w"&gt;  &lt;/span&gt;ed25519/KEY_ID_S
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;S
ssb&lt;span class="w"&gt;  &lt;/span&gt;cv25519/KEY_ID_E
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;E
ssb*&lt;span class="w"&gt; &lt;/span&gt;ed25519/KEY_ID_A
&lt;span class="w"&gt;     &lt;/span&gt;created:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2023&lt;/span&gt;-10-06&lt;span class="w"&gt;  &lt;/span&gt;expires:&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt;       &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;A
&lt;span class="o"&gt;[&lt;/span&gt;ultimate&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;YourName&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;YourEmail@example.org&amp;gt;

Note:&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;copy&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;secret&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;will&lt;span class="w"&gt; &lt;/span&gt;only&lt;span class="w"&gt; &lt;/span&gt;be&lt;span class="w"&gt; &lt;/span&gt;deleted&lt;span class="w"&gt; &lt;/span&gt;with&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;save&amp;quot;&lt;/span&gt;.
gpg&amp;gt;
Save&lt;span class="w"&gt; &lt;/span&gt;changes?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;y/N&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;N
Quit&lt;span class="w"&gt; &lt;/span&gt;without&lt;span class="w"&gt; &lt;/span&gt;saving?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;y/N&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;y
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that I exit without saving, because I want to back up the &lt;code&gt;[C]&lt;/code&gt;ertification key offline, and saving the key here will &lt;strong&gt;remove the private part&lt;/strong&gt; from the keyring.&lt;/p&gt;
&lt;p&gt;It will be contained in the YubiKey and can be used with the password, but it cannot be retrieved out of it.&lt;/p&gt;
&lt;h1 id="copy-the-public-key"&gt;Copy the public key&lt;/h1&gt;
&lt;p&gt;We now want to export the public key:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;gpg&lt;span class="w"&gt; &lt;/span&gt;--export&lt;span class="w"&gt; &lt;/span&gt;KEY_ID&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;pgp.pubkey.bin
$&lt;span class="w"&gt; &lt;/span&gt;gpg&lt;span class="w"&gt; &lt;/span&gt;--export&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;KEY_ID&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;pgp.pubkey.pem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which we will need to publish and import on our daily driver.&lt;/p&gt;
&lt;h1 id="using-the-yubikey"&gt;Using the YubiKey&lt;/h1&gt;
&lt;p&gt;Now we can disconnect the backed up &lt;code&gt;[C]&lt;/code&gt;ertification key, reboot into our persistent environment or change computers, depending on what our security precautions were, and move to our daily driver system.&lt;/p&gt;
&lt;p&gt;Once there, we import the &lt;strong&gt;public key&lt;/strong&gt; into our regular keyring:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# This is not the same system we were in before(!)&lt;/span&gt;
gpg&lt;span class="w"&gt; &lt;/span&gt;--import&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;pgp.pubkey.bin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And regular PGP operations should request unlocking the corresponding private key with its regular password.&lt;/p&gt;
&lt;h2 id="ssh-authentication"&gt;SSH authentication&lt;/h2&gt;
&lt;p&gt;In order to take advantage of SSH authentication, we want to use the gpg-agent as an SSH agent.&lt;/p&gt;
&lt;p&gt;This is usually done by setting up the &lt;code&gt;gpg-agent.conf&lt;/code&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt;EOF &amp;gt; gpg-agent.conf&lt;/span&gt;
&lt;span class="s"&gt;enable-ssh-support&lt;/span&gt;
&lt;span class="s"&gt;pinentry-program /usr/local/bin/pinentry-gtk2&lt;/span&gt;
&lt;span class="s"&gt;# Possibly on Debian-based:&lt;/span&gt;
&lt;span class="s"&gt;#pinentry-program /usr/bin/pinentry-gtk2&lt;/span&gt;
&lt;span class="s"&gt;# 2 min&lt;/span&gt;
&lt;span class="s"&gt;default-cache-ttl 120&lt;/span&gt;
&lt;span class="s"&gt;# 5 min&lt;/span&gt;
&lt;span class="s"&gt;max-cache-ttl 300&lt;/span&gt;
&lt;span class="s"&gt;# Grab input&lt;/span&gt;
&lt;span class="s"&gt;grab&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;span class="c1"&gt;# Applying this might require restarting the gpg-agent, you can use:&lt;/span&gt;
&lt;span class="c1"&gt;# gpgconf --kill gpg-agent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is usually done adding to the &lt;code&gt;${HOME}/.profile&lt;/code&gt; file something similar to:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Setup the environment variable so the correct SSH agent is contacted&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;SSH_AUTH_SOCK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;gpgconf&lt;span class="w"&gt; &lt;/span&gt;--list-dirs&lt;span class="w"&gt; &lt;/span&gt;agent-ssh-socket&lt;span class="k"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Tell the gpg-agent to start and update its tty&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UPDATESTARTUPTTY&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;gpg-connect-agent&lt;span class="w"&gt; &lt;/span&gt;/bye
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that if you use gnome, KDE or similar, their agents might have support for something like this already.&lt;/p&gt;
&lt;p&gt;Once the SSH and GPG agents have been set up, the SSH agent will list the key contained in the YubiKey as available for authentication:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;ssh-add&lt;span class="w"&gt; &lt;/span&gt;-L
ssh-ed25519&lt;span class="w"&gt; &lt;/span&gt;AAAA...J&lt;span class="w"&gt; &lt;/span&gt;cardno:YY_YYY_YYY
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By adding that line to an &lt;code&gt;authorized_keys&lt;/code&gt; file, we should be able to use the &lt;code&gt;[A]&lt;/code&gt;uthentication private key to enter with SSH on that system.&lt;/p&gt;
&lt;h1 id="managing-expiry-dates-and-the-certification-key"&gt;Managing expiry dates and the &lt;code&gt;[C]&lt;/code&gt;ertification key&lt;/h1&gt;
&lt;p&gt;This is similar to the creation and sending to card process, except we use the &lt;code&gt;expiry&lt;/code&gt; gpg command after editing.&lt;/p&gt;
&lt;p&gt;We can change the expiry date of multiple subkeys by selecting more than one and export the resulting public key.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Given the implications of such setup, some thought and personal/corporation-wide trade-offs must be considered, it is certainly not a 5 minute task.&lt;/p&gt;
&lt;p&gt;But it also isn&amp;rsquo;t as scary as it sounds and the full described process (along with documenting it here) took about 90 minutes.&lt;/p&gt;
&lt;p&gt;The result is actually more usable and secure than before:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In order to login to places, we need both something physical (the YubiKey) and something we know (its protection password)&lt;/li&gt;
&lt;li&gt;If we want to be sure no keys are loaded, we just unplug the YubiKey&lt;/li&gt;
&lt;li&gt;If we want to log in places, sign something or read an encrypted email, we plug it in&lt;/li&gt;
&lt;li&gt;We can backup and revoke the keys as necessary, if we ever lose the YubiKey, we can revoke the subkeys and generate new ones from the offline &lt;code&gt;[C]&lt;/code&gt;ertification key, while still having access to previously encrypted content&lt;/li&gt;
&lt;li&gt;If something bad happens with our daily driver, odds are that not having &lt;code&gt;id_rsa&lt;/code&gt; files or keys in the keyring will save us a lot of headaches and lateral jumps&lt;/li&gt;
&lt;/ul&gt;</content></entry><entry><title>ZFS replication tools</title><link href="https://evilham.eu/en/blog/2023-ZFS-replication-tools/" rel="alternate"></link><updated>2023-07-04T00:00:00Z</updated><author><name></name></author><id>urn:uuid:99e47d48-88ec-3dbb-a35f-f03c2ddad257</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;Back when I first started using &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; and ZFS, I needed tools
with an extremely low barrier of entry, that did their job well.&lt;/p&gt;
&lt;p&gt;When it comes to creating and pruning &lt;code&gt;snapshots&lt;/code&gt;, the job was first done by
&lt;a href="https://cgit.freebsd.org/ports/tree/sysutils/zfs-periodic" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;sysutils/zfs-periodic&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Over time however, even with minor improvements on my part, it has come short
for my current needs.&lt;/p&gt;
&lt;p&gt;As inspired by &lt;a href="https://dan.langille.org/2019/11/11/zfstools-sanoid-snapshots-on-the-local-host/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Dan Langille&amp;rsquo;s wonderful blog&lt;/a&gt; (Dan always
writes great blog posts, that end up being lovely complementary
documentation), I document here my somewhat bumpy road when it comes to
ZFS replication tools and why you might want to use something different at
each step.&lt;/p&gt;
&lt;h1 id="table-of-contents"&gt;Table of contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#quick-reminder-of-zfs"&gt;Quick reminder of ZFS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#snapshot-creation-and-prunning-with-zfs-periodic8"&gt;Snapshot creation and prunning with zfs-periodic(8)&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#adding-quarter_hourly"&gt;Adding quarter_hourly&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#zfs-replication-with-zxfer8"&gt;ZFS Replication with zxfer(8)&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#the-caveats"&gt;The caveats&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#encrypted-datasets"&gt;Encrypted datasets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#snapshot-creation-and-pruning-with-sanoid"&gt;Snapshot creation and pruning with sanoid&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#migrating-zfs-periodic8-snapshots-to-sanoid"&gt;Migrating zfs-periodic(8) snapshots to sanoid&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#snapshot-creation-and-pruning-with-sanoid_1"&gt;snapshot creation and pruning with sanoid&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#zfs-replication-with-syncoid"&gt;ZFS Replication with syncoid&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#issues-with-permissions"&gt;Issues with permissions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#issues-with-public-key-limitations"&gt;Issues with public key limitations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#encrypted-datasets_1"&gt;Encrypted datasets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#all-together"&gt;All together&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#remembering-zrepl"&gt;Remembering zrepl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#update-bonus-tools-from-the-fediverse"&gt;UPDATE: Bonus tools from the fediverse&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#zfs_autobackup"&gt;zfs_autobackup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#zrep"&gt;zrep&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="quick-reminder-of-zfs"&gt;Quick reminder of &lt;a href="https://en.wikipedia.org/wiki/ZFS" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;ZFS&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/ZFS" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;ZFS&lt;/a&gt; is nowadays (at least on &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; and Linux) actually
&lt;a href="https://github.com/openzfs/zfs/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;OpenZFS&lt;/a&gt;. Let&amp;rsquo;s see how they define it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;OpenZFS is an advanced file system and volume manager which was originally
developed for Solaris and is now maintained by the OpenZFS community.
This repository contains the code for running OpenZFS on Linux and FreeBSD.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Amongst the many beautiful things of ZFS, there is the fact that it works in a
&lt;a href="https://en.wikipedia.org/wiki/Copy-on-write" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Copy on Write&lt;/a&gt; fashion, which, amongst other things, enables it to create
&lt;code&gt;snapshots&lt;/code&gt; in an instantaneous fashion.&lt;/p&gt;
&lt;p&gt;That is, we can assign a name to a known state with, e.g.
&lt;code&gt;zfs snapshot -r zroot@goodstate&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Keep on working and, if we get on to a bad state, discard any changes made
since then by issuing a &lt;code&gt;rollback&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This means, we can have a very handy way of having real-life save points, and
work in a more relaxed fashion.&lt;/p&gt;
&lt;p&gt;Together with &lt;code&gt;zfs-send(8)&lt;/code&gt; and &lt;code&gt;zfs-receive(8)&lt;/code&gt;, we
quickly have the basis for a remote backup system that is incremental.&lt;/p&gt;
&lt;p&gt;Now that ZFS data set encryption (&lt;code&gt;zfs-load-key(8)&lt;/code&gt;) is a thing, it too can
be encrypted.&lt;/p&gt;
&lt;h1 id="snapshot-creation-and-prunning-with-zfs-periodic8"&gt;Snapshot creation and prunning with &lt;code&gt;zfs-periodic(8)&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;Now, back in ~2019, I was mostly a Linux user that was jumping to
&lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; and the last thing I wanted was to fiddle too much with a
new file system.&lt;/p&gt;
&lt;p&gt;Luckily, there is a very simple way to manage ZFS snapshots in an automatic
fashion right in the ports system: &lt;code&gt;zfs-periodic&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It profits from the &lt;code&gt;periodic(8)&lt;/code&gt; system, to run at regular
intervals, and gets configured in a minute with:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;pkg&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;zfs-periodic
Updating&lt;span class="w"&gt; &lt;/span&gt;FreeBSD&lt;span class="w"&gt; &lt;/span&gt;repository&lt;span class="w"&gt; &lt;/span&gt;catalogue...
FreeBSD&lt;span class="w"&gt; &lt;/span&gt;repository&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;up&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;date.

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;/1&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Fetching&lt;span class="w"&gt; &lt;/span&gt;zfs-periodic-1.0.20130213.pkg:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;%&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;KiB&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.3kB/s&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;:01
Checking&lt;span class="w"&gt; &lt;/span&gt;integrity...&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;conflicting&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;/1&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Installing&lt;span class="w"&gt; &lt;/span&gt;zfs-periodic-1.0.20130213...
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;/1&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Extracting&lt;span class="w"&gt; &lt;/span&gt;zfs-periodic-1.0.20130213:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;%
&lt;span class="o"&gt;=====&lt;/span&gt;
Message&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;zfs-periodic-1.0.20130213:

--
In&lt;span class="w"&gt; &lt;/span&gt;order&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;enable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;periodic&lt;span class="w"&gt; &lt;/span&gt;snapshots&lt;span class="w"&gt; &lt;/span&gt;you&lt;span class="w"&gt; &lt;/span&gt;need
to&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;these&lt;span class="w"&gt; &lt;/span&gt;lines&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;/etc/periodic.conf

&lt;span class="nv"&gt;hourly_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;root&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;hourly_show_success&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;NO&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;hourly_show_info&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;YES&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;hourly_show_badconfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;NO&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;hourly_zfs_snapshot_enable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;YES&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;hourly_zfs_snapshot_pools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;tank&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;hourly_zfs_snapshot_keep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;
&lt;span class="nv"&gt;daily_zfs_snapshot_enable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;YES&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;daily_zfs_snapshot_pools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;tank&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;daily_zfs_snapshot_keep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;
&lt;span class="nv"&gt;weekly_zfs_snapshot_enable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;YES&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;weekly_zfs_snapshot_pools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;tank&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;weekly_zfs_snapshot_keep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="nv"&gt;monthly_zfs_snapshot_enable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;YES&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;monthly_zfs_snapshot_pools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;tank&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;monthly_zfs_snapshot_keep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;

To&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;hourly&lt;span class="w"&gt; &lt;/span&gt;snapshots&lt;span class="w"&gt; &lt;/span&gt;you&lt;span class="w"&gt; &lt;/span&gt;also&lt;span class="w"&gt; &lt;/span&gt;need&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;add
something&lt;span class="w"&gt; &lt;/span&gt;like&lt;span class="w"&gt; &lt;/span&gt;this&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;/etc/crontab:

&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;*&lt;span class="w"&gt;       &lt;/span&gt;*&lt;span class="w"&gt;       &lt;/span&gt;*&lt;span class="w"&gt;       &lt;/span&gt;*&lt;span class="w"&gt;       &lt;/span&gt;root&lt;span class="w"&gt;    &lt;/span&gt;periodic&lt;span class="w"&gt; &lt;/span&gt;hourly
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We literally do that (adding the &lt;code&gt;crontab(5)&lt;/code&gt; entry and configuring
&lt;code&gt;zfs-periodic(8)&lt;/code&gt; in  &lt;code&gt;/usr/local/etc/periodic.conf&lt;/code&gt; (*)), and we have a simple
system that takes hourly, daily, weekly and monthly snapshots.&lt;/p&gt;
&lt;p&gt;Half way there with zero effort, this is what has kept me using it for years.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;(*): I prefer to use &lt;code&gt;/usr/local/etc/periodic.conf&lt;/code&gt; for things outside the
     base system. This is a matter of taste and it helps easen administration.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="adding-quarter_hourly"&gt;Adding &lt;code&gt;quarter_hourly&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Given how cheap these snapshots are, I (and people at work) started wanting
to have them more often, which in my case meant every 15 minutes.&lt;/p&gt;
&lt;p&gt;Luckily, &lt;code&gt;periodic(8)&lt;/code&gt; supports arbitrary directories:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;directory  An arbitrary directory containing a set of executables to be
           run.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, it wasn&amp;rsquo;t as simple as:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;/usr/local/etc/periodic/quarter_hourly/
ln&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;/usr/local/etc/periodic/daily/000.zfs-snapshot&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;/usr/local/periodic/quarter_hourly/000.zfs-snapshot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because the actual file in there depends on the name of the directory, so it
has to be customised.
And also because the &lt;code&gt;/usr/local/bin/zfs-snapshot&lt;/code&gt; script that gets installed
needs to understand all the &lt;code&gt;periodic.conf&lt;/code&gt; settings.&lt;/p&gt;
&lt;p&gt;Luckily making these changes was fairly simple.
Since I won&amp;rsquo;t be using &lt;code&gt;zfs-periodic(8)&lt;/code&gt; any longer, and the patch has been
more than tested over the years, I&amp;rsquo;m opening a
&lt;a href="https://github.com/ross/zfs-periodic/pull/10" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Merge Request&lt;/a&gt;, which you can use to benefit from these
changes.&lt;/p&gt;
&lt;p&gt;Take into account that it&amp;rsquo;ll also need a corresponding &lt;code&gt;crontab(5)&lt;/code&gt; entry:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;*/15       *       *       *       *       root    periodic quarter_hourly
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1 id="zfs-replication-with-zxfer8"&gt;ZFS Replication with &lt;code&gt;zxfer(8)&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;Snapshots take care of local mess ups, but they don&amp;rsquo;t serve as a backup.&lt;/p&gt;
&lt;p&gt;For that, I took to &lt;a href="https://github.com/allanjude/zxfer/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;zxfer&lt;/a&gt;, which is simple to use and has worked
great with some caveats.&lt;/p&gt;
&lt;p&gt;A lovely thing of &lt;code&gt;zxfer(8)&lt;/code&gt; is that it supports an &lt;code&gt;rsync(1)&lt;/code&gt; mode, that
allows us to back up remote systems that do not support ZFS, to a ZFS-based
system that takes care of snapshots and so on.&lt;/p&gt;
&lt;h2 id="the-caveats"&gt;The caveats&lt;/h2&gt;
&lt;p&gt;Mostly, even with a pull-based approach, &lt;code&gt;zxfer(8)&lt;/code&gt; doesn&amp;rsquo;t protect much
against snapshots disappearing on the source, which means that there is a
somewhat plausible scenario where pulling data results in breaking certain
retention policies.&lt;/p&gt;
&lt;p&gt;While there is a &lt;code&gt;-g&lt;/code&gt; flag to protect old snapshots, it means that we have to
replicate snapshot pruning on the destination system, but cannot create
snapshots as they could collide with the source snapshots.&lt;/p&gt;
&lt;p&gt;That means &lt;code&gt;zfs-periodic(8)&lt;/code&gt; and &lt;code&gt;zxfer(8)&lt;/code&gt; are better than nothing and work
decently, but have important limitations.&lt;/p&gt;
&lt;h3 id="encrypted-datasets"&gt;Encrypted datasets&lt;/h3&gt;
&lt;p&gt;When encrypted datasets got added to &lt;a href="https://github.com/openzfs/zfs/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;OpenZFS&lt;/a&gt;, I realised that
we need to use &lt;code&gt;zfs send -w&lt;/code&gt;, or else the backup will happen unencrypted.
So I wrote a &lt;a href="https://github.com/allanjude/zxfer/pull/55" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;patch for zxfer&lt;/a&gt; to do just that.&lt;/p&gt;
&lt;p&gt;You can help land &lt;a href="https://github.com/allanjude/zxfer/pull/55" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;that patch&lt;/a&gt; in &lt;code&gt;zxfer(8)&lt;/code&gt;, it is basically
only missing the manpage and USAGE texts, but my manpage syntax is rusty
and time is not an abundantly available commodity.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d love to help you land &lt;a href="https://github.com/allanjude/zxfer/pull/55" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;this change&lt;/a&gt; though!&lt;/p&gt;
&lt;h1 id="snapshot-creation-and-pruning-with-sanoid"&gt;Snapshot creation and pruning with &lt;code&gt;sanoid&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;After having read &lt;a href="https://dan.langille.org/2019/11/11/zfstools-sanoid-snapshots-on-the-local-host/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Dan Langille&amp;rsquo;s blog post&lt;/a&gt; on ZFS tools several
months ago, I had been wanting to give it a try, since it looked simple to
setup and more akin to my current needs.&lt;/p&gt;
&lt;p&gt;I based my configuration mostly off Dan&amp;rsquo;s, so checked that
&lt;a href="https://dan.langille.org/2019/11/11/zfstools-sanoid-snapshots-on-the-local-host/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;great post&lt;/a&gt; :-).&lt;/p&gt;
&lt;h2 id="migrating-zfs-periodic8-snapshots-to-sanoid"&gt;Migrating &lt;code&gt;zfs-periodic(8)&lt;/code&gt; snapshots to sanoid&lt;/h2&gt;
&lt;p&gt;This ends up being somewhat simple with &lt;a href="/en/blog/2023-ZFS-replication-tools/periodic2sanoid.sh"&gt;&lt;code&gt;periodic2sanoid.sh&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Check locally and remotely that actions make sense (and results match)&lt;/span&gt;
&lt;span class="c1"&gt;#  output lists &amp;quot;DS&amp;quot; as the base dataset, it acts recursively&lt;/span&gt;
./periodic2sanoid.sh&lt;span class="w"&gt; &lt;/span&gt;zroot/usr/home
&lt;span class="o"&gt;[&lt;/span&gt;...&lt;span class="o"&gt;]&lt;/span&gt;
DS@daily-2023-06-28&lt;span class="w"&gt; &lt;/span&gt;--&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;DS@autosnap_2023-06-28_12:24:44_daily
&lt;span class="o"&gt;[&lt;/span&gt;...&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# Actually apply changes&lt;/span&gt;
env&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DRY_RUN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;NO&lt;span class="w"&gt; &lt;/span&gt;./periodic2sanoid.sh&lt;span class="w"&gt; &lt;/span&gt;zroot/usr/home
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And here is the code for that script, you can (and should!) audit it:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/bin/sh -eu&lt;/span&gt;

&lt;span class="c1"&gt;# Copyright (c) 2023, Evilham (https://evilham.com)&lt;/span&gt;
&lt;span class="c1"&gt;# BSD 2-Clause copyright and disclaimer apply.&lt;/span&gt;
&lt;span class="c1"&gt;# See: http://www.opensource.org/licenses/bsd-license.php&lt;/span&gt;

&lt;span class="nv"&gt;DS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="nv"&gt;DRY_RUN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DRY_RUN&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;YES&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

epoch_to_time&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;epoch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;date&lt;span class="w"&gt; &lt;/span&gt;-j&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;%s&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;epoch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;+%Y-%m-%d_%H:%M:%S&amp;#39;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

target_name&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;snap_epoch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;snap_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;snap_time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;epoch_to_time&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;snap_epoch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;snap_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;quarter_hourly-*&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;# quarter_hourly --&amp;gt; frequently&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;autosnap_%s_%s&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;snap_time&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;frequently&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;hourly-*&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;autosnap_%s_%s&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;snap_time&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hourly&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;daily-*&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;autosnap_%s_%s&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;snap_time&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;daily&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;weekly-*&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;autosnap_%s_%s&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;snap_time&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;weekly&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;monthly-*&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;autosnap_%s_%s&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;snap_time&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;monthly&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;yearly-*&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;autosnap_%s_%s&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;snap_time&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;yearly&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;*&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;# Empty --&amp;gt; no renaming&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


zfs&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;-rpHt&lt;span class="w"&gt; &lt;/span&gt;snap&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;creation&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;creation,name&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$DS&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;line&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;snap_epoch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;cut&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;full_snap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;cut&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;ds_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;full_snap&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;cut&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;@&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;snap_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;full_snap&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;cut&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;@&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;-&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;snap_target_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;target_name&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;snap_epoch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;snap_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;snap_target_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# We use DS to facilitate comparing local and remote actions&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;%s@%s\t--&amp;gt;\t%s@%s\n&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;DS&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;snap_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;DS&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;snap_target_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DRY_RUN&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;YES&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;zfs rename \&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;full_snap&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\&amp;quot; \&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ds_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;snap_target_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;zfs&lt;span class="w"&gt; &lt;/span&gt;rename&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;full_snap&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ds_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;snap_target_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="snapshot-creation-and-pruning-with-sanoid_1"&gt;snapshot creation and pruning with &lt;code&gt;sanoid&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;This, as &lt;a href="https://dan.langille.org/2019/11/11/zfstools-sanoid-snapshots-on-the-local-host/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Dan documented it&lt;/a&gt;, works wonderfully after taking
into account the &lt;code&gt;crontab(5)&lt;/code&gt; caveat and using &lt;code&gt;lockf(1)&lt;/code&gt;.&lt;/p&gt;
&lt;h1 id="zfs-replication-with-syncoid"&gt;ZFS Replication with &lt;code&gt;syncoid&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;When installing &lt;code&gt;sanoid&lt;/code&gt;, we also get &lt;code&gt;syncoid&lt;/code&gt; as a replication tool.&lt;/p&gt;
&lt;h2 id="issues-with-permissions"&gt;Issues with permissions&lt;/h2&gt;
&lt;p&gt;While trying to replicate the datasets, I realised that it expects either
to run as &lt;code&gt;root&lt;/code&gt; or to have &lt;code&gt;sudo&lt;/code&gt; available.&lt;/p&gt;
&lt;p&gt;Of course, that&amp;rsquo;s not ideal, as I prefer to rely on &lt;code&gt;zfs-allow(8)&lt;/code&gt; and do not
even have &lt;code&gt;sudo&lt;/code&gt; as an available command.&lt;/p&gt;
&lt;p&gt;There is a flag &lt;code&gt;--no-privilege-elevation&lt;/code&gt; which helps circumvent these checks,
but the fact that it fails and requires the flag to work as non-root, is kind
of a red flag for me.&lt;/p&gt;
&lt;h2 id="issues-with-public-key-limitations"&gt;Issues with public key limitations&lt;/h2&gt;
&lt;p&gt;As with &lt;code&gt;zxfer(8)&lt;/code&gt;, I was setting up a pull-based approach over SSH.
Since this user and that SSH key should only be allowed to use &lt;code&gt;zfs send&lt;/code&gt;
and little more, I use the &lt;code&gt;authorized_keys&lt;/code&gt; file to force the &lt;code&gt;command&lt;/code&gt;
to a particular script.&lt;/p&gt;
&lt;p&gt;This resulted in issues with &lt;code&gt;syncoid&lt;/code&gt;&amp;lsquo;s shell escaping, which lead me to
this &lt;a href="https://github.com/jimsalterjrs/sanoid/issues/719" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;reported issue&lt;/a&gt; from 2022.
I believe the person that reported it was in a similar position to mine.&lt;/p&gt;
&lt;p&gt;I ended up &amp;ldquo;fixing it&amp;rdquo; (the security implications are still to be determined),
by expanding the list of allowed commands and using &lt;code&gt;sh -s&lt;/code&gt; with
&lt;code&gt;$SSH_ORIGINAL_COMMAND&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SSH_ORIGINAL_COMMAND&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;uname&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;zfs get &amp;quot;&lt;/span&gt;*&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/sbin/zfs get &amp;quot;&lt;/span&gt;*&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;zfs list &amp;quot;&lt;/span&gt;*&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/sbin/zfs list &amp;quot;&lt;/span&gt;*&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;zfs send -n &amp;quot;&lt;/span&gt;*&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;zfs send -w &amp;quot;&lt;/span&gt;*&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; zfs send -w &amp;quot;&lt;/span&gt;*&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/sbin/zfs send -w &amp;quot;&lt;/span&gt;*&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;echo -n&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;command -v lzop&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;command -v mbuffer&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;zpool get -o value -H feature@extensible_dataset &amp;quot;&lt;/span&gt;*&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;exit&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;*&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Command not allowed! See ya!&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SSH_ORIGINAL_COMMAND&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/tmp/test
sh&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt;EOF&lt;/span&gt;
&lt;span class="s"&gt;${SSH_ORIGINAL_COMMAND}&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="encrypted-datasets_1"&gt;Encrypted datasets&lt;/h2&gt;
&lt;p&gt;This is also a bit of an issue, and we need to specify &lt;code&gt;--sendoptions="w"&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="all-together"&gt;All together&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;syncoid&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;--no-privilege-elevation&lt;span class="w"&gt; &lt;/span&gt;--sendoptions&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;sucker@HOST:zroot/usr/home&lt;span class="w"&gt; &lt;/span&gt;backups/pools/HOST/zroot/usr/home
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This kind of works, but when looking to refine it, I realised that it does not
offer me any benefit regarding &lt;code&gt;zxfer(8)&lt;/code&gt; when it comes to protecting the
snapshots.&lt;/p&gt;
&lt;h1 id="remembering-zrepl"&gt;Remembering &lt;code&gt;zrepl&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;After getting frustrated again, I realised this wasn&amp;rsquo;t quite looking as I was
expecting (I was so sure this was doable!).&lt;/p&gt;
&lt;p&gt;Turns out, I had read about &lt;a href="https://zrepl.github.io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;zrepl&lt;/a&gt; some months ago, and checked the
documentation and determined it was something I needed to use.&lt;/p&gt;
&lt;p&gt;So, this misremembering caused me to look deeply into &lt;code&gt;sanoid&lt;/code&gt; and &lt;code&gt;syncoid&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But hey, at least now I am very sure about what I want, and I can probably
quickly adapt my renaming script for a migration to zrepl.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;I really should blog more often, if anything to save me some time.&lt;/p&gt;
&lt;p&gt;This will have to be a two-parter, with this one being what I&amp;rsquo;ve tried and
why I&amp;rsquo;m not keeping it, and the second one being how I get to setup
&lt;a href="https://zrepl.github.io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;zrepl&lt;/a&gt; to my liking.&lt;/p&gt;
&lt;h1 id="update-bonus-tools-from-the-fediverse"&gt;UPDATE: Bonus tools from the fediverse&lt;/h1&gt;
&lt;p&gt;I &lt;a href="https://chaos.social/@evilham/110657917872957344" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;posted this&lt;/a&gt; on the
fediverse, and some people shared what they use and why!&lt;/p&gt;
&lt;iframe src="https://chaos.social/@evilham/110657917872957344/embed" class="mastodon-embed" style="max-width: 100%; border: 0" width="400" allowfullscreen="allowfullscreen"&gt;&lt;/iframe&gt;
&lt;script src="https://chaos.social/embed.js" async="async"&gt;&lt;/script&gt;

&lt;h3 id="zfs_autobackup"&gt;&lt;code&gt;zfs_autobackup&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://mdon.stefanomarinelli.it/@stefano/110658001245769750" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;@stefano@mdon.stefanomarinelli.it&lt;/a&gt; mentioned
&lt;a href="https://github.com/psy0rz/zfs_autobackup" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;zfs_autobackup&lt;/a&gt;, and they have a very nice
&lt;a href="https://it-notes.dragas.net/2022/05/30/how-we-are-migrating-many-of-our-servers-from-linux-to-freebsd-part-2/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;blog post series&lt;/a&gt; documenting this, and other steps they took
on migrating from Linux to &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;.&lt;/p&gt;
&lt;iframe src="https://mdon.stefanomarinelli.it/@stefano/110658001245769750/embed" class="mastodon-embed" style="max-width: 100%; border: 0" width="400" allowfullscreen="allowfullscreen"&gt;&lt;/iframe&gt;

&lt;h2 id="zrep"&gt;&lt;code&gt;zrep&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://radiosocial.de/@dk3jf/110660370655210749" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;@dk3jf@radiosocial.de&lt;/a&gt; mentioned &lt;a href="https://github.com/bolthole/zrep" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;zrep&lt;/a&gt;:&lt;/p&gt;
&lt;iframe src="https://radiosocial.de/@dk3jf/110660370655210749/embed" class="mastodon-embed" style="max-width: 100%; border: 0" width="400" allowfullscreen="allowfullscreen"&gt;&lt;/iframe&gt;</content></entry><entry><title>Python: yield, generators and Deferreds</title><link href="https://evilham.eu/en/blog/2022-python-yield-generator-deferred/" rel="alternate"></link><updated>2022-08-28T00:00:00Z</updated><author><name></name></author><id>urn:uuid:4585092c-9260-39e7-8120-25ad65079d49</id><content type="html">&lt;h1 id="introduccio"&gt;Introducció&lt;/h1&gt;
&lt;p&gt;El company Pedro està aprenent a fer anar &lt;a href="https://docs.scrapy.org/en/latest/index.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Scrapy&lt;/a&gt; i em comenta
espontàniament:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;una cosa meravellosa d&amp;rsquo;emacs és que està tot molt ben documentat, i de seguida estàs mirant el codi font còmodament (tant sigui emacs lisp que c); hi ha algun equivalent a python? he intentat això:&lt;/p&gt;
&lt;p&gt;(ara m&amp;rsquo;he posat amb el scrapy tutorial)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;12345678910&lt;/span&gt;&lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;49&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;28&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ipython3&lt;/span&gt;
&lt;span class="n"&gt;Python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.9.2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;def&lt;/span&gt;&lt;span class="n"&gt;ault&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Feb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;28&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2021&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;17&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;03&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;44&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;copyright&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;credits&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;license&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;more&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;information&lt;/span&gt;
&lt;span class="n"&gt;IPython&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;7.20.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;An&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;enhanced&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="n"&gt;eractive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Python&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;?&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yield&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;lt;ipython-input-1-b8899ae5635b&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yield&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;
&lt;span class="n"&gt;SyntaxError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;invalid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;syntax&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;El poc que sé de &lt;a href="https://docs.scrapy.org/en/latest/index.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Scrapy&lt;/a&gt; és que és una eina molt potent i que paga
la pena aprendre (però encara no hi he tingut ocasió!) i, encara més
important: que està escrita en &lt;a href="https://python.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Python&lt;/a&gt;, fent servir la llibreria
&lt;a href="https://twistedmatrix.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Twisted&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Amb aquesta informació i havent après &lt;a href="https://twistedmatrix.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Twisted&lt;/a&gt; fa més anys dels que
voldria admetre, entenc immediatament que el dubte darrere la pregunta, no va
de &lt;code&gt;yield&lt;/code&gt; únicament, sinó de com funciona la &lt;strong&gt;programació asíncrona&lt;/strong&gt; en
&lt;a href="https://twistedmatrix.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Twisted&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Anem a fer-hi una ullada, amb l&amp;rsquo;explicació que a mi m&amp;rsquo;hagués agradat tenir
quan ho aprenia i de pas mirem els generadors!&lt;/p&gt;
&lt;h1 id="taula-de-continguts"&gt;Taula de continguts&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduccio"&gt;Introducció&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#taula-de-continguts"&gt;Taula de continguts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#helpyield"&gt;help(yield)?&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#que-es-help"&gt;Què és help?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#i-per-que-no-funciona-helpyield"&gt;I per què no funciona help(yield)?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#la-manera-correcta-helpyield"&gt;La manera correcta: help(&amp;ldquo;yield&amp;rdquo;)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#us-normal-de-yield-en-python"&gt;Ús normal de yield en Python&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#una-funcio-generadora-de-nombres-de-fibonacci"&gt;Una funció generadora de nombres de Fibonacci&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#per-que-cal-el-yield-amb-scrapytwisted"&gt;Per què cal el yield amb Scrapy/Twisted?&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#parentesi-de-programacio-asincrona"&gt;Parèntesi de programació asíncrona&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#programacio-asincrona-en-python-modern"&gt;Programació asíncrona en Python modern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#programacio-asincrona-amb-twisted"&gt;Programació asíncrona amb Twisted&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusio"&gt;Conclusió&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="helpyield"&gt;&lt;code&gt;help(yield)&lt;/code&gt;?&lt;/h1&gt;
&lt;p&gt;Comencem per respondre la pregunta concreta:
&amp;ldquo;com puc obtenir informació sobre &lt;code&gt;yield&lt;/code&gt; des de &lt;a href="https://python.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Python&lt;/a&gt;?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Doncs no anàvem malament amb &lt;code&gt;help(yield)&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;Aturem-nos però a pensar què passa quan fem alguna consulta que sí funciona,
com ara &lt;code&gt;help("")&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; help(&amp;quot;&amp;quot;)
Help on class str in module builtins:

class str(object)
[...]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="que-es-help"&gt;Què és &lt;code&gt;help&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;Des de la terminal interactiva podem esbrinar-ho:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;help&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;_sitebuiltins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_Helper&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;És a dir, que és una instància de la classe &lt;code&gt;_sitebuiltins._Helper&lt;/code&gt;,
un objecte!&lt;/p&gt;
&lt;p&gt;Podem fer-hi coses curioses?&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; a = help
&amp;gt;&amp;gt;&amp;gt; a(&amp;quot;&amp;quot;)
Help on class str in module builtins:

class str(object)
[...]
&amp;gt;&amp;gt;&amp;gt; dir(help)
[&amp;#39;__call__&amp;#39;, &amp;#39;__class__&amp;#39;, &amp;#39;__delattr__&amp;#39;, &amp;#39;__dict__&amp;#39;, &amp;#39;__dir__&amp;#39;, &amp;#39;__doc__&amp;#39;,
 &amp;#39;__eq__&amp;#39;, &amp;#39;__format__&amp;#39;, &amp;#39;__ge__&amp;#39;, &amp;#39;__getattribute__&amp;#39;, &amp;#39;__gt__&amp;#39;, &amp;#39;__hash__&amp;#39;,
 &amp;#39;__init__&amp;#39;, &amp;#39;__init_subclass__&amp;#39;, &amp;#39;__le__&amp;#39;, &amp;#39;__lt__&amp;#39;, &amp;#39;__module__&amp;#39;, &amp;#39;__ne__&amp;#39;,
 &amp;#39;__new__&amp;#39;, &amp;#39;__reduce__&amp;#39;, &amp;#39;__reduce_ex__&amp;#39;, &amp;#39;__repr__&amp;#39;, &amp;#39;__setattr__&amp;#39;,
 &amp;#39;__sizeof__&amp;#39;, &amp;#39;__str__&amp;#39;, &amp;#39;__subclasshook__&amp;#39;, &amp;#39;__weakref__&amp;#39;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Interessant! Veiem que implementa &lt;code&gt;__call__&lt;/code&gt;, és a dir, és un objecte
&lt;code&gt;callable&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;callable&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;obj&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;Return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;whether&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;callable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;.&lt;span class="nv"&gt;e&lt;/span&gt;.,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;some&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;function&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;.

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;Note&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;classes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;callable&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;instances&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;classes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;__call__&lt;/span&gt;&lt;span class="ss"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;method&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="i-per-que-no-funciona-helpyield"&gt;I per què no funciona &lt;code&gt;help(yield)&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;Ara que sabem que &lt;code&gt;help&lt;/code&gt; és un objecte, i que és un &lt;code&gt;callable&lt;/code&gt;, entenem que
&lt;code&gt;help(yield)&lt;/code&gt; no fa res més que cridar &lt;code&gt;help&lt;/code&gt; amb &lt;code&gt;yield&lt;/code&gt; com a argument&amp;hellip;&lt;/p&gt;
&lt;p&gt;Però &lt;code&gt;yield&lt;/code&gt; és una paraula reservada! D&amp;rsquo;aquí que tinguem un error de sintaxi.&lt;/p&gt;
&lt;h2 id="la-manera-correcta-helpyield"&gt;La manera correcta: &lt;code&gt;help("yield")&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Per evitar aquest error de sintaxi, podem posar la paraula &lt;code&gt;yield&lt;/code&gt; entre
cometes, d&amp;rsquo;aquesta manera és una sintaxi vàlida, i &lt;code&gt;help&lt;/code&gt; comprovarà si
correspon a un concepte conegut.&lt;/p&gt;
&lt;p&gt;Aquesta estratègia no només ens és útil aquí, si volem saber com funciona &lt;code&gt;|&lt;/code&gt;
o &lt;code&gt;+&lt;/code&gt; podem fer el mateix: &lt;code&gt;help("|")&lt;/code&gt; o &lt;code&gt;help("+")&lt;/code&gt;.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;help&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;yield&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;yield&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;statement&lt;/span&gt;
&lt;span class="o"&gt;*********************&lt;/span&gt;

&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;yield_stmt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;::=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;yield_expression&lt;/span&gt;

&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;yield&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;statement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;semantically&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;equivalent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;expression&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;statement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;can&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;used&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;omit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;parentheses&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;would&lt;/span&gt;
&lt;span class="nv"&gt;otherwise&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;required&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;equivalent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;expression&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;statement&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;For&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;example&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;statements&lt;/span&gt;

&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;expr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;expr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nv"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;equivalent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;expression&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;statements&lt;/span&gt;

&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;expr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;expr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;Yield&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;expressions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;statements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;used&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;when&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;defining&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nv"&gt;generator&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;used&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;generator&lt;/span&gt;
&lt;span class="nv"&gt;function&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;definition&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;sufficient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cause&lt;/span&gt;
&lt;span class="nv"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;definition&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;generator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;instead&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;normal&lt;/span&gt;
&lt;span class="nv"&gt;function&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="nv"&gt;For&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;full&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;details&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;yield&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;semantics&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;refer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Yield&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;expressions&lt;/span&gt;
&lt;span class="nv"&gt;section&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1 id="us-normal-de-yield-en-python"&gt;Ús normal de &lt;code&gt;yield&lt;/code&gt; en Python&lt;/h1&gt;
&lt;p&gt;El text de &lt;code&gt;help("yield")&lt;/code&gt; ens explica que es fa servir en funcions
generadores, però no hi entra molt en detall.
Tot i que podríem seguir amb la documentació amb &lt;code&gt;help&lt;/code&gt;, ja és útil canviar
a explicacions més casolanes!&lt;/p&gt;
&lt;h2 id="una-funcio-generadora-de-nombres-de-fibonacci"&gt;Una funció generadora de &lt;a href="https://ca.wikipedia.org/wiki/Successi%C3%B3_de_Fibonacci" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;nombres de Fibonacci&lt;/a&gt;&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Generador de nombres de Fibonacci&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;La faríem servir així:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;generator&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;13&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Si ens hi fixem, la funció &lt;code&gt;fib&lt;/code&gt; conté un bucle infinit (&lt;code&gt;while True&lt;/code&gt;)!
Però &lt;code&gt;a = fib()&lt;/code&gt; retorna de seguida!
No només això, &lt;code&gt;a&lt;/code&gt; acaba essent de tipus &lt;code&gt;generator&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;La màgia és que aquest &lt;code&gt;generator&lt;/code&gt; és iterable, en particular podem anar-hi
avançant amb &lt;code&gt;next&lt;/code&gt; per obtenir el següent nombre de Fibonacci.&lt;/p&gt;
&lt;p&gt;Només es generen exactament aquells nombres de Fibonacci que necessitem.&lt;/p&gt;
&lt;p&gt;Aquest concepte és força útil i en &lt;a href="https://python.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Python&lt;/a&gt; modern es fa servir
moltíssim.&lt;/p&gt;
&lt;p&gt;Hi ha maneres molt, i molt interessants de fer-los servir :-) però no era
el tema, el tema era per què cal &lt;code&gt;yield&lt;/code&gt; amb &lt;a href="https://docs.scrapy.org/en/latest/index.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Scrapy&lt;/a&gt;, i amb
&lt;a href="https://twistedmatrix.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Twisted&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id="per-que-cal-el-yield-amb-scrapytwisted"&gt;Per què cal el &lt;code&gt;yield&lt;/code&gt; amb &lt;a href="https://docs.scrapy.org/en/latest/index.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Scrapy&lt;/a&gt;/&lt;a href="https://twistedmatrix.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Twisted&lt;/a&gt;?&lt;/h1&gt;
&lt;p&gt;Doncs&amp;hellip; Perquè a &lt;a href="https://python.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Python&lt;/a&gt; no hi havia &lt;code&gt;async&lt;/code&gt; i &lt;code&gt;await&lt;/code&gt; fa 20 anys!&lt;/p&gt;
&lt;p&gt;La primera versió de &lt;a href="https://twistedmatrix.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Twisted&lt;/a&gt; va sortir al 2002. Aleshores el
concepte de programació asíncrona no era tan comú com ho és ara, i per
descomptat &lt;a href="https://python.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Python&lt;/a&gt; no ho suportava.&lt;/p&gt;
&lt;h2 id="parentesi-de-programacio-asincrona"&gt;Parèntesi de programació asíncrona&lt;/h2&gt;
&lt;p&gt;Ja que hem mencionat el concepte un parell de vegades, caldrà il·lustrar-ho
breument:&lt;/p&gt;
&lt;p&gt;Tradicionalment un programa s&amp;rsquo;executa de forma seqüencial, és a dir en l&amp;rsquo;ordre
en què estan escrites les instruccions.&lt;/p&gt;
&lt;p&gt;En el món real però, especialment en el món de les xarxes, fer-ho d&amp;rsquo;aquesta
manera és altament ineficient.&lt;/p&gt;
&lt;p&gt;Ja que parlem de &lt;a href="https://docs.scrapy.org/en/latest/index.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Scrapy&lt;/a&gt;, pensem en la tasca
&lt;em&gt;&amp;ldquo;descarregar la primera plana de 1000 pàgines web&amp;rdquo;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Si ho fem de manera seqüencial, trigarem la suma de tots els temps de
descàrrega, és a dir, si es triga &lt;code&gt;1 segon&lt;/code&gt; per cada pàgina, trigarem
&lt;code&gt;1000 segons&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Si ho fem de manera asíncrona, es facilita paral·lelitzar aquesta tasca, el
que fem és començar la descàrrega d&amp;rsquo;una pàgina web i rebre una
notificació / &amp;ldquo;callback&amp;rdquo; quan aquesta descàrrega finalitza.
Així, si podem fer 10 descàrregues paral·leles i triguem &lt;code&gt;1 segon&lt;/code&gt; per
descàrrega, la mateixa tasca trigarà &lt;code&gt;100 segons&lt;/code&gt; (100 blocs de &lt;code&gt;1 segon&lt;/code&gt;
amb 10 descàrregues per bloc).&lt;/p&gt;
&lt;h2 id="programacio-asincrona-en-python-modern"&gt;Programació asíncrona en &lt;a href="https://python.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Python&lt;/a&gt; modern&lt;/h2&gt;
&lt;p&gt;En &lt;a href="https://python.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Python&lt;/a&gt; modern es pot fer programació asíncrona sense necessitar
altres llibreries, aquesta implementació va estar &lt;a href="https://peps.python.org/pep-3156/#acknowledgments" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;molt influenciada&lt;/a&gt;
per les maneres de fer de &lt;a href="https://twistedmatrix.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Twisted&lt;/a&gt; i els 10 anys d&amp;rsquo;experiència
mantenint aquest tipus de codi en &lt;a href="https://python.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Python&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Molt per sobre, per definir una funció asíncrona:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;async&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Pausa l&amp;#39;execució fins que `g` retorni&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Segueix fent altres coses&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Podem fer servir &lt;code&gt;async def&lt;/code&gt; per marcar la funció &lt;code&gt;f&lt;/code&gt; com a asíncrona, i
en el cos d&amp;rsquo;aquesta funció podem fer servir &lt;code&gt;await&lt;/code&gt; per &amp;ldquo;esperar&amp;rdquo; a que altres
funcions asíncrones acabin, i així poder escriure codi que sembli seqüencial.&lt;/p&gt;
&lt;p&gt;En executar &lt;code&gt;f()&lt;/code&gt;, no obtenim directament el resultat, sinó que obtenim una
&lt;code&gt;coroutine&lt;/code&gt; (segons com una &lt;code&gt;Promise&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Aquests són els noms que més es fan servir avui dia per aquests conceptes, en
part perquè es van popularitzar amb &lt;a href="https://nodejs.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Node.js&lt;/a&gt; al 2009.&lt;/p&gt;
&lt;h2 id="programacio-asincrona-amb-twisted"&gt;Programació asíncrona amb &lt;a href="https://twistedmatrix.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Twisted&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;No és molt diferent! Però per aconseguir-ho va caldre abusar conceptes que ja
estaven disponibles en &lt;a href="https://python.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Python&lt;/a&gt; aleshores, com ara els generators!&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;

&lt;span class="nd"&gt;@defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inlineCallbacks&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Pausa l&amp;#39;execució fins que `g` retorni&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# Segueix fent altres coses&lt;/span&gt;
    &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returnValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Podem fer servir el decorador &lt;code&gt;defer.inlineCallbacks&lt;/code&gt; per
&amp;ldquo;marcar la funció &lt;code&gt;f&lt;/code&gt; com a asíncrona&amp;rdquo;, i
en el cos d&amp;rsquo;aquesta funció podem fer servir &lt;code&gt;yield&lt;/code&gt; per &amp;ldquo;esperar&amp;rdquo; a que altres
funcions asíncrones acabin, i així poder escriure codi que sembli seqüencial.&lt;/p&gt;
&lt;p&gt;En executar &lt;code&gt;f()&lt;/code&gt;, no obtenim directament el resultat, sinó que obtenim un
&lt;code&gt;twisted.internet.defer.Deferred&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Conceptualment molt semblant, però força diferent a la vista. Per què?&lt;/p&gt;
&lt;p&gt;Doncs en no tenir els conceptes dintre del llenguatge, desenvolupadors com
&lt;a href="https://en.wikipedia.org/wiki/Glyph_Lefkowitz" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Glyph Lefkowitz&lt;/a&gt; van tenir idees molt bones; si ens hi fixem, fer
servir &lt;code&gt;yield&lt;/code&gt; al cos de la funció, vol dir que &lt;code&gt;f&lt;/code&gt; retorna un &lt;code&gt;generator&lt;/code&gt; i no
pas un resultat.&lt;/p&gt;
&lt;p&gt;De fet, no tenim ben bé una manera de marcar &amp;ldquo;el que &lt;code&gt;f&lt;/code&gt; retorna&amp;rdquo;, perquè cada
operació que fem de manera asíncrona necessitarà un &lt;code&gt;yield&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;D&amp;rsquo;aquí que necessitem ambdós: &lt;code&gt;defer.inlineCallbacks&lt;/code&gt; i &lt;code&gt;defer.returnValue&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Amb el decorador &lt;a href="https://docs.twistedmatrix.com/en/stable/api/twisted.internet.defer.html#inlineCallbacks" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;&lt;code&gt;defer.inlineCallbacks&lt;/code&gt;&lt;/a&gt;, el que fem és
convertir aquesta funció, en quelcom que ens retorna un &lt;code&gt;Deferred&lt;/code&gt;, i amb
&lt;code&gt;defer.returnValue&lt;/code&gt; el que fem és especificar quin serà el valor de retorn
d&amp;rsquo;aquest &lt;code&gt;Deferred&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Els &lt;a href="https://docs.twistedmatrix.com/en/stable/api/twisted.internet.defer.Deferred.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;&lt;code&gt;Deferreds&lt;/code&gt;&lt;/a&gt;, com les &lt;code&gt;Promises&lt;/code&gt;, en el fons són molt
equivalents, són un objecte de &lt;a href="https://python.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Python&lt;/a&gt;, que ens representa un procés o càlcul
que encara no ha finalitzat.&lt;/p&gt;
&lt;p&gt;A aquests objectes els podem afegir manualment &lt;code&gt;callbacks&lt;/code&gt;, per tal d&amp;rsquo;encadenar
processos o podem fer servir &lt;code&gt;yield&lt;/code&gt; dintre de funcions marcades com a
asíncrones amb &lt;code&gt;defer.inlineCallbacks&lt;/code&gt; per tal d&amp;rsquo;esperar el seu resultat.&lt;/p&gt;
&lt;h1 id="conclusio"&gt;Conclusió&lt;/h1&gt;
&lt;p&gt;La sintaxi &amp;ldquo;nova&amp;rdquo; de &lt;a href="https://python.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Python&lt;/a&gt; per programació asíncrona és molt bonica!&lt;/p&gt;
&lt;p&gt;I &lt;a href="https://twistedmatrix.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Twisted&lt;/a&gt;, en ser una llibreria tan innovadora, encara no se&amp;rsquo;n
beneficia al 100%. En part és perquè el canvi de Python 2 a Python 3 va trigar
força, perquè la base de codi és molt extensa (hi ha &lt;em&gt;molts&lt;/em&gt; protocols
implementats), en part també perquè l&amp;rsquo;ecosistema &lt;code&gt;async&lt;/code&gt; a &lt;a href="https://python.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Python&lt;/a&gt; va
trigar en estabilitzar-se.&lt;/p&gt;
&lt;p&gt;La realitat és que a dia d&amp;rsquo;avui es pot escriure codi que faci servir
&lt;a href="https://twistedmatrix.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Twisted&lt;/a&gt; amb la sintaxi nova!
Tot i això, molt sovint és útil saber d&amp;rsquo;on venim :-) perquè la compatibilitat
no és perfecta, i a vegades ens trobem amb la necessitat d&amp;rsquo;escriure codi amb
la sintaxi antiga; a més a més, entenent això podrem entendre els tutorials i
el codi de les llibreries que necessitem, com &lt;a href="https://docs.scrapy.org/en/latest/index.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Scrapy&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Per cert, que quedi clar que &lt;a href="https://twistedmatrix.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Twisted&lt;/a&gt; encara és un projecte rellevant!
De fet, avui mateix s&amp;rsquo;ha anunciat la pre-release d&amp;rsquo;una nova versió:
&lt;a href="https://mail.python.org/archives/list/twisted@python.org/thread/LBYWFR4LL3WYG3YEHQBRHAWMHR7CTOK5/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;22.8.0&lt;/a&gt;.&lt;/p&gt;</content></entry><entry><title>Fixing web pitfalls with Greasemonkey</title><link href="https://evilham.eu/en/blog/2021-fixing-web-pitfalls-with-greasemonkey/" rel="alternate"></link><updated>2021-06-05T00:00:00Z</updated><author><name></name></author><id>urn:uuid:f0cdb7f4-a848-37b2-8b0e-d370bad1949e</id><content type="html">&lt;h1 id="introduccio"&gt;Introducció&lt;/h1&gt;
&lt;p&gt;A molts webs, particularment els de les &lt;a href="consadmin"&gt;administracions&lt;/a&gt; és evident
que les coses es dissenyen perquè siguin pràctiques per qui les desenvolupa
(teòricament més barat) i no pas per qui les utilitza (major utilitat).&lt;/p&gt;
&lt;p&gt;Una cosa que no es coneix gaire, és que molt sovint podem afegir les nostres
personalitzacions a sobre per arreglar aquestes pífies.&lt;/p&gt;
&lt;p&gt;Aquí veurem un exemple del portal de notes de la Generalitat de Catalunya,
es diu Esfer@.&lt;/p&gt;
&lt;h1 id="taula-de-continguts"&gt;Taula de continguts&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduccio"&gt;Introducció&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#taula-de-continguts"&gt;Taula de continguts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#les-pifies"&gt;Les pífies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#les-millores"&gt;Les millores&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#obtenint-o-creant-aquestes-millores"&gt;Obtenint (o creant) aquestes millores&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#primer-pas-firefox-amb-greasemonkey"&gt;Primer pas: Firefox amb Greasemonkey&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#segon-pas-millores-esfera"&gt;Segon pas: millores esfera&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#tercer-pas-libreoffice"&gt;Tercer pas: LibreOffice&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#altres-webs"&gt;Altres webs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusio"&gt;Conclusió&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="les-pifies"&gt;Les pífies&lt;/h1&gt;
&lt;p&gt;En la Educació Secundària Obligatòria és necessari posar un comentari per cada
alumne, veiem com és la interfície per fer-ho:&lt;/p&gt;
&lt;p&gt;&lt;img alt="esfera_pifia" src="/en/blog/2021-fixing-web-pitfalls-with-greasemonkey/esfera_pifia.png" /&gt;&lt;/p&gt;
&lt;p&gt;A l&amp;rsquo;esquerra hi ha un desplegable per seleccionar la nota de cada alumne
(representat en una fila) i a la dreta hi ha un botó, en color &lt;em&gt;vermell&lt;/em&gt; ens
informa que hi ha un comentari posat, en color &lt;em&gt;gris&lt;/em&gt; que no n&amp;rsquo;hi ha.&lt;/p&gt;
&lt;p&gt;En fer-hi click, s&amp;rsquo;obre un diàleg modal (que &amp;ldquo;ocupa&amp;rdquo; tota la pantalla) on podem
posar el comentari, hem de fer click de nou en &lt;strong&gt;desa&lt;/strong&gt; i anar pel següent
alumne.&lt;/p&gt;
&lt;p&gt;Sembla poc pràctic? Ho és. Addicionalment, la sessió d&amp;rsquo;usuari caduca després
de 30 minuts, així que: posa comentari personalitzat a tots els alumnes del
curs de la ESO en 30 minuts o tota la teva feina es perd.&lt;/p&gt;
&lt;p&gt;És un desastre, vaja.&lt;/p&gt;
&lt;h1 id="les-millores"&gt;Les millores&lt;/h1&gt;
&lt;p&gt;Les millores fan aquesta pinta tan senzilla:&lt;/p&gt;
&lt;p&gt;&lt;img alt="esfera_millores" src="/en/blog/2021-fixing-web-pitfalls-with-greasemonkey/esfera_millores.png" /&gt;&lt;/p&gt;
&lt;p&gt;Afegim 3 botons a l&amp;rsquo;apartat d&amp;rsquo;avaluacions per grup i matèria:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Explicació millores Esfer@&lt;/code&gt;: un enllaç a aquest document explicatiu&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Baixar CSV&lt;/code&gt;: genera i baixa un fitxer &lt;code&gt;notes.csv&lt;/code&gt; on podreu emplenar les
  notes i els comentaris amb tranquilitat.
  Aquest fitxer &lt;code&gt;CSV&lt;/code&gt; és quasi un full de càlcul, no cal explicar res més;
  heu de modificar/emplenar les columnes de &lt;code&gt;Nota&lt;/code&gt; i de &lt;code&gt;Comentari&lt;/code&gt;.
  Caldrà que feu servir &lt;a href="https://www.libreoffice.org/download/download/?lang=ca" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;LibreOffice&lt;/a&gt; (vegeu també més abaix el perquè).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Importar CSV&lt;/code&gt;: demana triar el fitxer &lt;code&gt;notes.csv&lt;/code&gt; modificat i aplica els
  canvis de nota i comentari, posteriorment refresca la pàgina perquè ho
  comproveu. Recordeu que la responsabilitat és vostra!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ha estat provat amb la ESO i el Batxillerat (per tant amb notes &amp;ldquo;qualitatives&amp;rdquo; i
quantitatives), i en notes de trimestre i finals.&lt;/p&gt;
&lt;h1 id="obtenint-o-creant-aquestes-millores"&gt;Obtenint (o creant) aquestes millores&lt;/h1&gt;
&lt;h2 id="primer-pas-firefox-amb-greasemonkey"&gt;Primer pas: &lt;a href="https://getfirefox.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Firefox&lt;/a&gt; amb &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Greasemonkey&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Fem servir &lt;a href="https://getfirefox.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Firefox&lt;/a&gt; i la extensió &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Greasemonkey&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;La extensió &lt;a href="https://www.greasespot.net" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Greasemonkey&lt;/a&gt;, permet afegir &amp;ldquo;scripts&amp;rdquo; (codi) personalitzat a
certs webs, d&amp;rsquo;aquesta manera podem interactuar amb aquests webs amb
Interfície d&amp;rsquo;Usuari / Experiència d&amp;rsquo;Usuari poc satisfactòria i&amp;hellip;
Bé, solucionar-nos la vida una mica.&lt;/p&gt;
&lt;h2 id="segon-pas-millores-esfera"&gt;Segon pas: &lt;a href="/en/blog/2021-fixing-web-pitfalls-with-greasemonkey/esfera.user.js"&gt;millores esfera&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Un cop instal·lat el &lt;a href="https://www.greasespot.net" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Greasemonkey&lt;/a&gt;, només cal que
&lt;a href="/en/blog/2021-fixing-web-pitfalls-with-greasemonkey/esfera.user.js"&gt;feu click aquí&lt;/a&gt;.
I ara, sempre que estigueu a la part d&amp;rsquo;avaluacions de l&amp;rsquo;Esfer@ (per grup i
matèria), després d&amp;rsquo;uns segons apareixeran aquests 3 botons que hem mencionat.&lt;/p&gt;
&lt;p&gt;En visitar qualsevol enllaç que acabi amb &lt;code&gt;.user.js&lt;/code&gt; ensenyarà un diàleg
preguntant si volem instal·lar aquest codi personalitzat.&lt;/p&gt;
&lt;p&gt;Això és una cosa que hem de fer amb cura, en aquest cas però, espero que us en
fieu (i si no, comproveu o demaneu a algú que comprovi el codi!).&lt;/p&gt;
&lt;h2 id="tercer-pas-libreoffice"&gt;Tercer pas: &lt;a href="https://www.libreoffice.org/download/download/?lang=ca" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;LibreOffice&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;És super important fer servir &lt;a href="https://www.libreoffice.org/download/download/?lang=ca" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;LibreOffice&lt;/a&gt; i no pas Excel o semblants.&lt;/p&gt;
&lt;p&gt;El motiu és que Excel no reconeix el format del fitxer fàcilment i tampoc el
manté de manera senzilla; en canvi amb &lt;a href="https://www.libreoffice.org/download/download/?lang=ca" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;LibreOffice&lt;/a&gt; només heu de confirmar
que fa bona pinta i fer click en &lt;code&gt;Ok&lt;/code&gt;. I sobre tot en desar canvis: &lt;code&gt;Desar en format CSV&lt;/code&gt; sempre.&lt;/p&gt;
&lt;h2 id="altres-webs"&gt;Altres webs&lt;/h2&gt;
&lt;p&gt;En altres webs podeu fer coses semblants, el web de &lt;a href="https://www.greasespot.net" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Greasemonkey&lt;/a&gt; te molta
informació (en anglès), us podeu administrar aquest tipus de millores com
vulgueu.&lt;/p&gt;
&lt;h1 id="conclusio"&gt;Conclusió&lt;/h1&gt;
&lt;p&gt;Tot te solució :-).
No sempre hem d&amp;rsquo;acceptar el que ens ofereixen, que hi ha molts interessos i no
sempre estan alineats amb els nostres.&lt;/p&gt;
&lt;p&gt;En aquest cas, amb 4 clicks us podeu estalviar molta frustració, errades i podeu
revisar que els comentaris (i per tant la vostra feina) estiguin ben fets.&lt;/p&gt;
&lt;p&gt;Recordeu també que &lt;strong&gt;és la vostra responsabilitat comprovar que tot estigui
bé&lt;/strong&gt;. Això us pot ajudar i donar comoditat, però al final la responsabilitat és
vostra!&lt;/p&gt;
&lt;p&gt;Si això us és útil, em podeu &lt;a href="/ca/quant-a/"&gt;convidar a un cafè o a dinar&lt;/a&gt; o també
em podeu &lt;a href="/ca/quant-a/"&gt;contractar&lt;/a&gt; en alguna de les moltes coses relacionades amb
ordinadors, xarxes, sistemes, etc. que faig ^^.&lt;/p&gt;</content></entry><entry><title>FreeBSD: IPv6 in a VNET jail</title><link href="https://evilham.eu/en/blog/2021-freebsd-ipv6-in-vnet-jails/" rel="alternate"></link><updated>2021-01-05T00:00:00Z</updated><author><name></name></author><id>urn:uuid:70264264-a090-3eac-93b7-a6532afbc691</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;Fellow human &lt;a href="https://bsd.network/@LeJax/105498957095356726" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;@LeJax@bsd.network&lt;/a&gt;
has been trying to setup IPv6-enabled VNET Jails in &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This, turns out, can be a bit frustrating, because all the tools are there, but
the tricks are not widely documented.&lt;/p&gt;
&lt;p&gt;In an attempt to fix that, I am going to document all the tricks and sources I
have found (and remember) that were necessary until I got a working and
reproducible setup.
Hopefully this will not only help LeJax, but also it will review my notes and
allow them (or someone else) to prepare a nice general-use write up on this
topic that might be useful to others.&lt;/p&gt;
&lt;p&gt;I use &lt;a href="https://cdi.st" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;cdist&lt;/a&gt; and &lt;a href="https://github.com/iocage/iocage/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;iocage&lt;/a&gt; extensively, but the general tricks
should be valid without any or either of those.&lt;/p&gt;
&lt;h1 id="table-of-contents"&gt;Table of Contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#general-notes"&gt;General notes&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#reminder-about-freebsd-jails"&gt;Reminder about FreeBSD jails&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#note-on-iocage"&gt;Note on iocage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#note-on-cdist"&gt;Note on cdist&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#networking-options-for-ipv6-vnet-jails"&gt;Networking options for IPv6 VNET jails&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#vnet-virtualised-network-stack"&gt;VNET: virtualised network stack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#option-a-jail-host-acts-as-a-router"&gt;Option A: jail host acts as a router&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#option-b-bridge-to-public-interface"&gt;Option B: bridge to public interface&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#option-c-no-slaac-static-routing"&gt;Option C: no SLAAC, static routing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#ipv6-accept_rtadv-and-auto_linklocal"&gt;IPv6: accept_rtadv and auto_linklocal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#firewall-passing-the-devices"&gt;Firewall &amp;ndash; Passing the devices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#using-my-cdist-types"&gt;Using my cdist types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="general-notes"&gt;General notes&lt;/h1&gt;
&lt;h2 id="reminder-about-freebsd-jails"&gt;Reminder about &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; jails&lt;/h2&gt;
&lt;p&gt;Jails on &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; are a very useful OS tool that is tightly
integrated with the rest of the Operating System, which means that, for the most
part, issues regarding IPv6 in jails are actually issues with
general networking and not specific to jails.&lt;/p&gt;
&lt;h2 id="note-on-iocage"&gt;Note on &lt;a href="https://github.com/iocage/iocage/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;iocage&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/iocage/iocage/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;iocage&lt;/a&gt; is a helper to create and manage jails, some like it, some
don&amp;rsquo;t.
It itself doesn&amp;rsquo;t do much magic, but has some ZFS integrations that I find
useful.
These notes should mostly apply to jails created with &lt;code&gt;jail(8)&lt;/code&gt; as well.&lt;/p&gt;
&lt;h2 id="note-on-cdist"&gt;Note on &lt;a href="https://cdi.st" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;cdist&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://cdi.st" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;cdist&lt;/a&gt; is a &amp;ldquo;usable configuration management system&amp;rdquo;.
It&amp;rsquo;s basically shell on steroids to manage servers.
If I cite a type, the &lt;code&gt;manifest&lt;/code&gt; and &lt;code&gt;gencode-remote&lt;/code&gt;
(optionally the &lt;code&gt;explorer/*&lt;/code&gt;) files should be reviewed.
These should contain enough comments to be understandable and to identify the
relevant bits.&lt;/p&gt;
&lt;h1 id="networking-options-for-ipv6-vnet-jails"&gt;Networking options for IPv6 VNET jails&lt;/h1&gt;
&lt;h2 id="vnet-virtualised-network-stack"&gt;VNET: virtualised network stack&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;From &lt;code&gt;vnet(9)&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;VNET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;technique&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;virtualize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt;
&lt;span class="n"&gt;basic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idea&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;global&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notably&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;variables&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;per&lt;/span&gt;
&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sysctls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eventhandlers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;access&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;them&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;correct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Each&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;virtual&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;attached&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;prison&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vnet0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;being&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;
&lt;span class="n"&gt;unrestricted&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/blockquote&gt;
&lt;p&gt;With &lt;code&gt;VNET(9)&lt;/code&gt;, our jails basically become a separated machine network-wise.
We can (and maybe want to) also run a firewall on the jail itself.&lt;/p&gt;
&lt;p&gt;It also means we have to take care of networking somehow; this is usually the
tricky bit.
For IPv4 that means: static addresses or DHCP, for IPv6 that might mean SLAAC.&lt;/p&gt;
&lt;p&gt;Because of IPv6 nature, the usual tricks for IPv4 of using NAT / port forwarding
are a subpar solution, so we shouldn&amp;rsquo;t necessarily just try to replicate that.&lt;/p&gt;
&lt;h2 id="option-a-jail-host-acts-as-a-router"&gt;Option A: jail host acts as a router&lt;/h2&gt;
&lt;p&gt;Depending on how that is happening, our host might need to act as a router
(the &lt;code&gt;sysctl&lt;/code&gt; nodes: &lt;code&gt;net.inet.ip.forwarding&lt;/code&gt; / &lt;code&gt;net.inet6.ip6.forwarding&lt;/code&gt;
set to &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;dhcpd(8)&lt;/code&gt; and &lt;code&gt;rtadvd(8)&lt;/code&gt; running, &amp;hellip;, see: &lt;a href="/en/blog/2019-FreeBSD-eXO-router/"&gt;FreeBSD router&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;This might be the case if, e.g. we assign a &lt;code&gt;/64&lt;/code&gt; to the jails in a given host.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vnet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bridge&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;

&lt;span class="n"&gt;Has&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="n"&gt;Receives&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;uses&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SLAAC&lt;/span&gt;
&lt;span class="n"&gt;routed&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;rtadv&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IPv6&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="n"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="n"&gt;connectivity&lt;/span&gt;
&lt;span class="n"&gt;Runs&lt;/span&gt;
&lt;span class="n"&gt;rtadvd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If we do this, some important things to take into account:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The jail host will need to forward packages
  (&lt;code&gt;ipv6_gateway_enable=YES&lt;/code&gt; in &lt;code&gt;/etc/rc.conf&lt;/code&gt;,
   which results in &lt;code&gt;sysctl net.inet6.ip6.forwarding=1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The jail host will have to run &lt;code&gt;rtadvd(8)&lt;/code&gt; in a way that reaches the jails&lt;/li&gt;
&lt;li&gt;Depending on the setup, &lt;code&gt;ipv6_cpe_wanif&lt;/code&gt; should be set in &lt;code&gt;/etc/rc.conf&lt;/code&gt; if
  the jail host needs to accept route advertisements on some interface.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In any case &lt;code&gt;rc.conf(5)&lt;/code&gt; has more information on these settings.&lt;/p&gt;
&lt;h2 id="option-b-bridge-to-public-interface"&gt;Option B: bridge to public interface&lt;/h2&gt;
&lt;p&gt;This might be easier to setup if it matches our security needs and we already
have a working router with SLAAC.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;                                   &lt;/span&gt;&lt;span class="err"&gt;·&lt;/span&gt;&lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;firewall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;passes&lt;/span&gt;
&lt;span class="w"&gt;                                  &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;interface&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;traffic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bridge&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;--&lt;/span&gt;&lt;span class="err"&gt;·&lt;/span&gt;&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;switch&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;like&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bridge&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;--&lt;/span&gt;&lt;span class="err"&gt;·&lt;/span&gt;
&lt;span class="w"&gt;                                  &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vnet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="n"&gt;uses&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SLAAC&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;
&lt;span class="n"&gt;Pre&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;existing&lt;/span&gt;&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="err"&gt;·&lt;/span&gt;&lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="n"&gt;gain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IPv6&lt;/span&gt;
&lt;span class="n"&gt;Does&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SLAAC&lt;/span&gt;&lt;span class="w"&gt;                                             &lt;/span&gt;&lt;span class="n"&gt;Connectivity&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="option-c-no-slaac-static-routing"&gt;Option C: no SLAAC, static routing&lt;/h2&gt;
&lt;p&gt;This gets a tad complicated to manage and I haven&amp;rsquo;t tested it.&lt;/p&gt;
&lt;p&gt;The network schemes might look a lot like Option A and Option B depending on how
we actually do it, but it doesn&amp;rsquo;t have to.&lt;/p&gt;
&lt;p&gt;The main difference being that instead of SLAAC + ND + DAD, we are potentially
routing things somehow else.&lt;/p&gt;
&lt;h2 id="ipv6-accept_rtadv-and-auto_linklocal"&gt;IPv6: &lt;code&gt;accept_rtadv&lt;/code&gt; and &lt;code&gt;auto_linklocal&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;This is actually the key point to get everything to work.&lt;/p&gt;
&lt;p&gt;There is a bug in the latest &lt;a href="https://github.com/iocage/iocage/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;iocage&lt;/a&gt; release (1.2 in January 2021),
in which newly created jails are not properly prepared for IPv6.
This is fixed in the development branch
(see &lt;a href="https://github.com/iocage/iocage/commit/5f1574e4a738108ad2bd1b6454bec2e9a14f4c6c" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;commit&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;For regular jails, and before &lt;a href="https://github.com/iocage/iocage/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;iocage&lt;/a&gt; has release beyond 1.2, we have
to manually ensure that the interface is properly setup.&lt;/p&gt;
&lt;p&gt;In any case, we do need linklocal addresses, in order for
Neighbour Discovery (ND) to work.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Inside&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Jail&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Partial&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;contents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conf&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;an&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IPv4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DHCP&lt;/span&gt;
&lt;span class="nx"&gt;ifconfig_epair0b&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;SYNCDHCP&amp;quot;&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Ensure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;we&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;accept&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;advertisements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;linklocal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;
&lt;span class="nx"&gt;ifconfig_emailr0b_ipv6&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;inet6 accept_rtadv auto_linklocal&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1 id="firewall-passing-the-devices"&gt;Firewall &amp;ndash; Passing the devices&lt;/h1&gt;
&lt;p&gt;Nowadays it should be safe to use, e.g. &lt;code&gt;pf(4)&lt;/code&gt; on VNET jails
(TODO: citation needed, it&amp;rsquo;s somewhere on the freebsd-net/freebsd-questions ML).&lt;/p&gt;
&lt;p&gt;We basically setup the firewall as usual, except that, by default,
the &lt;code&gt;devfs.rules(5)&lt;/code&gt; do not expose the necessary packet filter devices.&lt;/p&gt;
&lt;p&gt;Something similar might apply for &lt;code&gt;ipfw(4)&lt;/code&gt;.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;In&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Jail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Host&lt;/span&gt;:
#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Partial&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;contents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;devfs&lt;/span&gt;.&lt;span class="nv"&gt;rules&lt;/span&gt;
[&lt;span class="nv"&gt;devfsrules_iocage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;]
&lt;span class="nv"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;$de&lt;/span&gt;&lt;span class="nv"&gt;vfsrules_jail&lt;/span&gt;
&lt;span class="nv"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;pf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;unhide&lt;/span&gt;
&lt;span class="nv"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;pflog&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;unhide&lt;/span&gt;
&lt;span class="nv"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;pfsynv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;unhide&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Changing this file requires a &lt;code&gt;service devfs restart&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And we have to make sure that our jails use this set of &lt;code&gt;devfs&lt;/code&gt; rules.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;iocage&lt;span class="w"&gt; &lt;/span&gt;set&lt;span class="w"&gt; &lt;/span&gt;devfs_ruleset=5&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;JAIL&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In any case, care should be taken not to block too much (e.g. blocking all
&lt;code&gt;icmp6&lt;/code&gt; will utterly break IPv6 connectivity).&lt;/p&gt;
&lt;h1 id="using-my-cdist-types"&gt;Using my &lt;a href="https://cdi.st" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;cdist&lt;/a&gt; types&lt;/h1&gt;
&lt;p&gt;I wrote a couple &lt;a href="https://git.sr.ht/~evilham/cdist-evilham/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;cdist types&lt;/a&gt; that take care of all this in a
reliable fashion.
You can find them and the source for other useful &lt;a href="https://cdi.st" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;cdist&lt;/a&gt; types
on &lt;a href="https://git.sr.ht/~evilham/cdist-evilham/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;my cdist repository&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://git.sr.ht/~evilham/cdist-evilham/tree/main/item/type/__evilham_iocage" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;__evilham_iocage&lt;/a&gt;:
  Setup the jail host with iocage to be ready for IPv6-enabled jails.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.sr.ht/~evilham/cdist-evilham/tree/main/item/type/__evilham_iocage_jail" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;__evilham_iocage_jail&lt;/a&gt;:
  Create and maintain an IPv6-enabled jail.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And the &lt;code&gt;manifest&lt;/code&gt; for the jail host looks like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# Setup the jail host
__evilham_iocage --zpool jails
# Create/update the necessary jails
require=&amp;quot;__evilham_iocage&amp;quot; __evilham_iocage_jail \
    &amp;quot;jail.example.org&amp;quot; \
    --bridge &amp;quot;bridge0&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then I can setup DNS and &lt;code&gt;ssh -6 jail.example.org&lt;/code&gt;, or use a &lt;a href="https://cdi.st" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;cdist&lt;/a&gt;
manifest to set up the service.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Hopefully this contains enough pointers to help others figure out IPv6-enabled
jails, and to document this in a more generic fashion for everyone else.&lt;/p&gt;
&lt;p&gt;If this was useful for you, do &lt;a href="/en/about/"&gt;let me know&lt;/a&gt;.&lt;/p&gt;</content></entry><entry><title>Rainer Maria Rilke's letter to Friedrich Westhoff</title><link href="https://evilham.eu/en/blog/2020-rilke-letter-westhoff/" rel="alternate"></link><updated>2020-12-12T00:00:00Z</updated><author><name></name></author><id>urn:uuid:cae3bdd1-a43c-3cbc-8a49-fabf0f27767f</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;Putting this here because of its overall quality, significance  and
for easy future access.&lt;/p&gt;
&lt;h1 id="the-letter"&gt;The letter&lt;/h1&gt;
&lt;h2 id="envelope"&gt;Envelope&lt;/h2&gt;
&lt;p&gt;von: Rainer Maria Rilke&lt;/p&gt;
&lt;p&gt;an: Friedrich Westhoff&lt;/p&gt;
&lt;p&gt;Rom, am 29. April 1904&lt;/p&gt;
&lt;h2 id="body"&gt;Body&lt;/h2&gt;
&lt;p&gt;Mein lieber Friedrich,&lt;/p&gt;
&lt;p&gt;wir haben durch Mutter in dieser Zeit öfters von dir gehört, und, ohne Genaueres von dir zu wissen, fühlen wir doch, dass du eine schwere Zeit hast. Mutter wird dir nicht helfen können, denn im Grunde kann keiner im Leben dem anderen helfen; das erfährt man immer wieder in jedem Konflikt und jeder Verwirrung: dass man allein ist.&lt;/p&gt;
&lt;p&gt;Das ist nicht so schlimm, wie es auf den ersten Blick scheinen mag; es ist auch wieder das Beste im Leben, dass jeder alles in sich selbst hat: sein Schicksal, seine Zukunft, seine ganze Weite und Welt. Nun gibt es freilich Momente, wo es schwer ist, in sich zu sein und innerhalb des eigenen Ichs auszuhalten; es geschieht, dass man gerade in den Augenblicken, da man fester und – fast müsste man sagen – eigensinniger denn je an sich festhalten sollte, sich an etwas Äußeres anschließt, während wichtiger Ereignisse den eigenen Mittelpunkt aus sich heraus in Fremdes, in einen anderen Menschen verlegt. Das ist gegen die allereinfachsten Gesetze des Gleichgewichts, und es kann nur Schweres dabei herauskommen.&lt;/p&gt;
&lt;p&gt;Clara und ich, lieber Friedrich, wir haben uns gerade darin gefunden und verstanden, dass alle Gemeinsamkeit nur im Erstarken zweier benachbarter Einsamkeiten bestehen kann, dass aber alles, was man Hingabe zu nennen pflegt, seinem Wesen nach der Gemeinsamkeit schädlich ist: Denn wenn ein Mensch sich verlässt, so ist er nichts mehr, und wenn zwei Menschen beide sich selbst aufgeben, um zueinander zu treten, so ist kein Boden mehr unter ihnen und ihr Beisammensein ist ein fortwährendes Fallen. – Wir haben, mein lieber Friedrich, nicht ohne große Schmerzen, solches erfahren, haben erfahren, was jeder, der ein eigenes Leben will, so oder so zu wissen bekommt.&lt;/p&gt;
&lt;p&gt;Ich werde einmal, wenn ich reifer und älter bin, vielleicht dazu kommen, ein Buch zu schreiben, ein Buch für junge Menschen; nicht etwa, weil ich glaube, etwas besser gekonnt zu haben als andere. Im Gegenteil, weil mir alles so viel schwerer geworden ist als anderen jungen Menschen von Kindheit an während meiner ganzen Jugend.&lt;/p&gt;
&lt;p&gt;Da habe ich immer und immer wieder erfahren, dass es kaum etwas Schwereres gibt, als sich lieb haben. Dass das Arbeit ist, Tagelohn, Friedrich, Tagelohn; weiß Gott, es gibt kein anderes Wort dafür. Sieh, und nun kommt noch dazu, dass die jungen Menschen auf so schweres Lieben nicht vorbereitet werden; denn die Konvention hat diese komplizierteste und äußerste Beziehung zu etwas Leichtem und Leichtsinnigem zu machen versucht, ihr den Schein gegeben, als könnten sie alle. Dem ist nicht so. Liebe ist etwas Schweres, und sie ist schwerer denn anderes, weil bei anderen Konflikten die Natur selbst den Menschern anhält, sich zu sammeln, sich ganz fest mit aller Kraft zusammenzufassen, während in der Steigerung der Liebe der Anreiz liegt, sich ganz fortzugeben. Aber denke doch nur, kann das etwas Schönes sein, sich fortzugeben nicht als Ganzes und Geordnetes, sondern so dem Zufall nach, Stück für Stück, wie es sich trifft? Kann solche Fortgabe, die einem Fortwerfen und Zerreißen so ähnlich sieht, etwas Gutes, kann sie Glück, Freude, Fortschritt sein? Nein, sie kann es nicht … Wenn du jemandem Blumen schenkst, so ordnest du sie vorher, nicht wahr? Aber junge Menschen, die sich lieb haben werfen sich einander hin in der Ungeduld und Hast ihrer Leidenschaft, und sie merken gar nicht, welcher Mangel an gegenseitiger Schätzung in dieser unaufgeräumten Hingabe liegt, merken es erst mit Staunen und Unwillen an dem Zerwürfnis, das aus aller dieser Unordnung zwischen ihnen entsteht. Und ist erst Uneinheit unter ihnen, dann wächst die Wirrnis mit jedem Tage; keiner von den beiden hat mehr etwas Unzerschlagenes, Reines und Unverdorbenes um sich, und mitten in der Trostlosigkeit eines Abbruchs suchen sie den Schein ihres Glückes [denn um des Glückes willen sollte all das doch sein] festzuhalten. Ach, sie vermögen sich kaum mehr zu entsinnen, was sie mit Glück meinten. In seiner Unsicherheit wird jeder immer ungerechter gegen den anderen; die einander wohltun wollten, berühren einer den anderen nun auf herrische und unduldsame Art, und im Bestreben, aus dem unhaltbaren und unerträglichen Zustand ihrer Wirrnis irgendwie herauszukommen, begehen sie den größten Fehler, der an menschlichen Beziehungen geschehen kann: sie werden ungeduldig. Sie drängen sich zu einem Abschluss, zu einer, wie sie glauben, endgültigen Entscheidung zu kommen, sie versuchen ihr Verhältnis, dessen überraschende Veränderungen sie erschreckt haben, ein für allemal festzustellen, damit es von nun ab „ewig“ [wie sie sagen] dasselbe bleibe. Das ist nur der letzte Irrtum in dieser langen Kette von aneinander festhaltenden Irrungen. Totes nicht einmal lässt sich endgültig festhalten [denn es zerfällt und verändert sich in seiner Art] wie viel weniger lässt sich Lebendes und Lebendiges ein für alle Mal abschließend behandeln. Leben ist ja gerade Sichverwandeln, und menschliche Beziehungen, die ein Lebensextrakt sind, sind das Veränderlichste von allem, steigen und fallen von Minute zu Minute, und Liebende sind diejenigen, in deren Beziehung und Berührung kein Augenblick dem anderen gleicht. Menschen, zwischen denen nie etwas Gewohntes, etwas schon einmal Dagewesenes vor sich geht, sondern lauter Neues, Unerwartetes, Unerhörtes. Es gibt solche Verhältnisse, die ein sehr großes, fast unerträgliches Glück sein müssen, aber sie können nur zwischen sehr reichen Menschen eintreten und zwischen solchen, die jeder für sich, reich, geordnet und versammelt sind, nur zwei weite, tiefe, eigene Welten können sie verbinden. – Junge Menschen – das liegt auf der Hand – können ein solches Verhältnis nicht gewinnen, aber sie können, wenn sie ihr Leben recht begreifen, langsam zu solchem Glück anwachsen und sich vorbereiten dafür. Sie müssen, wenn sie lieben, nicht vergessen, dass sie Anfänger sind, Stümper des Lebens, Lehrlinge in der Liebe, – müssen Liebe lernen, und dazu gehört [wie zu jedem Lernen] Ruhe, Geduld und Sammlung!&lt;/p&gt;
&lt;p&gt;Liebe ernst nehmen und leiden und wie eine Arbeit lernen, das ist es, Friedrich, was jungen Menschen nottut. – Die Leute haben, wie so vieles andere, auch die Stellung der Liebe im Leben missverstanden, sie haben sie zu Spiel und Vergnügen gemacht, weil sie meinten, dass Spiel und Vergnügen seliger denn Arbeit sei; es gibt aber nichts Glücklicheres als die Arbeit, und Liebe, gerade weil sie das äußerste Glück ist, kann nichts anderes als Arbeit sein. – Wer also liebt, der muss versuchen, sich zu benehmen, als ob er eine große Arbeit hätte: Er muss viel allein sein und in sich gehen und sich zusammenfassen und sich festhalten; er muss arbeiten; er muss etwas werden!&lt;/p&gt;
&lt;p&gt;Denn, Friedrich, glaube mir, je mehr man ist, je reicher ist alles, was man erlebt. Und wer in seinem Leben eine tiefe Liebe haben will, der muss sparen und sammeln dafür und Honig zusammentragen.&lt;/p&gt;
&lt;p&gt;Man muss nie verzweifeln, wenn einem etwas verloren geht, ein Mensch oder eine Freude oder ein Glück; es kommt alles noch herrlicher wieder. Was abfallen muss, fällt ab; was zu uns gehört, bleibt uns, denn es geht alles nach Gesetzen vor sich, die größer als unsere Einsicht sind und mit denen wir nur scheinbar im Widerspruch stehen. Man muss in sich selber leben und an das ganze Leben denken, an alle seine Millionen Möglichkeiten, Weiten und Zukünfte, denen gegenüber es nichts Vergangenes und Verlorenes gibt. –&lt;/p&gt;
&lt;p&gt;Wir denken so viel an dich, lieber Friedrich; unsere Überzeugung ist die: Dass du in der Wirrnis der Ereignisse längst, aus dir heraus, den eigenen einsamen Ausweg gefunden hättest, der allein helfen kann, wenn nicht die ganze Last des Militärjahres noch auf dir läge … Ich erinnere mich, dass nach meiner eingesperrten Militärschulzeit mein Freiheitsdrang und mein entstelltes Selbstgefühl [das sich erst allmählich von Bügen und Beulen, die man ihm beigebracht hatte, erholen musste] mich zu Verirrungen und Wünschen, die gar nicht zu meinem Leben gehören, treiben wollte, und es war mein Glück, dass meine Arbeit da war: In ihr fand ich mich und finde mich täglich in ihr und suche mich nirgends anders mehr. So tun wir beide; so ist Claras und mein Leben. Und du wirst auch dazu kommen, ganz gewiss. Sei guten Mutes, alles ist vor dir, und die Zeit, die mit Schwerem hingeht, ist nie verloren. Wir grüßen dich, lieber Friedrich, von Herzen:&lt;/p&gt;
&lt;p&gt;Rainer und Clara&lt;/p&gt;</content></entry><entry><title>Terminal-based presentations</title><link href="https://evilham.eu/en/blog/2020-terminal-presentations/" rel="alternate"></link><updated>2020-10-31T00:00:00Z</updated><author><name></name></author><id>urn:uuid:60ea9709-ef2f-3a06-9594-3453f9c635cf</id><content type="html">&lt;h1 id="introduccion"&gt;Introducción&lt;/h1&gt;
&lt;p&gt;En tiempos de Pandemia, los encuentros sociales son principalmente digitales,
esto incluye también encuentros técnicos, en el contexto de
&amp;ldquo;cosas de ordenadores&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;En los últimos meses hemos hecho talleres regulares de &lt;a href="https://cdi.st" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;cdist&lt;/a&gt;,
y también algún curso de IPv6 con &lt;a href="https://maadix.net" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;MaadiX&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Como la mayoría de contenido es texto, las herramientas habituales para
compartir pantalla no son ideales.
Esto se debe a que estas herramientas no transmiten el texto, sino que
transmiten vídeo; es decir, imágenes comprimidas (simplificando un poco).&lt;/p&gt;
&lt;p&gt;Al enviar vídeo, tenemos varios problemas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cada persona espectadora no puede configurar la fuente a sus
  necesidades / preferencias&lt;/li&gt;
&lt;li&gt;Se requiere un ancho de banda bastante superior&lt;/li&gt;
&lt;li&gt;Si la calidad de la conexión no es perfecta, esto puede resultar en que
  la herramienta usada baje la calidad y el texto sea ilegible&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Así que, como siempre, hacemos las cosas diferente.
A petición &amp;ldquo;popular&amp;rdquo;, vamos a ver cómo.&lt;/p&gt;
&lt;h1 id="tabla-de-contenido"&gt;Tabla de contenido&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduccion"&gt;Introducción&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#tabla-de-contenido"&gt;Tabla de contenido&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#una-terminal"&gt;Una terminal&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#varias-terminales-en-una-tmux"&gt;Varias terminales (en una): tmux&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#compartiendo-la-terminal-gotty"&gt;Compartiendo la terminal: GoTTY&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#integracion-con-tmux"&gt;Integración con tmux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#configurando-gotty"&gt;Configurando GoTTY&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#usandolo-con-otras-personas-nginx-como-reverse-proxy-tls"&gt;Usándolo con otras personas: Nginx como reverse proxy + TLS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#diapositivas-en-la-terminal-patat"&gt;&amp;ldquo;Diapositivas&amp;rdquo; en la terminal: patat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#otras-cosas-editando-texto"&gt;Otras cosas: editando texto, &amp;hellip;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusión&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="una-terminal"&gt;Una terminal&lt;/h1&gt;
&lt;p&gt;Lo hacemos todo desde una terminal. La mayoría de personas involucradas están
cómodas con la línea de comandos; y las que no, están cómodas leyendo
documentación, así que es una elección bastante correcta.&lt;/p&gt;
&lt;h2 id="varias-terminales-en-una-tmux"&gt;Varias terminales (en una): &lt;a href="http://tmux.github.io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;tmux&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Una terminal única puede ser algo limitante, así que usamos
&lt;a href="http://tmux.github.io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;tmux&lt;/a&gt;. Un &amp;ldquo;multiplexor&amp;rdquo; de terminal.&lt;/p&gt;
&lt;p&gt;Para la persona que lo está usando, es como si hubiera una única terminal,
pero realmente, con &lt;code&gt;Ctrl+b+c&lt;/code&gt; podemos &lt;strong&gt;c&lt;/strong&gt;ear una nueva terminal y
con &lt;code&gt;Ctrl+b+n&lt;/code&gt; y &lt;code&gt;Ctrl+b+p&lt;/code&gt; respectivamente podemos movernos a la terminal
siguiente (&lt;strong&gt;n&lt;/strong&gt;ext) o la anterior (&lt;strong&gt;p&lt;/strong&gt;revious).&lt;/p&gt;
&lt;p&gt;Para instalar &lt;a href="http://tmux.github.io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;tmux&lt;/a&gt; podemos usar habitualmente los paquetes del
sistema. En sistemas como &lt;a href="https://OpenBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;OpenBSD&lt;/a&gt;, no hace falta porque
está incluido en el sistema base :-).&lt;/p&gt;
&lt;h1 id="compartiendo-la-terminal-gotty"&gt;Compartiendo la terminal: &lt;a href="https://github.com/yudai/gotty/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;GoTTY&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Esta es la parte interesante, después de evaluar varias opciones,
&lt;a href="https://github.com/yudai/gotty/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;GoTTY&lt;/a&gt; es la que mejor configuración por defecto tenía y la que
más control permitía a la persona usuaria.&lt;/p&gt;
&lt;p&gt;Para instalar &lt;a href="https://github.com/yudai/gotty/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;GoTTY&lt;/a&gt; en &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; podemos usar
&lt;code&gt;pkg install gotty&lt;/code&gt;; en otros sistemas este paquete puede no estar
disponible, pero hay versiones pre-compiladas en su repositorio.
Ante la duda, compilar binarios de &lt;a href="golang.org"&gt;Go&lt;/a&gt; es sencillo :-).
(Y hay instrucciones en la web de &lt;a href="https://github.com/yudai/gotty/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;GoTTY&lt;/a&gt;)&lt;/p&gt;
&lt;h2 id="integracion-con-tmux"&gt;Integración con &lt;a href="http://tmux.github.io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;tmux&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Este es el contenido de mi configuración de &lt;a href="http://tmux.github.io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;tmux&lt;/a&gt;, en
&lt;code&gt;~/.tmux.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Activar y desactivar barra con: Ctrl+F3&lt;/span&gt;
&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;F3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;
&lt;span class="c1"&gt;# Compartir la sessión con GoTTY con: Ctrl+F2&lt;/span&gt;
&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;F2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ttyshare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;gotty tmux attach -r -t &lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;$(tmux display -p &amp;#39;#S&amp;#39;)&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Si nos fijamos, con esta configuración añado dos atajos de teclado:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Ctrl+F3&lt;/code&gt;: para activar y desactivar la barra de estado de &lt;a href="http://tmux.github.io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;tmux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl+F2&lt;/code&gt;: para empezar a compartir la sesión de &lt;a href="http://tmux.github.io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;tmux&lt;/a&gt; con &lt;a href="https://github.com/yudai/gotty/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;GoTTY&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Analicemos un poco más la línea:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bind-key -n C-F2 new-window -n ttyshare&lt;/code&gt;:
Cuando se presione &lt;code&gt;Ctrl+F2&lt;/code&gt; Crear una nueva &amp;ldquo;ventana&amp;rdquo; de &lt;a href="http://tmux.github.io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;tmux&lt;/a&gt;
llamada &lt;code&gt;ttyshare&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Y en esa nueva &amp;ldquo;ventana&amp;rdquo; ejecutaremos el comando:
&lt;code&gt;gotty tmux attach -r -t $(tmux display -p '#S')&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Esto significa que &lt;a href="https://github.com/yudai/gotty/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;GoTTY&lt;/a&gt; empezará a ejecutarse, y para cada cliente
que se conecte, se ejecutará: &lt;code&gt;tmux attach -r -t $(tmux display -p '#S')&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Excepto que&amp;hellip; &lt;a href="http://tmux.github.io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;tmux&lt;/a&gt; reemplaza &lt;code&gt;'#S'&lt;/code&gt; con el nombre de la sesión actual,
es decir, donde presionamos &lt;code&gt;Ctrl+F2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;De esta forma, para cada persona que visite la dirección de &lt;a href="https://github.com/yudai/gotty/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;GoTTY&lt;/a&gt;,
empezaremos un &lt;a href="http://tmux.github.io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;tmux&lt;/a&gt; al que le decimos:
&amp;ldquo;conéctate a la sesión donde presionamos &lt;code&gt;Ctrl+F2&lt;/code&gt; de forma sólo lectura&amp;rdquo;.
Para obtener el efecto sólo lectura, usamos &lt;code&gt;-r&lt;/code&gt; como argumento de
&lt;code&gt;tmux attach&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Vale la pena mencionar que: por defecto &lt;a href="https://github.com/yudai/gotty/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;GoTTY&lt;/a&gt; también crea sesiones
de sólo lectura, al hacerlo aquí también, conseguimos seguridad por capas:
para comprometer nuestra sesión, haría falta que haya un problema en
&lt;a href="https://github.com/yudai/gotty/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;GoTTY&lt;/a&gt; y también que haya un problema en &lt;a href="http://tmux.github.io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;tmux&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Así, al presionar &lt;code&gt;Ctrl+F2&lt;/code&gt;, se crea la ventana &lt;code&gt;ttyshare&lt;/code&gt; y vemos lo siguiente:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;2020&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;11&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;08&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;23&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;43&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;Load&lt;/span&gt;&lt;span class="n"&gt;ing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;${&lt;/span&gt;&lt;span class="n"&gt;HOME&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;go&lt;/span&gt;&lt;span class="n"&gt;tty&lt;/span&gt;
&lt;span class="mf"&gt;2020&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;11&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;08&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;23&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;43&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;starting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmux&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;attach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;
&lt;span class="mf"&gt;2020&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;11&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;08&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;23&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;43&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;8080&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;17&lt;/span&gt;&lt;span class="n"&gt;lmim3q&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Es decir, que dese el navegador, puedo abrir la dirección
&lt;code&gt;http://[::1]:8080/17lmim3q/&lt;/code&gt;
y ver, en vivo, mi sesión de &lt;a href="http://tmux.github.io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;tmux&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="configurando-gotty"&gt;Configurando &lt;a href="https://github.com/yudai/gotty/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;GoTTY&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A este programa no le hace falta mucha configuración porque los valores por
defecto son bastante razonables, pero hay alguna cosa que sí prefiero cambiar.&lt;/p&gt;
&lt;p&gt;A partir de la &lt;a href="https://github.com/yudai/gotty/blob/master/.gotty" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;configuración completa&lt;/a&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;addresses&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;when&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Limitar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;conexiones&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;la&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;máquina&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;local&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;via&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;IPv6&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;::1&amp;quot;&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Enable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;generation&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;De&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;esta&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;forma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tenemos&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;una&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;capa&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;de&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;seguridad&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;más&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;al&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;requerir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;compartir&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;un&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;enlace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;las&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;personas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;que&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;queremos&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;compartir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;la&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;enable_random_url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;window&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Aquí&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;podemos&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;personalizar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;el&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;título&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;de&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;la&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pestaña&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;en&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;el&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;navegador&lt;/span&gt;
&lt;span class="n"&gt;title_format&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;Hola!&amp;quot;&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Timeout&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;secodns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;waiting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;disable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Por&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;defecto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;si&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;no&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;se&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;conecta&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;un&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cliente&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;en&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;segundos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GoTTY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;se&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cierra&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;esto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;es&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;poco&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;práctico&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;si&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;configuramos&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;esperamos&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;las&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;otras&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;personas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Al&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ponerlo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;desactivamos&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;esta&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;desconexión&lt;/span&gt;
&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="usandolo-con-otras-personas-nginx-como-reverse-proxy-tls"&gt;Usándolo con otras personas: Nginx como reverse proxy + TLS&lt;/h2&gt;
&lt;p&gt;Si nos fijamos, configuro &lt;a href="https://github.com/yudai/gotty/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;GoTTY&lt;/a&gt; para escuchar únicamente en
la máquina local via IPv6.&lt;/p&gt;
&lt;p&gt;Justamente esto no es lo que queremos, si no que queremos que otras personas
puedan acceder a nuestra terminal.
Si no usamos cifrado en el transporte (TLS), nuestra comunicación es como una
postal, cualquiera puede leerla.&lt;/p&gt;
&lt;p&gt;Este trabajo lo hace &lt;a href="https://nginx.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;nginx&lt;/a&gt; mucho mejor que lo podría
hacer &lt;a href="https://github.com/yudai/gotty/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;GoTTY&lt;/a&gt;, además ya tenemos una serie de automatizaciones para
obtener certificados con &lt;a href="https://letsencrypt.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Let&amp;rsquo;s Encrypt&lt;/a&gt;, así que
es ideal usarlo para recibir las llamadas del mundo exterior y pasarlas a
&lt;a href="https://github.com/yudai/gotty/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;GoTTY&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Esto es bastante estándard, excepto que tenemos que tener en cuenta que
&lt;a href="https://github.com/yudai/gotty/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;GoTTY&lt;/a&gt; usa Web Sockets para actualizar las terminales de los
clientes en &amp;ldquo;tiempo real&amp;rdquo;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;sh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;^/&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;)?&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Upgrade&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nx"&gt;http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Connection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Upgrade&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Host&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Real&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;IP&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nx"&gt;remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Forwarded&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;For&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nx"&gt;proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Forwarded&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Proto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Forwarded&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Port&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;proxy_pass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//MyComputerHost;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;sh&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Host&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Real&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;IP&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nx"&gt;remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Forwarded&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;For&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nx"&gt;proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Forwarded&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Proto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Forwarded&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Port&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;proxy_pass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//MyComputerHost;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;De esta forma, puedo enviar enlaces del tipo:
&lt;code&gt;https://evilham.com/sh/17lmim3q/&lt;/code&gt;
y las personas que lo reciban podrán seguir lo que sea que tenga en la
terminal.&lt;/p&gt;
&lt;h1 id="diapositivas-en-la-terminal-patat"&gt;&amp;ldquo;Diapositivas&amp;rdquo; en la terminal: &lt;a href="https://github.com/jaspervdj/patat" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;patat&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;También hay muchas alternativas de programas o utilidades para hacer
presentaciones en la terminal, pero todas tenían algún tipo de inconveniente
lo suficientemente grande como para no ser prácticas.&lt;/p&gt;
&lt;p&gt;Todas excepto &lt;a href="https://github.com/jaspervdj/patat" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;patat&lt;/a&gt;, que consigue hacerlo todo bien por defecto.&lt;/p&gt;
&lt;p&gt;Sólo hace falta escribir Markdown, y ejecutar &lt;code&gt;patat presentacion.md&lt;/code&gt; y ya
estamos presentando con nuestras fantásticas diapositivas.&lt;/p&gt;
&lt;p&gt;Para instalar esta herramienta en Linux, podemos usar algunos paquetes,
o descargar el binario precompilado desde &lt;a href="https://github.com/jaspervdj/patat" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;su repositorio&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Es posible que me anime a crear un &lt;em&gt;port&lt;/em&gt; para &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; pronto ^^.&lt;/p&gt;
&lt;h1 id="otras-cosas-editando-texto"&gt;Otras cosas: editando texto, &amp;hellip;&lt;/h1&gt;
&lt;p&gt;Al interactuar con nuestros humanos seguro
que salen dudas, o queremos comprobar un manual, o queremos mirar un fichero,
o queremos entrar en una máquina remota, &amp;hellip;&lt;/p&gt;
&lt;p&gt;¿Lo mejor de estas presentaciones?
Al hacerlo todo en una única sesión de &lt;a href="http://tmux.github.io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;tmux&lt;/a&gt;, podemos rápidamente
presionar &lt;code&gt;Ctrl+b+c&lt;/code&gt;, usar las herramientas habituales
(&lt;code&gt;man&lt;/code&gt;, &lt;code&gt;vim&lt;/code&gt;, &lt;code&gt;ssh&lt;/code&gt;, &amp;hellip;) y volver a la presentación;
y nuestros humanos sabrán de qué hablamos.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusión&lt;/h1&gt;
&lt;p&gt;Estas herramientas son bastante sencillas, y una vez las hemos configurado,
le dan a las otras personas plena flexibilidad a la hora de configurar el
tamaño de letra y demás, y a nosotros también a la hora de salirnos un poco
de lo que habíamos preparado.&lt;/p&gt;
&lt;p&gt;No es para todas las presentaciones, pero para muchas&amp;hellip; Seguro que es más
eficiente y agradable ^^.&lt;/p&gt;</content></entry><entry><title>FreeBSD: Installing</title><link href="https://evilham.eu/en/blog/2020-freebsd-installing/" rel="alternate"></link><updated>2020-08-18T00:00:00Z</updated><author><name></name></author><id>urn:uuid:cd0e51fb-2ebd-3936-a122-6740925eb279</id><content type="html">&lt;h1 id="introduccio"&gt;Introducció&lt;/h1&gt;
&lt;p&gt;He hagut de fer servir les meves &lt;a href="../2020-words-of-summer/" title="Paraules d'estiu"&gt;paraules d&amp;rsquo;estiu&lt;/a&gt; per qüestions
personals i professionals; així que la continuació d&amp;rsquo;aquell fil d&amp;rsquo;idees ha
trigat una mica en arribar.&lt;/p&gt;
&lt;p&gt;Per coses de la vida, aquests dies ha arribat un &lt;em&gt;mini-server&lt;/em&gt; a les meves mans.
Així que agafarem un petit desviament en coses &amp;ldquo;tècniques&amp;rdquo; abans de tornar al
per què, que és segurament la única cosa que importa al final.&lt;/p&gt;
&lt;p&gt;Aquí agafarem aquest &lt;em&gt;mini-server&lt;/em&gt; i hi instal·larem &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; com a
sistema operatiu.&lt;/p&gt;
&lt;p&gt;Per què és interessant? Doncs aquesta màquina tindrà certs serveis digitals
que sempre està bé tenir sota el nostre control.&lt;/p&gt;
&lt;p&gt;I el procés, des de zero, no és una cosa que es documenti habitualment en
aquesta llengua ;-).&lt;/p&gt;
&lt;h1 id="taula-de-continguts"&gt;Taula de continguts&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduccio"&gt;Introducció&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#taula-de-continguts"&gt;Taula de continguts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#que-es-freebsd"&gt;Què és FreeBSD?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#per-que-freebsd"&gt;Per què FreeBSD?&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#diferencies-de-desenvolupament-amb-linux"&gt;Diferències de desenvolupament amb Linux&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#installant-freebsd"&gt;Instal·lant FreeBSD&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#versions-linies-de-suport"&gt;Versions / línies de suport&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#current-futur-130"&gt;CURRENT &amp;ndash; Futur 13.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#release-actualment-121"&gt;RELEASE &amp;ndash; Actualment 12.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#stable-actualment-114-i-113"&gt;STABLE &amp;ndash; Actualment 11.4 [i 11.3]&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#muntant-un-usb-dinstallacio"&gt;Muntant un USB d&amp;rsquo;instal·lació&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#installant"&gt;Instal·lant!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#assegurant-el-sistema-una-mica"&gt;Assegurant el sistema una mica&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusio"&gt;Conclusió&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="que-es-freebsd"&gt;Què és &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;?&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; és un &amp;ldquo;&lt;a href="https://ca.wikipedia.org/wiki/Sistema_operatiu" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Sistema Operatiu&lt;/a&gt;
&lt;a href="https://ca.wikipedia.org/wiki/Llic%C3%A8ncia_BSD" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="Algú hauria de traduir la llicència a la Viquipèdia"&gt;obert i gratuït&lt;/a&gt;, semblant o compatible amb &lt;a href="https://ca.wikipedia.org/wiki/Unix-like" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="Unix-like"&gt;Unix&lt;/a&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Això, pels que som resultat dels mitjans i l&amp;rsquo;educació digital que ens forma
per ser consumidors passius, potser implica directament hores i hores de
lectura.&lt;/p&gt;
&lt;p&gt;Anem a fer-ho una mica més fàcil:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://ca.wikipedia.org/wiki/Sistema_operatiu" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Sistema operatiu&lt;/a&gt;:&lt;/strong&gt;
  sense això, un ordinador és un aparell més aviat inútil.
  No ens permetria engegar cap programa, llegir cap correu, no hi podríem fer
  res.
  El sistema operatiu és necessari, perquè és el que &amp;ldquo;sap parlar&amp;rdquo; amb els
  components físics que conformen l&amp;rsquo;ordinador.
  Tot i ser necessari, no sempre és evident; en general, el que volem com a
  usuaris és fer servir els programes que necessitem.
  Però en algun moment veurem que tenir la possibilitat de modificar el sistema
  a la nostra voluntat, també te els seus avantatges.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://ca.wikipedia.org/wiki/Llic%C3%A8ncia_BSD" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="Algú hauria de traduir la llicència a la Viquipèdia"&gt;Obert i gratuït&lt;/a&gt;:&lt;/strong&gt;
  aquest és un gran tema, i el món està dividit en interpretacions.
  Bàsicament, obert, vol dir que tenim accés al codi font del sistema, és a dir
  als fitxers de text que ha escrit algun humà, molt sovint de manera altruista,
  que després un ordinador processa per convertir-ho en instruccions que pugui
  executar.
  I gratuït, vol dir que no cal pagar res per fer-ho servir.
  El món es divideix en interpretacions per la &lt;em&gt;manera&lt;/em&gt; en que el programari
  pot ser obert i gratuït.
  Pels que tinguin curiositat, internet està plena de diverses perspectives
  en el tema de les llicències, per exemple &lt;a href="https://ca.wikipedia.org/wiki/Llic%C3%A8ncia_BSD" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="Algú hauria de traduir la llicència a la Viquipèdia"&gt;BSD&lt;/a&gt; (permissives)
  o &lt;a href="https://ca.wikipedia.org/wiki/GNU_General_Public_License" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="Llicència Pública General GNU"&gt;GPL&lt;/a&gt; (&lt;em&gt;&lt;a href="https://ca.wikipedia.org/wiki/Copyleft" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;copyleft&lt;/a&gt;&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://ca.wikipedia.org/wiki/Unix-like" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="Unix-like"&gt;Semblant a Unix&lt;/a&gt;:&lt;/strong&gt;
  Unix és un sistema operatiu que es va crear vora els anys 1970, amb unes idees
  molt clares que han marcat el desenvolupament del món dels ordinadors.
  Aquell sistema, era comercial i requeria unes llicències, però es distribuïa
  amb el codi font, cosa que va fer que es fes molt popular a les universitats.
  En particular, a Berkeley
  (d&amp;rsquo;aquí la B de BSD &amp;ndash; la distribució de programari de Berkeley).
  Per qüestions de llicència i propietat intel·lectual, el codi de BSD ja no
  està basat en el codi de Unix, però sí que manté les idees bàsiques i un
  alt nivell de compatibilitat (una cosa que es diu
  &lt;a href="https://ca.wikipedia.org/wiki/POSIX" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;compatibilitat POSIX&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="per-que-freebsd"&gt;Per què &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;?&lt;/h1&gt;
&lt;p&gt;Perquè funciona, funciona bé, i és senzill d&amp;rsquo;administrar.&lt;/p&gt;
&lt;p&gt;Això no vol dir que altres sistemes no funcionin bé o siguin difícils
d&amp;rsquo;administrar; fins a un cert punt, el tema va &amp;ldquo;a gustos&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Bàsicament, en aquest punt de la meva vida professional, me&amp;rsquo;n surto força bé amb
el sistema que em posin davant; ara, també tinc clar que &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;
(i &lt;a href="https://OpenBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;OpenBSD&lt;/a&gt; en alguns casos) és el que em permet fer més, més ràpid,
i sense preocupar-me gaire del com perquè la càrrega mental associada és mínima.&lt;/p&gt;
&lt;p&gt;Naturalment hi ha altres alternatives que compleixin amb &amp;ldquo;obert i gratuït&amp;rdquo;,
per exemple &lt;a href="https://ca.wikipedia.org/wiki/Linux" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Linux&lt;/a&gt;; però hi ha &lt;a href="http://techrights.org/2020/08/02/red-hat-layoffs/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;tendències preocupants&lt;/a&gt;
al món de Linux actualment.&lt;/p&gt;
&lt;p&gt;Un altre factor per no fer servir Linux és que la experiència ja m&amp;rsquo;ha portat a
&amp;ldquo;aprendre Linux&amp;rdquo; tres vegades a la meva vida (sense comptar haver fet servir
diverses distribucions, que fins a cert punt són cadascuna un món).
És a dir, canvia massa ràpid i en general sense prou bons motius; o bé
perquè els canvis es fan massa ràpid i s&amp;rsquo;arriba a solucions poc ideals o bé
perquè els problemes que s&amp;rsquo;hi estan solucionant, són els problemes de quatre
empreses, i no els problemes dels usuaris.&lt;/p&gt;
&lt;h2 id="diferencies-de-desenvolupament-amb-linux"&gt;Diferències de desenvolupament amb Linux&lt;/h2&gt;
&lt;p&gt;Els sistemes BSD es desenvolupen en conjunt, és a dir que quan obtenim el codi font
o un binari, realment estem obtenint &lt;em&gt;tot&lt;/em&gt; el que ens cal per compilar o instal·lar
un sistema operatiu sencer amb: &lt;em&gt;kernel&lt;/em&gt; (o &lt;a href="https://ca.wikipedia.org/wiki/Nucli_del_sistema_operatiu" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;nucli&lt;/a&gt;, més o menys la part
que parla amb allò físic), i utilitats d&amp;rsquo;usuari (o &lt;em&gt;world&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;Sense les utilitats d&amp;rsquo;usuari, no tindríem manera d&amp;rsquo;interactuar amb el kernel, i per
tant amb l&amp;rsquo;ordinador.&lt;/p&gt;
&lt;p&gt;I, sense el kernel, els programes no poden parlar amb la part física, i per tant són
capaços ni d&amp;rsquo;ensenyar-nos alguna cosa per pantalla (és una aproximació més o menys
intuïtiva i no 100% acurada).&lt;/p&gt;
&lt;p&gt;Això sembla una diferència menor, però realment és un gran avantatge; en tenir control
de les utilitats d&amp;rsquo;usuari &lt;strong&gt;i també&lt;/strong&gt; del kernel, hi ha també una co-responsabilitat,
és a dir, si es vol introduir un canvi en el kernel, es fa necessari fer en paral·lel
els canvis adients a les utilitats d&amp;rsquo;usuari.&lt;/p&gt;
&lt;p&gt;I també si es fan canvis en una utilitat d&amp;rsquo;usuari, cal assegurar-se que les altres
utilitats que fan part del sistema operatiu, mantinguin una compatibilitat i
consistència general.&lt;/p&gt;
&lt;p&gt;Això, al món de Linux no és possible, perquè les utilitats d&amp;rsquo;usuari solen ser &amp;ldquo;GNU&amp;rdquo;
(o busybox) i Linux és únicament el kernel.
D&amp;rsquo;aquesta manera, la responsabilitat pels canvis es mou d&amp;rsquo;una banda a l&amp;rsquo;altra, i,
molt sovint, els canvis no tenen lloc, i s&amp;rsquo;acaben generant noves eines per salvaguardar
les limitacions de les eines existents.&lt;/p&gt;
&lt;p&gt;Per exemple, &lt;code&gt;ifconfig&lt;/code&gt;, la comanda tradicional per administrar interfícies de xarxa,
a Linux es considera arcaica, perquè ara existeix &lt;code&gt;ip&lt;/code&gt;.
I no és que &lt;code&gt;ip&lt;/code&gt; sigui dolent, és que és un canvi, que només és necessari, perquè
els problemes inherents al sistema de desenvolupament
&lt;a href="https://blog.farhan.codes/2018/06/25/linux-maintains-bugs-the-real-reason-ifconfig-on-linux-is-deprecated/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;no són adreçables&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;En canvi, als BSD, &lt;code&gt;ifconfig&lt;/code&gt; és molt més potent del que mai ho va ser a Linux i
també te molt més sentit; aquest desenvolupament conjunt del kernel i les utilitats
d&amp;rsquo;usuari hi te molt a veure.&lt;/p&gt;
&lt;h1 id="installant-freebsd"&gt;Instal·lant &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Com sempre, el primer pas és descarregar l&amp;rsquo;instal·lador, això en en aquest cas
es fa al web de &lt;a href="https://www.freebsd.org/releases/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ens trobem amb la primera decisió: quina versió triem?
Depèn de l&amp;rsquo;ús de cada, així que anem a entendre com funcionen.&lt;/p&gt;
&lt;h2 id="versions-linies-de-suport"&gt;Versions / línies de suport&lt;/h2&gt;
&lt;p&gt;Hi ha 3 línies del sistema operatiu &lt;a href="https://lists.freebsd.org/pipermail/freebsd-announce/2015-February/001624.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;suportades&lt;/a&gt;.
Per mantenir-se al dia, recomanaria subscriure&amp;rsquo;s a la
&lt;a href="https://lists.freebsd.org/mailman/listinfo/freebsd-announce" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;llista d&amp;rsquo;anuncis de FreeBSD&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Com a regla general, per un sistema que vulguem que funcioni sense problemes, recomanaria
fer servir sempre l&amp;rsquo;&lt;a href="https://www.freebsd.org/security/security.html#sup" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;última versió suportada&lt;/a&gt; de &lt;strong&gt;RELEASE&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="current-futur-130"&gt;&lt;strong&gt;CURRENT&lt;/strong&gt; &amp;ndash; Futur 13.0&lt;/h3&gt;
&lt;p&gt;Aquesta és la línia de desenvolupament, literalment és on vénen a parar tots els
canvis, tant al kernel com a les utilitats d&amp;rsquo;usuari.
Per aquest motiu, no es recomana per usuaris novells, sinó per aquells que vulguin
ajudar el desenvolupament, encara que sigui només provant i trobant problemes.
Havent dit això, la línia de desenvolupament sol ser molt estable, hi ha empreses
com Netflix que actualitzen un parell de vegades al mes a la línia de desenvolupament
i jo ho faig gairebé a diari al meu sistema principal.&lt;/p&gt;
&lt;p&gt;Al món de BSD hi ha una dita, i és que si no vols que els bugs/problemes t&amp;rsquo;arribin, els has de trobar tu abans.
Com més persones ajudin a provar la línia de desenvolupament, més segurs podem estar que les altres línies de suport no tenen grans problemes.&lt;/p&gt;
&lt;p&gt;Quan ja ha passat un temps (mínim dos anys) i les persones que desenvolupen el sistema troben que és el moment,
aquesta línia es &amp;ldquo;congela&amp;rdquo; i després d&amp;rsquo;una feina de solucionar problemes diversos, acabar d&amp;rsquo;actualitzar documentació i demés, s&amp;rsquo;acaba convertint en una versió &lt;strong&gt;RELEASE&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="release-actualment-121"&gt;&lt;strong&gt;RELEASE&lt;/strong&gt; &amp;ndash; Actualment 12.1&lt;/h3&gt;
&lt;p&gt;Aquesta és l&amp;rsquo;última versió major (en aquest cas 12, amb 1 essent la versió menor).
Des del moment en que es va publicar la versió 12.0, aquesta línia de desenvolupament te garantit el
suport (actualitzacions de seguretat, disponibilitat de binaris oficials, &amp;hellip;) un &lt;strong&gt;mínim de 5 anys&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;És a dir, que FreeBSD 12 te suport, com a mínim, fins el 30 de Juny de 2024.&lt;/p&gt;
&lt;p&gt;Ara, quan es publica una versió &lt;em&gt;menor&lt;/em&gt;, com ara la 12.2 que vindrà aviat, la versió menor anterior (l&amp;rsquo;actual 12.1) deixa de ser suportada 3 mesos després.&lt;/p&gt;
&lt;p&gt;És a dir, que tenim un període de 3 mesos per fer una actualització menor; és una cosa que cal tenir en compte.
Normalment s&amp;rsquo;avisa amb mesos d&amp;rsquo;antelació.&lt;/p&gt;
&lt;h3 id="stable-actualment-114-i-113"&gt;&lt;strong&gt;STABLE&lt;/strong&gt; &amp;ndash; Actualment 11.4 [i 11.3]&lt;/h3&gt;
&lt;p&gt;Les versions &lt;strong&gt;STABLE&lt;/strong&gt; són antigues &lt;strong&gt;RELEASE&lt;/strong&gt; que encara estan suportades, és a dir, des que es va publicar FreeBSD 11.0, s&amp;rsquo;ha publicat la 12.0.&lt;/p&gt;
&lt;p&gt;Però com que la versió FreeBSD 11 te suport garantit de 5 anys, seguirà existint, com a mínim, fins el 10 d&amp;rsquo;octubre de 2021.&lt;/p&gt;
&lt;p&gt;Ara, veiem que a data d&amp;rsquo;avui hi ha &lt;em&gt;dues&lt;/em&gt; versions de FreeBSD 11, això vol dir que fa menys de 3 mesos que es va publicar la versió 11.4 i, per tant,
la versió 11.3 encara està suportada.&lt;/p&gt;
&lt;p&gt;En efecte, FreeBSD 11.4 es va publicar el 16 de juny de 2020, i per tant FreeBSD 11.3 deixa de tenir suport el 30 de setembre de 2020.&lt;/p&gt;
&lt;h2 id="muntant-un-usb-dinstallacio"&gt;Muntant un USB d&amp;rsquo;instal·lació&lt;/h2&gt;
&lt;p&gt;A data d&amp;rsquo;avui, amb el criteri que he explicat anteriorment, ens decidiríem per FreeBSD 12.1, així que
podem descarregar &lt;a href="https://download.freebsd.org/ftp/releases/ISO-IMAGES/12.1/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;la imatge d&amp;rsquo;instal·lació&lt;/a&gt; (també hi ha imatges per &lt;a href="https://download.freebsd.org/ftp/releases/VM-IMAGES/12.1-RELEASE/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;màquines virtuals pre-instal·lades&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;En general, voldrem descarregar el fitxer &lt;code&gt;-disc1.iso&lt;/code&gt; de la nostra arquitectura de hardware, si no sabem el que és, segurament sigui &lt;code&gt;amd64&lt;/code&gt;.
És a dir que volem el fitxer &lt;code&gt;FreeBSD-12.1-RELEASE-amd64-disc1.iso&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pels paranòics entre nosaltres, els anuncis d&amp;rsquo;una nova versió també es &lt;a href="https://www.FreeBSD.org/releases/12.1R/announce.asc" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;publiquen signats&lt;/a&gt;.
I el llistat de claus PGP del projecte es troba al famòs &lt;a href="https://www.freebsd.org/doc/handbook/pgpkeys.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Handbook de FreeBSD&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Comprovant la signatura, i després la integritat del fitxer, podem estar segurs que el fitxer que hem descarregat,
és el fitxer que les persones que desenvolupen el sistema operatiu han generat.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Un cop l&amp;rsquo;hem descarregat, hi ha utilitats que ens ajuden a gravar aquesta imatge en un CD o, molt millor, en un USB.&lt;/p&gt;
&lt;p&gt;Si heu descarregat la imatge sota Windows, &lt;a href="https://rufus.ie" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;rufus&lt;/a&gt; és un programa molt bo per fer-ho.&lt;/p&gt;
&lt;p&gt;Si, en canvi, ho heu fet en Linux o en un Mac, el més senzill és fer-ho amb la comanda &lt;code&gt;dd&lt;/code&gt;
(si algú te un mètode més &amp;ldquo;gràfic&amp;rdquo;, que m&amp;rsquo;avisi ;-)).
En particular:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;FreeBSD&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;.&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;RELEASE&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;amd64&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;disc1&lt;/span&gt;.&lt;span class="nv"&gt;iso&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="nv"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;EL_MEU_USB&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nv"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;conv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;sync&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Cal que us assegureu de reemplaçar &lt;code&gt;EL_MEU_USB&lt;/code&gt; amb el nom del vostre USB,
recordeu que si us equivoqueu molt, &lt;strong&gt;això pot voler dir perdre les vostres dades&lt;/strong&gt;!&lt;/p&gt;
&lt;h2 id="installant"&gt;Instal·lant!&lt;/h2&gt;
&lt;p&gt;Havent escrit la imatge al meu USB, engego el micro-server iniciant des de l&amp;rsquo;USB.&lt;/p&gt;
&lt;p&gt;I ara ve la part &amp;ldquo;fàcil&amp;rdquo;, on un menú ens anirà guiant per instal·lar &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;;
la majoria de les decisions es poden canviar un cop instal·lat el sistema, així que no és molt crític,
i els valors per defecte són raonables en general.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Sense tocar res veig una pantalla de benvinguda que em dóna a triar entre &lt;code&gt;Install&lt;/code&gt;, &lt;code&gt;Shell&lt;/code&gt; i &lt;code&gt;LiveCD&lt;/code&gt;; volem &lt;code&gt;Install&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Configuro el meu teclat&lt;/li&gt;
&lt;li&gt;Li dono un nom a la màquina&lt;/li&gt;
&lt;li&gt;Desactivo &lt;code&gt;kernel-dbg&lt;/code&gt; i &lt;code&gt;lib32&lt;/code&gt; que no em fan falta (tampoc fa mal deixar-los)&lt;/li&gt;
&lt;li&gt;Trio instal·lació automàtica amb ZFS (&lt;code&gt;Auto (ZFS)&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Vaig a &lt;code&gt;Pool Type/Disks&lt;/code&gt; i trio &lt;code&gt;stripe&lt;/code&gt; en el disc dur que vull&lt;/li&gt;
&lt;li&gt;Configuro &lt;code&gt;Encrypt Swap&lt;/code&gt; com &lt;code&gt;YES&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Vaig a &lt;code&gt;Install: Proceed with installation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Confirmo els canvis al disc dur (si m&amp;rsquo;he equivocat, podria perdre dades!)&lt;/li&gt;
&lt;li&gt;Espero aproximadament 2 minuts mentre el sistema s&amp;rsquo;instal·la&lt;/li&gt;
&lt;li&gt;Configuro una contrasenya pel super usuari (no hi ha feedback visual, però està agafant els vostres caracters. Després desactivarem la contrasenya)&lt;/li&gt;
&lt;li&gt;Trio la interfície de xarxa principal (en tinc dues)&lt;/li&gt;
&lt;li&gt;Confirmo que vull IPv4 amb DHCP (és el més habitual)&lt;/li&gt;
&lt;li&gt;Confirmo que vull IPv6 amb SLAAC (&lt;em&gt;hauria&lt;/em&gt; de ser més habitual)&lt;/li&gt;
&lt;li&gt;Confirmo la configuració que hem obtingut de la xarxa&lt;/li&gt;
&lt;li&gt;La hora interna de la meva màquina és UTC (així no hi ha problemes de canvis horaris)&lt;/li&gt;
&lt;li&gt;Configuro la meva zona horària&lt;/li&gt;
&lt;li&gt;Confirmo la data i l&amp;rsquo;hora&lt;/li&gt;
&lt;li&gt;Com a serveis activo &lt;code&gt;sshd&lt;/code&gt;, &lt;code&gt;ntpd&lt;/code&gt; i &lt;code&gt;powerd&lt;/code&gt;, desactivo &lt;code&gt;dumpdev&lt;/code&gt; (es pot canviar a posteriori)&lt;/li&gt;
&lt;li&gt;Activo totes les opcions de seguretat a &lt;code&gt;System Hardening&lt;/code&gt; (es pot canviar a posteriori)&lt;/li&gt;
&lt;li&gt;No afegeixo usuaris, ho faig a posteriori&lt;/li&gt;
&lt;li&gt;Trio &lt;code&gt;Exit&lt;/code&gt; per indicar que ja hem instal·lat el sistema&lt;/li&gt;
&lt;li&gt;Trio que vull una consola (&lt;code&gt;shell&lt;/code&gt;) al nou sistema abans de reiniciar&lt;/li&gt;
&lt;li&gt;Edito &lt;code&gt;/etc/sshd/sshd.conf&lt;/code&gt; amb &lt;code&gt;vi /etc/sshd/sshd.conf&lt;/code&gt; per habilitar les sessions remotes de super usuari canviant &lt;code&gt;#PermitRootLogin no&lt;/code&gt; per &lt;code&gt;PermitRootLogin yes&lt;/code&gt; (ho canviarem tot seguit, no és una configuració gens segura)&lt;/li&gt;
&lt;li&gt;Tanco aquesta consola amb &lt;code&gt;exit&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Trio &lt;code&gt;Reboot&lt;/code&gt; i trec l&amp;rsquo;USB d&amp;rsquo;instal·lació&lt;/li&gt;
&lt;li&gt;El sistema inicia i ja el puc acabar de configurar remotament&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;És a dir, que esperem un total de 2 minuts i contestem preguntes majoritàriament senzilles;
el procès triga en total uns 5 minuts si ja coneixem les preguntes i uns 20 minuts si no.&lt;/p&gt;
&lt;h2 id="assegurant-el-sistema-una-mica"&gt;Assegurant el sistema una mica&lt;/h2&gt;
&lt;p&gt;Compte!
Durant la instal·lació hem près una decició temporal que cal desfer tan aviat com sigui possible:
hem de deshabilitar l&amp;rsquo;inici de sessió remot amb contrasenya.&lt;/p&gt;
&lt;p&gt;Si no ho fem, com que hem deixat accés de super usuari remot amb contrasenya, potencialment,
algú a la nostra xarxa podria intentar esbrinar la contrasenya i fer-se amb el control de l&amp;rsquo;equip.&lt;/p&gt;
&lt;p&gt;El motiu d&amp;rsquo;aquesta decisió és, que jo faig servir autenticació amb claus públiques, però configurar
aquesta autenticació només amb un teclat és una mica farragós.&lt;/p&gt;
&lt;p&gt;Per això inicio sessió al &lt;em&gt;micro-server&lt;/em&gt; amb usuari &lt;code&gt;root&lt;/code&gt; i la contrasenya que he configurat abans.
Executo:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# ifconfig
bge0: flags=8843&amp;lt;UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST&amp;gt; metric 0 mtu 1500
    options=c019b&amp;lt;RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,TSO4,VLAN_HWTSO,LINKSTATE&amp;gt;
    ether XX:XX:XX:XX:XX:XX
    inet6 fe80::9640:XXXX:XXXX:XXXX%bge0 prefixlen 64 scopeid 0x1
    inet6 2a0f:de00:X:X:X:X:X:X prefixlen 64 autoconf
    inet 192.168.1.12 netmask 0xfffff000 broadcast 192.168.1.255
    media: Ethernet autoselect (1000baseT &amp;lt;full-duplex&amp;gt;)
    status: active
    nd6 options=23&amp;lt;PERFORMNUD,ACCEPT_RTADV,AUTO_LINKLOCAL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Que em diu que la IP local de la màquina és &lt;code&gt;192.168.1.12&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I llavors des del meu ordinador:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="mf"&gt;@192.168.1.12&lt;/span&gt;
&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;:~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chmod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;
&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;:~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rsa&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AAAAB3NzaC1yc2EAAA&lt;/span&gt;&lt;span class="p"&gt;[..]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;evilham&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pgp_auth&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;authorized_keys&lt;/span&gt;
&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;:~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chmod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;authorized_keys&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;És a dir, configuro la meva clau pública per iniciar sessió (els detalls&amp;hellip; són tema d&amp;rsquo;un altre dia).&lt;/p&gt;
&lt;p&gt;I ara ja puc desactivar l&amp;rsquo;inici de sessió amb contrasenya amb &lt;code&gt;vi /etc/ssh/sshd_config&lt;/code&gt;
i canviant &lt;code&gt;PermitRootLogin yes&lt;/code&gt; per &lt;code&gt;PermitRootLogin without-password&lt;/code&gt;
i reiniciant el servei amb &lt;code&gt;service sshd restart&lt;/code&gt;.&lt;/p&gt;
&lt;h1 id="conclusio"&gt;Conclusió&lt;/h1&gt;
&lt;p&gt;Fins aquí avui, ara tenim un sistema &lt;a href="https://FreeBSD.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; instal·lat de nou,
que servirà com a base per fer-hi &amp;ldquo;qualsevol cosa&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Abans d&amp;rsquo;això caldrà configurar el sistema d&amp;rsquo;una manera una manera més segura,
i d&amp;rsquo;una manera automatizada, per si mai cal reinstal·lar-ho.&lt;/p&gt;
&lt;p&gt;Totes aquestes coses les configuro automàticament amb &lt;a href="https://cdi.st" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;cdist&lt;/a&gt;,
que és un tema prou gran per per un altre dia.&lt;/p&gt;
&lt;p&gt;PS: 2826 paraules han estat escrites pels &lt;a href="https://ca.wikipedia.org/wiki/Teorema_dels_micos_infinits" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="És una bona aproximació de com funciona el meu cervell"&gt;micos infinits&lt;/a&gt;.&lt;/p&gt;</content></entry><entry><title>Words of summer</title><link href="https://evilham.eu/en/blog/2020-words-of-summer/" rel="alternate"></link><updated>2020-08-10T00:00:00Z</updated><author><name></name></author><id>urn:uuid:00d14260-9ca9-3d83-bac5-7c808cc5b8a0</id><content type="html">&lt;h1 id="introduccio"&gt;Introducció&lt;/h1&gt;
&lt;p&gt;Dilluns, tarda d&amp;rsquo;estiu pandèmic.
Temperatura, massa alta; humitat relativa, 9001%; vent, gairebé inexistent;
estat de la mar&amp;hellip; tranquil·la.&lt;/p&gt;
&lt;p&gt;Així és com llegeixo que &lt;a href="https://nitter.net/jamiattenberg" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="Jami Attenberg"&gt;una escriptora&lt;/a&gt;, encoratja persones des de
fa un parell d&amp;rsquo;anys a escriure 1000 paraules cada dia en sessions de dues
setmanes, un parell de vegades l&amp;rsquo;any.
I que això, que es diu &lt;a href="https://1000wordsofsummer.substack.com/about" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="1000 paraules d'estiu"&gt;#1000wordsofsummer&lt;/a&gt;, comença avui.&lt;/p&gt;
&lt;p&gt;Penso:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hauria d&amp;rsquo;escriure més, em cal més pràctica.&lt;/p&gt;
&lt;p&gt;M&amp;rsquo;hi apunto! Si més no, es pot intentar. El pitjor que pot passar és que
ho deixis en &lt;em&gt;#500wordsofsummer&lt;/em&gt;, que tampoc està gens malament;
o que ho facis dos dies i ho deixis, que tampoc passa res. &lt;em&gt;¡Dale!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Com a motivació, fes-ho en català, que últimament no hi escrius gairebé mai.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Així doncs, sobre què escric?
El que més bé se&amp;rsquo;m dóna (per a alguns valors de &lt;code&gt;b&lt;/code&gt;) és escriure qüestions
tècniques, i no és una cosa que es trobi habitualment en català.&lt;/p&gt;
&lt;p&gt;Vet aquí que, en un acte subversiu lingüístic:
escriurem qüestions tècniques en català.&lt;/p&gt;
&lt;p&gt;Però&amp;hellip;
Qüestions tècniques sense rerefons tampoc és una cosa que em motivi del tot.&lt;/p&gt;
&lt;p&gt;Solució: popurri d&amp;rsquo;actualitat amb motivacions socials, per tal d&amp;rsquo;aconseguir un
fil conductor per quatre coses tècniques amb cohesió i coherència internes &lt;em&gt;justetes&lt;/em&gt; però, amb una mica de sort, existents. Som-hi!&lt;/p&gt;
&lt;h1 id="taula-de-continguts"&gt;Taula de continguts&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduccio"&gt;Introducció&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#taula-de-continguts"&gt;Taula de continguts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#conjuntura-socioeconomica"&gt;Conjuntura socioeconòmica&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#la-dualitat-privilegi-factor-dexclusio"&gt;La dualitat: privilegi | factor d&amp;rsquo;exclusió&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#acces-a-tecnologia-i-coneixement"&gt;Accés a tecnologia i coneixement&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#el-privilegi"&gt;El privilegi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#el-factor-dexclusio"&gt;El factor d&amp;rsquo;exclusió&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#llengues"&gt;Llengües&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#el-privilegi_1"&gt;El privilegi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#el-factor-dexclusio_1"&gt;El factor d&amp;rsquo;exclusió&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#cultura-de-consum-no-passiu"&gt;Cultura de consum (no passiu)&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#el-privilegi_2"&gt;El privilegi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#el-factor-dexclusio_2"&gt;El factor d&amp;rsquo;exclusió&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#no-endogamia-intellectual"&gt;No endogàmia intel·lectual&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#el-privilegi_3"&gt;El privilegi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#el-factor-dexclusio_3"&gt;El factor d&amp;rsquo;exclusió&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#temes-a-treballar"&gt;Temes a treballar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusio"&gt;Conclusió&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="conjuntura-socioeconomica"&gt;Conjuntura socioeconòmica&lt;/h1&gt;
&lt;p&gt;Arran de &lt;a href="https://www.theverge.com/2020/3/4/21164553/youtube-coronavirus-demonetization-sensitive-subjects-advertising-guidelines-revenue" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="Les circumstàncies actuals són l'eufemisme dels YouTubers quan Google els canvia les regles sobre la marxa; ja sabeu que, realment, parlo de la COVID19"&gt;les circumstàncies actuals&lt;/a&gt;, Catalunya te unes &lt;a href="https://www.ara.cat/economia/Catalunya-tercera-economia-mes-afectada-covid-19-PIB-Airef-segon-trimestre-coronavirus-covid19_0_2502949782.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="El PIB Català cau al 2020 un 26% respecte el 2n trimestre del 2019"&gt;dades&lt;/a&gt; &lt;a href="http://www.elpuntavui.cat/economia/article/18-economia/1827269-es-dispara-la-destruccio-de-llocs-de-treball-a-catalunya-per-la-covid-19.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="Atur del 12.78%, és un 9.67% més que al 2019"&gt;macroeconòmiques&lt;/a&gt; nefastes.
I, no és que no hi pensi de normal, però és especialment en les crisis quan
penso en coses com &lt;a href="https://www.ccma.cat/tv3/alacarta/quatre-gats/yayo-herrero-cal-repensar-com-es-pot-reconvertir-el-model-economic-perque-no-torni-a-passar-el-mateix/video/6053540/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="Hi ha gent que parla molt més bé del que considero assolible per mi"&gt;model econòmic&lt;/a&gt;,
&lt;a href="https://estudiants.msf.cat/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="Iniciatives que intenten adreçar la diferència d'oportunitats"&gt;igualtat d&amp;rsquo;oportunitats&lt;/a&gt;, etc.&lt;/p&gt;
&lt;h1 id="la-dualitat-privilegi-factor-dexclusio"&gt;La dualitat: privilegi | factor d&amp;rsquo;exclusió&lt;/h1&gt;
&lt;p&gt;Crec que un factor clau per reconèixer els nostres privilegis és ser conscients
que, conceptualment, els privilegis formen una dualitat amb els factors
d&amp;rsquo;exclusió.
És a dir, que l&amp;rsquo;absència d&amp;rsquo;un factor d&amp;rsquo;exclusió, és un privilegi, i viceversa.&lt;/p&gt;
&lt;p&gt;Aquesta dualitat és útil per mi, perquè trobo més fàcil imaginar-me un factor
d&amp;rsquo;exclusió, que un privilegi.
Els privilegis poden ser subtils i estar
interioritzats, en canvi, imaginar-nos que en la absència de quelcom,
ens podríem veure exclosos, és més factible.&lt;/p&gt;
&lt;p&gt;No és que jo sigui una persona en una posició tan privilegiada, com per
influenciar aquestes macrodades o adreçar problemes sistèmics de manera eficaç.&lt;/p&gt;
&lt;p&gt;Però sí que he tingut alguns privilegis, dels quals vull ser molt conscient,
i que potser puc fer servir per eliminar el seu dual, en forma de factor
d&amp;rsquo;exclusió, per algú altre.&lt;/p&gt;
&lt;h2 id="acces-a-tecnologia-i-coneixement"&gt;Accés a tecnologia i coneixement&lt;/h2&gt;
&lt;h3 id="el-privilegi"&gt;El privilegi&lt;/h3&gt;
&lt;p&gt;Vaig tenir el meu &lt;a href="/en/blog/2012-How-I-got-into-programming/" title="Fa molts anys que vaig escriure això... Ni ho he revisat"&gt;primer ordinador&lt;/a&gt;
als 6 anys i una connexió a internet (56kbps!) als 8 ó 9 anys.&lt;/p&gt;
&lt;p&gt;Com a experiència curiosa, durant la meva infantesa era l&amp;rsquo;únic nen amb un
ordinador a casa.
I, ja aleshores, vaig ensenyar a tots els humans petits del meu carrer
a engegar, apagar i fer servir un ordinador.
A escriure-hi, a dibuixar-hi, &amp;hellip;&lt;/p&gt;
&lt;p&gt;Es pot dir que ja anava descobrint, la meva línia més
&lt;em&gt;&lt;a href="https://dle.rae.es/jáquer" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="Jáquer, Cyber, Cyber, Jáquer"&gt;jáquer&lt;/a&gt;&lt;/em&gt; (&lt;a href="https://it.wikipedia.org/wiki/Etica_hacker" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="Algú hauria de traduir aquest article al català"&gt;la hacker de debò&lt;/a&gt;, no la de la RAE).
És a dir, que aquest món es tracta de:
compartir, obrir, descentralitzar,
facilitar accés a les tecnologies de la informació i&amp;hellip; millorar el món mateix.&lt;/p&gt;
&lt;p&gt;Tenint en compte que aleshores vivia en un país d&amp;rsquo;Amèrica Llatina, on encara
avui els habitants amb accés a internet representen només un 63.2% de la
població (UE: 89.4%, CAT: 93.7%); és evident que hi tenia algun avantatge.&lt;/p&gt;
&lt;h3 id="el-factor-dexclusio"&gt;El factor d&amp;rsquo;exclusió&lt;/h3&gt;
&lt;p&gt;D&amp;rsquo;altra banda, aquests mesos hem vist com, fins i tot a Catalunya amb una
penetració d&amp;rsquo;accés a internet tan alta (93.7%), les tasques educatives s&amp;rsquo;han
vist afectades de manera negativa
i han fet més evident el caràcter privilegiat de l&amp;rsquo;accés al coneixement
alhora que el factor d&amp;rsquo;exclusió es feia més gran.&lt;/p&gt;
&lt;p&gt;Arreu el món persones altruistes han intentat mitigar aquest factor mitjançant
&lt;a href="https://c3d2.de/news/pentaradio24-20200526.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="Entrevista on es parla d'infraestructures compartides a escoles"&gt;infraestructures compartides&lt;/a&gt;, &lt;a href="https://estudiants.msf.cat/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="Iniciatives que intenten adreçar la diferència d'oportunitats"&gt;iniciatives socials&lt;/a&gt;,
&lt;a href="https://exo.cat" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="Associació per l'expansió de la Xarxa Oberta -- l'únic proveïdor d'internet que no em posa de nervis"&gt;cultura associativa&lt;/a&gt;, &amp;hellip;&lt;/p&gt;
&lt;p&gt;No tot és dolent.&lt;/p&gt;
&lt;h2 id="llengues"&gt;Llengües&lt;/h2&gt;
&lt;h3 id="el-privilegi_1"&gt;El privilegi&lt;/h3&gt;
&lt;p&gt;Parlo i escric &lt;a href="https://chaos.social/@evilham/104607623045948898" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;quatre idiomes&lt;/a&gt; amb fluïdesa i a nivells cultes
(n&amp;rsquo;entenc més, n&amp;rsquo;aprenc &amp;ldquo;seriosament&amp;rdquo; un altre).&lt;/p&gt;
&lt;p&gt;No tot el mèrit és meu; vaig tenir la &amp;ldquo;sort&amp;rdquo; d&amp;rsquo;anar a una escola privada on,
de sèrie, teníem el doble d&amp;rsquo;hores d&amp;rsquo;anglès.&lt;/p&gt;
&lt;p&gt;També tenia accés a un ordinador i a internet a casa des de molt petit.
Resulta que l&amp;rsquo;anglès és l&amp;rsquo;idioma dels ordinadors, aleshores els jocs no es
traduïen gaire, a internet l&amp;rsquo;anglès era bàsicament la única llengua (la
Wikipedia i Google no existien. Ja tinc una edat&amp;hellip;).
Així que tenia força motius per agafar un diccionari i anar traduint la
història dels meus Jocs de Rol (RPG) o els manuals dels programes que feia
servir.&lt;/p&gt;
&lt;p&gt;** Els privilegis s&amp;rsquo;acumulen i creen un bucle de retroalimentació positiu. **&lt;/p&gt;
&lt;h3 id="el-factor-dexclusio_1"&gt;El factor d&amp;rsquo;exclusió&lt;/h3&gt;
&lt;p&gt;L&amp;rsquo;anglès ja era l&amp;rsquo;idioma dels ordinadors fa 20 anys, i encara és el cas.&lt;/p&gt;
&lt;p&gt;Sense saber anglès, l&amp;rsquo;accés a la documentació és sovint limitat, arriba tard, o no està actualitzat.
Molts programes es tradueixen quan arriben a un punt &amp;ldquo;prou estable&amp;rdquo; o
directament no es tradueixen en absolut o no es tradueixen a certes llengües.&lt;/p&gt;
&lt;p&gt;Fins i tot a països com Alemanya amb una llengua parlada per molts referents
de l&amp;rsquo;àmbit, no parlar anglès és un inconvenient.&lt;/p&gt;
&lt;p&gt;Ara, també es produeix tecnologia en alemany.
També es parla de tecnologia en alemany.&lt;/p&gt;
&lt;h2 id="cultura-de-consum-no-passiu"&gt;Cultura de consum (no passiu)&lt;/h2&gt;
&lt;h3 id="el-privilegi_2"&gt;El privilegi&lt;/h3&gt;
&lt;p&gt;De petit no tenia ningú que em solucionés els problemes amb l&amp;rsquo;ordinador o els
electrònics, el que en principi sona com un factor d&amp;rsquo;exclusió, combinat amb
la meva curiositat va esdevenir un privilegi.&lt;/p&gt;
&lt;p&gt;Mai he percebut la tecnologia com un consumible, com una cosa estàtica,
inalterable.&lt;/p&gt;
&lt;p&gt;Això és, en part, perquè eren altres temps; comprar un disc dur o una ampliació
de memòria requeria estalviar molt, tot el que es pogués reutilitzar, era molt
benvingut.&lt;/p&gt;
&lt;p&gt;Tampoc hi havia una indústria tecnològica orientada al consum i al recanvi,
però començava a desenvolupar-se.&lt;/p&gt;
&lt;h3 id="el-factor-dexclusio_2"&gt;El factor d&amp;rsquo;exclusió&lt;/h3&gt;
&lt;p&gt;Els nostres mitjans de comunicació ens parlen de &lt;em&gt;tuitàires&lt;/em&gt;, &lt;em&gt;YouTubers&lt;/em&gt;,
&lt;em&gt;Instagrammers&lt;/em&gt;, &lt;em&gt;Influencers&lt;/em&gt;, de &lt;em&gt;WhatsApp&lt;/em&gt;, &lt;em&gt;Telegram&lt;/em&gt;, &lt;em&gt;iPhones&lt;/em&gt; i
tantes altres empreses i productes exclusivament com si fóssim consumidors
passius.&lt;/p&gt;
&lt;p&gt;El fet que una d&amp;rsquo;aquestes plataformes (centralitzades, autoritàries i tancades)
implementi una funcionalitat banal es torna digne de 5 minuts de tele-diari o
de dotzenes d&amp;rsquo;articles de &amp;ldquo;tecnologia&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;És a dir, se&amp;rsquo;ns educa per consumir, per acceptar el que ens ofereixin, per no
qüestionar, criticar, modificar, millorar.&lt;/p&gt;
&lt;p&gt;El millor exemple d&amp;rsquo;això és el programa &lt;a href="https://www.ccma.cat/catradio/popap/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Popap&lt;/a&gt; de Catalunya Radio.&lt;/p&gt;
&lt;p&gt;No és un mal programa, al contrari, és entretingut, però no eś crític, no
ofereix noves perspectives, va sempre enrere; és passiu.&lt;/p&gt;
&lt;p&gt;Si som consumidors passius, sempre estarem en una posició de desavantatge.&lt;/p&gt;
&lt;h2 id="no-endogamia-intellectual"&gt;No endogàmia intel·lectual&lt;/h2&gt;
&lt;h3 id="el-privilegi_3"&gt;El privilegi&lt;/h3&gt;
&lt;p&gt;He tingut la sort de tenir al meu costat persones fantàstiques i
alienes a aquest àmbit, que han ajudat a que no caigui en les trampes
habituals d&amp;rsquo;una indústria, en general, bastant malaltissa quan es tracta de
posar les persones endavant.&lt;/p&gt;
&lt;p&gt;Sense persones crítiques i amb perspectives diferents, passa que generem
persones d&amp;rsquo;ufana i supèrbia infinites.&lt;/p&gt;
&lt;p&gt;Persones que són capaces de considerar el
&lt;a href="https://www.theguardian.com/technology/2016/feb/17/san-francisco-tech-open-letter-i-dont-want-to-see-homeless-riff-raff" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="Una mica antic. L'esperit no canvia."&gt;&lt;strong&gt;veure&lt;/strong&gt; persones sense sostre un problema&lt;/a&gt;,
en comptes de considerar com un problema el fet que
l&amp;rsquo;accés a l&amp;rsquo;habitatge no sigui universal.&lt;/p&gt;
&lt;p&gt;La part positiva, a més de poder viure amb mi mateix, és que de manera
totalment orgànica i espontània es generen xarxes de solidaritat amb
persones que comparteixen la teva manera de veure el món.&lt;/p&gt;
&lt;p&gt;Sense connexió humana, emocional, intel·lectual; res més te sentit.&lt;/p&gt;
&lt;h3 id="el-factor-dexclusio_3"&gt;El factor d&amp;rsquo;exclusió&lt;/h3&gt;
&lt;p&gt;Aquest&amp;hellip; No serà ben bé un factor d&amp;rsquo;exclusió.
Tristament, la societat en que vivim premia aquest tipus de comportament i
aquestes persones poden sobreviure bastant bé amb altres de semblants.&lt;/p&gt;
&lt;p&gt;El meu &amp;ldquo;consol&amp;rdquo; és que aquestes persones sovint conviuen amb una buidor interna,
no accediran a una xarxa de solidaritat,
no sabran el que és poder comptar amb altres persones i tenir una certa
seguretat de que no estaràs sola en el món.&lt;/p&gt;
&lt;p&gt;Sembla que en el nostre món, aquesta xarxa de solidaritat serà cada vegada més
necessària, fins i tot pels benestants.&lt;/p&gt;
&lt;h1 id="temes-a-treballar"&gt;Temes a treballar&lt;/h1&gt;
&lt;p&gt;Tenint en compte aquests factors (&lt;em&gt;quasi segurament&lt;/em&gt; no exhaustius), se&amp;rsquo;m
planteja com a fil conductor:&lt;/p&gt;
&lt;p&gt;Maneres de mitigar alguns d&amp;rsquo;aquests factors d&amp;rsquo;exclusió digital.&lt;/p&gt;
&lt;p&gt;El com, l&amp;rsquo;anirem descobrint.
Molt segurament passi per comentar l&amp;rsquo;&lt;em&gt;status-quo&lt;/em&gt;, explicar breument per què calen
alternatives, i explicar com es pot passar de ser una persona que consumeix
tecnologia de manera passiva, a coproduir tecnologia o, com a mínim, a
consumir-la de manera conscient i crítica.&lt;/p&gt;
&lt;h1 id="conclusio"&gt;Conclusió&lt;/h1&gt;
&lt;p&gt;Demà més, &lt;em&gt;hopefully&lt;/em&gt;.
Si hi ha algun tema que trobeu especialment interessant, alguna qüestió que no hagi previst o
algun error campant, &lt;a href="/ca/quant-a/"&gt;poseu-vos en contacte&lt;/a&gt; i m&amp;rsquo;ho penso / ho analitzo :-).&lt;/p&gt;
&lt;p&gt;La nota que no hauria de sobrar a internet però que no sobra: els insults us els podeu quedar,
la crítica constructiva, si us plau, &lt;a href="/ca/quant-a/"&gt;feu-la arribar&lt;/a&gt; ^^.&lt;/p&gt;
&lt;p&gt;PS: 1822 paraules han estat escrites pels &lt;a href="https://ca.wikipedia.org/wiki/Teorema_dels_micos_infinits" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank" title="És una bona aproximació de com funciona el meu cervell"&gt;micos infinits&lt;/a&gt;.&lt;/p&gt;</content></entry><entry><title>Phabricator: batch audit</title><link href="https://evilham.eu/en/blog/2020-phabricator-batch-audit/" rel="alternate"></link><updated>2020-07-30T00:00:00Z</updated><author><name></name></author><id>urn:uuid:3472f753-72d1-3ccb-bb2d-aa0880e0c146</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;At work I use &lt;a href="https://www.phacility.com/phabricator/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;phabricator&lt;/a&gt; to collaborate with my employees,
usually I know what they are working on and have a rough idea that things
are going fine, so we currently don&amp;rsquo;t rely on pre-commit code reviews, which
are a wonderful tool, but instead rely on semi-regular audits.
(Check &lt;a href="https://secure.phabricator.com/book/phabricator/article/reviews_vs_audit/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;this&lt;/a&gt; for an overview on
&lt;a href="https://www.phacility.com/phabricator/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;phabricator&lt;/a&gt; reviews and audits)&lt;/p&gt;
&lt;p&gt;This means, that every once in a while, I have to go through my audit queue
and check that indeed things are going mostly alright.
I raise concerns about particular changes seldom, because of a fluid
communication, so I mostly just read and accept changes to keep
having a reasonably up-to-date overview.&lt;/p&gt;
&lt;p&gt;The issue is: &lt;a href="https://www.phacility.com/phabricator/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;phabricator&lt;/a&gt;&amp;lsquo;s UI is very slow for this workflow.&lt;/p&gt;
&lt;p&gt;The good news is that it has a very good API that allows automation of many
things.&lt;/p&gt;
&lt;p&gt;This is about how I implemented &lt;a href="./phabaudit.sh"&gt;phabaudit.sh&lt;/a&gt;, a batch audit
workflow for &lt;a href="https://www.phacility.com/phabricator/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;phabricator&lt;/a&gt; that works-for-me(tm).&lt;/p&gt;
&lt;h1 id="table-of-contents"&gt;Table of Contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#conduit-phabricators-api"&gt;Conduit: phabricator&amp;rsquo;s API&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#guessing-the-api-endpoint"&gt;Guessing the API endpoint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#choosing-the-interaction-tool"&gt;Choosing the interaction tool&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#parsing-the-output"&gt;Parsing the output&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#getting-the-diff-introduced-changes"&gt;Getting the diff (introduced changes)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#approving-the-changes"&gt;Approving the changes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#putting-it-all-together"&gt;Putting it all together&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="conduit-phabricators-api"&gt;Conduit: &lt;a href="https://www.phacility.com/phabricator/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;phabricator&lt;/a&gt;&amp;lsquo;s API&lt;/h1&gt;
&lt;p&gt;Even before &lt;a href="https://swagger.io" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;swagger&lt;/a&gt; was a thing, &lt;a href="https://www.phacility.com/phabricator/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;phabricator&lt;/a&gt; had a
built-in page documenting the conduit API and allowing users to explore the API
in a somewhat interactive fashion.&lt;/p&gt;
&lt;p&gt;This is found at &lt;code&gt;https://phabricator_instance/conduit/&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="guessing-the-api-endpoint"&gt;Guessing the API endpoint&lt;/h2&gt;
&lt;p&gt;Since I know audits are associated with commits, I searched for &lt;code&gt;commit&lt;/code&gt;-related
API endpoints.
Indeed, &lt;code&gt;diffusion.commit.search&lt;/code&gt; is a good place to start and it contains a
simple example, which satisfies my current needs:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{
  ...
  &amp;quot;queryKey&amp;quot;: &amp;quot;active&amp;quot;,
  ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Where &lt;code&gt;"queryKey": "active"&lt;/code&gt; means that I want to list &lt;strong&gt;active audits&lt;/strong&gt; only.
There is also a mention of the &lt;code&gt;order&lt;/code&gt; parameter which can be &lt;code&gt;oldest&lt;/code&gt; to list
entries in chronological order.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s test that.&lt;/p&gt;
&lt;h2 id="choosing-the-interaction-tool"&gt;Choosing the interaction tool&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;examples&lt;/strong&gt; section in the &lt;code&gt;conduit&lt;/code&gt; page offers &lt;code&gt;curl&lt;/code&gt; and
&lt;code&gt;arc call-conduit&lt;/code&gt; examples, both have pros and cons.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;curl&lt;/code&gt; is a mostly standard tool nearly guaranteed to be available, which means
that it can be mostly tested directly from the shell with:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;https://phabricator_instance/api/diffusion.commit.search&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;api.token&lt;span class="o"&gt;=&lt;/span&gt;api-token&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;order&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;oldest&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;queryKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;active&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;audits.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But there is another option: &lt;code&gt;arc call-conduit&lt;/code&gt;, that is
&lt;a href="https://www.phacility.com/phabricator/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;phabricator&lt;/a&gt;&amp;lsquo;s very well thought out Command Line Interface.&lt;/p&gt;
&lt;p&gt;It enables many workflows and takes care of managing the API tokens
(which I don&amp;rsquo;t have to pass int he command line every time).
Audits though are a workflow that is currently not supported, but &lt;code&gt;call-conduit&lt;/code&gt;
enables arbitrary API calls.&lt;/p&gt;
&lt;p&gt;I happen to use &lt;code&gt;arc&lt;/code&gt;, amongst others, to contribute to &lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;,
which does use pre-commit code reviews, that enables me and the
project members and community to review changes before they land, raise
concerns, suggest improvements, ensure nothing fishy is trying to be
committed, &amp;hellip;&lt;/p&gt;
&lt;p&gt;Since I already have &lt;code&gt;arc&lt;/code&gt;, I will use that to forget about authentication.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{&amp;quot;queryKey&amp;quot;: &amp;quot;active&amp;quot;, &amp;quot;order&amp;quot;: &amp;quot;oldest&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;arc&lt;span class="w"&gt; &lt;/span&gt;call-conduit&lt;span class="w"&gt; &lt;/span&gt;--conduit-uri&lt;span class="w"&gt; &lt;/span&gt;https://phabricator_instace/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;diffusion.commit.search&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;audits.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="parsing-the-output"&gt;Parsing the output&lt;/h2&gt;
&lt;p&gt;The output of both tools is JSON piped to an &lt;code&gt;audits.json&lt;/code&gt; file which can be
&amp;ldquo;easily&amp;rdquo; manipulated with &lt;code&gt;jq&lt;/code&gt;.
I write &amp;ldquo;easily&amp;rdquo; because it takes some getting used to, but using &lt;code&gt;jq&lt;/code&gt; is
certainly doable.&lt;/p&gt;
&lt;p&gt;This gives me the information I need to an &lt;code&gt;audit_commits.json&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;jq&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.response.data[] | {&amp;quot;commit&amp;quot;: .fields.identifier, &amp;quot;repository&amp;quot;: .fields.repositoryPHID}&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;audits.json&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;audit_commits.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="getting-the-diff-introduced-changes"&gt;Getting the &lt;code&gt;diff&lt;/code&gt; (introduced changes)&lt;/h2&gt;
&lt;p&gt;In order to get the &lt;code&gt;diff&lt;/code&gt;, so I can audit it locally, we need a couple more
API calls that can also be guessed from the
&lt;code&gt;https://phabricator_instance/conduit/&lt;/code&gt; page:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{&amp;quot;commit&amp;quot;: COMMIT, &amp;quot;repository&amp;quot;: REPOSITORY}&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;arc&lt;span class="w"&gt; &lt;/span&gt;call-conduit&lt;span class="w"&gt; &lt;/span&gt;--conduit-uri&lt;span class="w"&gt; &lt;/span&gt;https://phabricator_instance/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;diffusion.rawdiffquery

&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;error&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;null,
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;errorMessage&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;null,
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;response&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;tooSlow&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;false,
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;tooHuge&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;false,
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;filePHID&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;PHID-FILE-00000000&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And with that &lt;code&gt;filePHID&lt;/code&gt;, we can get the actual diff data:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{&amp;quot;phid&amp;quot;: &amp;quot;PHID-FILE-00000000&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;arc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;conduit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;conduit&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;phabricator_instance&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;download&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;error&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb nb-Type"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;errorMessage&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb nb-Type"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;response&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BASE_64_STUFF&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which is &lt;a href="https://en.wikipedia.org/wiki/Base64" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;base64 encoded&lt;/a&gt;, so we need to decode it.
By checking &lt;code&gt;man 1 b64decode&lt;/code&gt; I realise I need the &lt;code&gt;-r&lt;/code&gt; flag, so getting the
diff file to &lt;code&gt;test.diff&lt;/code&gt; in the hard drive looks like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{&amp;quot;phid&amp;quot;: &amp;quot;PHID-FILE-00000000&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;arc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;conduit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;conduit&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;phabricator_instance&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;jq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;b64decode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="approving-the-changes"&gt;Approving the changes&lt;/h2&gt;
&lt;p&gt;After opening &lt;code&gt;test.diff&lt;/code&gt; in my favourite editor, I decide that the change is
valid and I want to mark it as approved.&lt;/p&gt;
&lt;p&gt;Again, since audits are associated with commits, this will be in
&lt;code&gt;diffusion.commit.edit&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{&amp;quot;objectIdentifier&amp;quot;: COMMIT_PHID,&lt;/span&gt;
&lt;span class="s1"&gt;         &amp;quot;transactions&amp;quot;: [{&amp;quot;type&amp;quot;: &amp;quot;accept&amp;quot;, &amp;quot;value&amp;quot;: true}]}&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;arc&lt;span class="w"&gt; &lt;/span&gt;call-conduit&lt;span class="w"&gt; &lt;/span&gt;--conduit-uri&lt;span class="w"&gt; &lt;/span&gt;https://phabricator_instance/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;diffusion.commit.edit
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1 id="putting-it-all-together"&gt;Putting it all together&lt;/h1&gt;
&lt;p&gt;Now that I have all the pieces, I can finish automating it with some POSIX shell
as follows (download the full script &lt;a href="./phabaudit.sh"&gt;here&lt;/a&gt;):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Find the latest version at:&lt;/span&gt;
&lt;span class="c1"&gt;#   https://evilham.com/en/blog/2020-phabricator/batch-audit/phabaudit.sh&lt;/span&gt;
&lt;span class="c1"&gt;# With the accompanying post:&lt;/span&gt;
&lt;span class="c1"&gt;#   https://evilham.com/en/blog/2020-phabricator/batch-audit/&lt;/span&gt;
&lt;span class="c1"&gt;# &lt;/span&gt;
&lt;span class="c1"&gt;# Copyright 2020 Evilham &amp;lt;code@evilham.com&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Redistribution and use in source and binary forms, with or without&lt;/span&gt;
&lt;span class="c1"&gt;# modification, are permitted provided that the following conditions are met:&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# 1. Redistributions of source code must retain the above copyright notice,&lt;/span&gt;
&lt;span class="c1"&gt;#    this list of conditions and the following disclaimer.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# 2. Redistributions in binary form must reproduce the above copyright notice,&lt;/span&gt;
&lt;span class="c1"&gt;#    this list of conditions and the following disclaimer in the documentation&lt;/span&gt;
&lt;span class="c1"&gt;#    and/or other materials provided with the distribution.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &amp;quot;AS IS&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE&lt;/span&gt;
&lt;span class="c1"&gt;# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE&lt;/span&gt;
&lt;span class="c1"&gt;# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE&lt;/span&gt;
&lt;span class="c1"&gt;# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR&lt;/span&gt;
&lt;span class="c1"&gt;# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF&lt;/span&gt;
&lt;span class="c1"&gt;# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS&lt;/span&gt;
&lt;span class="c1"&gt;# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN&lt;/span&gt;
&lt;span class="c1"&gt;# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)&lt;/span&gt;
&lt;span class="c1"&gt;# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE&lt;/span&gt;
&lt;span class="c1"&gt;# POSSIBILITY OF SUCH DAMAGE.&lt;/span&gt;

&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Process environment variables.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# User may provide CONDUIT_ARGS or PHABRICATOR_INSTANCE&lt;/span&gt;
&lt;span class="c1"&gt;# If PHABRICATOR_INSTANCE is passed, CONDUIT_ARGS will be adapted so that&lt;/span&gt;
&lt;span class="c1"&gt;# arc call-conduit always uses: --conduit-uri ${PHABRICATOR_INSTANCE}&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# If CONDUIT_ARGS is passed, it will be used verbatim (modulo appending&lt;/span&gt;
&lt;span class="c1"&gt;# --conduit-uri as explained before).&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PHABRICATOR_INSTANCE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;CONDUIT_ARGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONDUIT_ARGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; --conduit-uri &amp;#39;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PHABRICATOR_INSTANCE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39;&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c1"&gt;# Temp dir for the audit diffs&lt;/span&gt;
&lt;span class="nv"&gt;TMP_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;mktemp&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;audits&lt;span class="k"&gt;)&lt;/span&gt;

get_audits&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{&amp;quot;queryKey&amp;quot;: &amp;quot;active&amp;quot;, &amp;quot;order&amp;quot;: &amp;quot;oldest&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;arc&lt;span class="w"&gt; &lt;/span&gt;call-conduit&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONDUIT_ARGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;diffusion.commit.search
&lt;span class="o"&gt;}&lt;/span&gt;

filter_commit_data&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;jq&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.response.data[] | &lt;/span&gt;
&lt;span class="s1"&gt;        {&lt;/span&gt;
&lt;span class="s1"&gt;            &amp;quot;phid&amp;quot;: .phid,&lt;/span&gt;
&lt;span class="s1"&gt;            &amp;quot;commit&amp;quot;: .fields.identifier,&lt;/span&gt;
&lt;span class="s1"&gt;            &amp;quot;repository&amp;quot;: .fields.repositoryPHID&lt;/span&gt;
&lt;span class="s1"&gt;        }&amp;#39;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

get_diff_phid&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;jq&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{&amp;quot;commit&amp;quot;: .commit, &amp;quot;repository&amp;quot;: .repository}&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;arc&lt;span class="w"&gt; &lt;/span&gt;call-conduit&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONDUIT_ARGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;diffusion.rawdiffquery&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;jq&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;.response.filePHID
&lt;span class="o"&gt;}&lt;/span&gt;

save_diff&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;diff_phid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Download the diff to disk&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;{\&amp;quot;phid\&amp;quot;: \&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;diff_phid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\&amp;quot;}&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;arc&lt;span class="w"&gt; &lt;/span&gt;call-conduit&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONDUIT_ARGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;file.download&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;jq&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;.response&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;b64decode&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TMP_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;diff_phid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.diff&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Return path to diff in stdout&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TMP_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;diff_phid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.diff&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

get_phid_url&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;jq&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{&amp;quot;names&amp;quot;: [.phid]}&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;arc&lt;span class="w"&gt; &lt;/span&gt;call-conduit&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONDUIT_ARGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;phid.lookup&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;jq&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.response | to_entries | .[] | .value.uri&amp;#39;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

accept_changes&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;jq&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{&lt;/span&gt;
&lt;span class="s1"&gt;        &amp;quot;objectIdentifier&amp;quot;: .phid,&lt;/span&gt;
&lt;span class="s1"&gt;        &amp;quot;transactions&amp;quot;: [{&amp;quot;type&amp;quot;: &amp;quot;accept&amp;quot;, &amp;quot;value&amp;quot;: true}]&lt;/span&gt;
&lt;span class="s1"&gt;    }&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;arc&lt;span class="w"&gt; &lt;/span&gt;call-conduit&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONDUIT_ARGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;diffusion.commit.edit
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;audit&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;get_audits&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;filter_commit_data&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;audit_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;audit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;get_phid_url&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;\n\nAudit URL: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;audit_url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;diff_phid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;audit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;get_diff_phid&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;diff_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;save_diff&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;diff_phid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Open in editor for easy review&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;EDITOR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;diff_file&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Ask for feedback as necessary&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Do you accept this change? [Y/n] &amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;accept
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-z&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;accept&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;accept&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;#[Yy]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# Close audit &lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;audit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;accept_changes
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Now that I&amp;rsquo;m done designing a workflow that is reasonable for me, I better
start doing some auditing &lt;code&gt;/o\&lt;/code&gt; and real-world testing of
&lt;a href="./phabaudit.sh"&gt;phabaudit.sh&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If this happens to be useful to you, &lt;a href="/en/about/"&gt;let me know&lt;/a&gt;!&lt;/p&gt;</content></entry><entry><title>FreeBSD updating a port: Twisted python</title><link href="https://evilham.eu/en/blog/2020-FreeBSD-updating-a-port-twisted-python/" rel="alternate"></link><updated>2020-04-01T00:00:00Z</updated><author><name></name></author><id>urn:uuid:61bdb670-e222-3c1a-8b47-264a089de3e1</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; is a wonderful Operating System, one of its many
interesting ideas is a division between the &lt;strong&gt;base system&lt;/strong&gt; and other
pieces of software, which are called &lt;strong&gt;ports&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Since &lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; is an operating system, this also means that there
are exactly two &lt;em&gt;source trees&lt;/em&gt; or repositories: the main tree (base system)
and the ports tree.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;base system&lt;/strong&gt; is developed together and therefore is kept consistent,
whenever an API or a behaviour changes, all pieces of the &lt;strong&gt;base system&lt;/strong&gt;
must be updated as well.&lt;/p&gt;
&lt;p&gt;This makes changes somewhat seamless and easy to deal with.&lt;/p&gt;
&lt;p&gt;On the other hand, &lt;strong&gt;ports&lt;/strong&gt; provide a &lt;em&gt;description&lt;/em&gt; of how to compile
third-party software under &lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; and how to install it manually or
how to build an installable binary package.&lt;/p&gt;
&lt;p&gt;However, &lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; development, including maintaining the ports tree,
is a voluntary-based effort; which means sometimes it lacks human power.&lt;/p&gt;
&lt;p&gt;Being a good open source user sometimes goes through being aware that people&amp;rsquo;s
time is limited, and helping out when things fall through the cracks.&lt;/p&gt;
&lt;p&gt;Here I document a bit how the general process of updating a port you detect as
being outdated is.&lt;/p&gt;
&lt;h1 id="table-of-contents"&gt;Table of Contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#communication-and-coordination"&gt;Communication and coordination&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#documentation"&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#chat-irc"&gt;Chat &amp;ndash; IRC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#bugzilla-problem-reports-prs"&gt;Bugzilla &amp;ndash; Problem Reports (PRs)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#phabricator-code-reviews"&gt;Phabricator &amp;ndash; Code Reviews&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#live-example-updating-twisted-python"&gt;Live example: Updating Twisted python&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#finding-out-more-about-the-port"&gt;Finding out more about the port&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#telling-people-well-be-updating-the-port"&gt;Telling people we&amp;rsquo;ll be updating the port&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#getting-the-port-tree"&gt;Getting the port tree&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#poudriere"&gt;Poudriere&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#testing-that-the-port-builds-currently"&gt;Testing that the port builds currently&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#actually-updating-the-port"&gt;Actually updating the port&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#changing-the-version"&gt;Changing the version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#dependencies"&gt;Dependencies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#building-the-new-version"&gt;Building the new version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#testing-the-port"&gt;Testing the port&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#submitting-the-patch"&gt;Submitting the patch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusions"&gt;Conclusions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="communication-and-coordination"&gt;Communication and coordination&lt;/h1&gt;
&lt;p&gt;Contrary to &amp;ldquo;common wisdom&amp;rdquo; in the industry, it is not &amp;ldquo;the technical bits&amp;rdquo;,
that is difficult when trying to help with a project; it is communication
and coordination that are difficult things when many people are involved.
And nothing big or useful enough gets built and maintained without people.&lt;/p&gt;
&lt;p&gt;In a nutshell, when interacting with a project of this kind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Be kind&lt;/li&gt;
&lt;li&gt;Respect and appreciate other people&amp;rsquo;s time and energy&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t assume incompetence or unwillingness, when it can be explained by
  lack of resources (usually: time, human, money, infra)&lt;/li&gt;
&lt;li&gt;Make sure you research before demanding time or effort from people&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Interestingly, the &lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; project has the communication and
coordination bits mostly quite well-defined.&lt;/p&gt;
&lt;h2 id="documentation"&gt;Documentation&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;&amp;lsquo;s documentation is really good, but there are multiple places
to look for information, depending on the nature of the information.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD Handbook&lt;/a&gt;: General system information and administration&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.freebsd.org/cgi/man.cgi" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Man pages&lt;/a&gt;: Information about specific bits of the system&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD wiki&lt;/a&gt;: Information that is prone to change often&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="chat-irc"&gt;Chat &amp;ndash; IRC&lt;/h2&gt;
&lt;p&gt;Following list of channels on &lt;code&gt;irc.freenode.net&lt;/code&gt; are a great place to start,
there are usually people who will point you out to the right resource, the
right person or the right place to deal with things.&lt;/p&gt;
&lt;p&gt;Just make sure you are patient since not everyone is online always (timezones!)
and express your question in as much of an accurate fashion as possible.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;#freebsd&lt;/code&gt;: For general OS questions and support&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#freebsd-ports&lt;/code&gt;: For questions more specific to maintenance of the ports tree&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#freebsd-dev&lt;/code&gt;: For questions more specific to development of the OS&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="bugzilla-problem-reports-prs"&gt;Bugzilla &amp;ndash; Problem Reports (PRs)&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://bugs.freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&amp;rsquo;s Bugzilla&lt;/a&gt; is used to track problems with the
base system or ports and also to document what is being worked on by whom.&lt;/p&gt;
&lt;p&gt;It also serves a special purpose with the ports workflow:
when a change is proposed to a specific port, if there is no feedback from the
port maintainer within a reasonable time, that change times out and can be
accepted and merged by anyone with permissions (commit bit) to the ports tree.&lt;/p&gt;
&lt;h2 id="phabricator-code-reviews"&gt;Phabricator &amp;ndash; Code Reviews&lt;/h2&gt;
&lt;p&gt;Code reviews are &lt;em&gt;wonderful&lt;/em&gt;. &lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; has a
&lt;a href="https://reviews.freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Phabricator instance&lt;/a&gt; that is used for precisely this purpose.&lt;/p&gt;
&lt;p&gt;It requires getting used to it, but &lt;a href="https://phabricator.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Phabricator&lt;/a&gt; is a very
reasonable piece of software and the review workflow is incredibly useful.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t be afraid, most people who might review your code will be kind and it
means that the end result will be better.&lt;/p&gt;
&lt;h1 id="live-example-updating-twisted-python"&gt;Live example: Updating &lt;a href="https://twistedmatrix.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Twisted python&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;As of this writing &lt;a href="https://www.freshports.org/devel/py-twisted/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Twisted python&amp;rsquo;s port&lt;/a&gt; is at version 18.9.0,
because of the way Twisted manages versions, this means it dates back to
September 2018.&lt;/p&gt;
&lt;p&gt;In between, Twisted has gotten significantly better and has had some security
fixes, as recently as a couple weeks ago: March 2020.&lt;/p&gt;
&lt;p&gt;So we really should update the port to version 20.3.0.&lt;/p&gt;
&lt;h2 id="finding-out-more-about-the-port"&gt;Finding out more about the port&lt;/h2&gt;
&lt;p&gt;A good place to find out about ports is
&lt;a href="https://www.freshports.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;www.freshports.org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If we go there and search for &lt;code&gt;twisted&lt;/code&gt;, we&amp;rsquo;ll come to the
&lt;a href="https://www.freshports.org/devel/py-twisted/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;port&amp;rsquo;s page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Amongst other things, it tells us that the port is a build dependency for
4 other ports and a run dependency for 55 ports.&lt;/p&gt;
&lt;p&gt;And it also tells us a very interesting bit, the full name of the port is:
&lt;code&gt;devel/py-twisted&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Each port has a main category, which is then followed by the port name,
separated by a &lt;code&gt;/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Indeed, this is where we will find the code defining the
&lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; port.&lt;/p&gt;
&lt;h2 id="telling-people-well-be-updating-the-port"&gt;Telling people we&amp;rsquo;ll be updating the port&lt;/h2&gt;
&lt;p&gt;First things first, we search on &lt;a href="https://bugs.freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&amp;rsquo;s Bugzilla&lt;/a&gt; for open
Problem Reports (PRs) relating the port we want to update.
Maybe someone is already working on it or there are subtleties that escape
our uninitiated knowledge (like dependencies, external blockers, &amp;hellip;).&lt;/p&gt;
&lt;p&gt;In this case there are none, so we open a PR informing that we&amp;rsquo;ll be updating
this port.&lt;/p&gt;
&lt;p&gt;This is done on &lt;a href="https://bugs.freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&amp;rsquo;s Bugzilla&lt;/a&gt; by &lt;strong&gt;filing a new bug&lt;/strong&gt;
against the &lt;strong&gt;Ports and packages&lt;/strong&gt; &amp;ldquo;product&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;There is a very good write-up about this by koobs in the
&lt;a href="https://wiki.freebsd.org/KubilayKocak/ThePerfectPortsIssue" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD wiki&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Very important bits here are how to write the &lt;code&gt;Summary&lt;/code&gt; field and how to
manage the &lt;code&gt;Flags&lt;/code&gt; of the PR.&lt;/p&gt;
&lt;p&gt;In particular, this will be our &lt;code&gt;Summary&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;devel/py-twisted: WIP: Update to 20.3.0 (includes security updates)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Severity, hardware and OS are not that important here, so we leave the
defaults.&lt;/p&gt;
&lt;p&gt;Now, &lt;em&gt;something&lt;/em&gt; will do magic and this PR will be assigned to the
&lt;strong&gt;maintainer&lt;/strong&gt; of the port, in this case that&amp;rsquo;s &lt;code&gt;python@freebsd.org&lt;/code&gt;, which
means there is no particular person, but a general Python team maintaining
the port.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s tell them about our intentions, this will be the body:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;noticed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FreeBSD&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;s twisted version is a bit outdated, so I&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;ll&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;taking&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;entirely&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;impossible&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ll have to update other ports&lt;/span&gt;
&lt;span class="s1"&gt;like attrs, we&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;ll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;see&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;also&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;whole&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myself&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;future&lt;/span&gt;
&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;too&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hopefully&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;people&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;who&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;would&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;consider&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;doing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;things&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;like&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;posting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;patch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;over&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;next&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;few&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Cheers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This way, if someone came wanting to do they same thing, they&amp;rsquo;d ask before
if we are still working on it, and we won&amp;rsquo;t duplicate efforts.&lt;/p&gt;
&lt;p&gt;Or maybe someone knows something we don&amp;rsquo;t, so they will use the chance to
tell us in our
&lt;a href="twistedpr"&gt;PR #245252&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="getting-the-port-tree"&gt;Getting the port tree&lt;/h2&gt;
&lt;p&gt;This can be done in multiple ways, the most convenient one is getting the
code from the same repository that is used by developers over anonymous SVN:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;svn checkout svn://svn.freebsd.org/ports ports
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Another one is &lt;code&gt;portsnap&lt;/code&gt;, which is in the base system, but instead of
explaining that, I&amp;rsquo;ll go a bit into my favourite: &lt;code&gt;poudriere&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="poudriere"&gt;Poudriere&lt;/h3&gt;
&lt;p&gt;Poudriere makes it simple to manage a &lt;code&gt;pkg&lt;/code&gt; repository and to build
customised packages, it also makes it simple to work on the ports tree.&lt;/p&gt;
&lt;p&gt;There is a wonderful
&lt;a href="https://wiki.freebsd.org/VladimirKrstulja/Guides/Poudriere" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Poudriere guide&lt;/a&gt;
on the &lt;a href="https://wiki.freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD wiki&lt;/a&gt;, so I&amp;rsquo;ll just do the TL;DR:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;poudriere&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;pre&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;built&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ports&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;pkg&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;pkg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;poudriere&lt;/span&gt;
#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;builder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;jail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;amd64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;based&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;RELEASE&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;poudriere&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;jail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;121&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;amd64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;.&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;RELEASE&lt;/span&gt;
#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;something&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;under&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;ports&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;completely&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;sure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;never&lt;/span&gt;
#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;messed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;it&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;clean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;up&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;with&lt;/span&gt;:
#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;rf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;ports&lt;/span&gt;
#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ports&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tree&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;poudriere&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ports&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;
#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;prefer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;somewhere&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;:
#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;poudriere&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ports&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;M&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;poudriere&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;ports&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="testing-that-the-port-builds-currently"&gt;Testing that the port builds currently&lt;/h2&gt;
&lt;p&gt;Before making any modifications, let&amp;rsquo;s test that the port builds as it is, to
discard that there is an issue somewhere else.&lt;/p&gt;
&lt;p&gt;For that we can execute:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;poudriere bulk -j 121-amd64 -p default devel/py-twisted
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now Poudriere will build the &lt;code&gt;devel/py-twisted&lt;/code&gt; port and everything it depends
on.
The first time this will take a while, but afterwards only the ports that have
changed will be rebuilt.&lt;/p&gt;
&lt;h2 id="actually-updating-the-port"&gt;Actually updating the port&lt;/h2&gt;
&lt;p&gt;Since this is a Python port, the &lt;a href="https://wiki.freebsd.org/Python/PortsPolicy" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Python Ports Policy&lt;/a&gt; has
precedence over the &lt;a href="https://www.freebsd.org/doc/en_US.ISO8859-1/books/porters-handbook/index.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Porter&amp;rsquo;s handbook&lt;/a&gt;, both are worth a read.&lt;/p&gt;
&lt;p&gt;We go to the place where the ports tree is located and navigate to the
&lt;code&gt;devel/py-twisted&lt;/code&gt; subdirectory.&lt;/p&gt;
&lt;p&gt;All ports have at least following structure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Makefile&lt;/code&gt;: Describes what is going to be built and how&lt;/li&gt;
&lt;li&gt;&lt;code&gt;distinfo&lt;/code&gt;: Contains the checksums of any needed external files
  (like source code!)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pkg-descr&lt;/code&gt;: Is what users see when they search for this port/package.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since Twisted&amp;rsquo;s is a simple port, we just have to edit its &lt;code&gt;Makefile&lt;/code&gt;:&lt;/p&gt;
&lt;h3 id="changing-the-version"&gt;Changing the version&lt;/h3&gt;
&lt;p&gt;The most obvious change is going to be&amp;hellip; The version, that&amp;rsquo;s following diff:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;PORTNAME=  twisted
&lt;span class="gd"&gt;-PORTVERSION=   18.9.0&lt;/span&gt;
&lt;span class="gd"&gt;-PORTREVISION=  1&lt;/span&gt;
&lt;span class="gi"&gt;+PORTVERSION=   20.3.0&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;CATEGORIES=    devel net python
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We change the &lt;code&gt;PORTVERSION&lt;/code&gt; and since there now is no &lt;code&gt;PORTREVISION&lt;/code&gt;, because
this is a new version, we remove that line.&lt;/p&gt;
&lt;p&gt;This &lt;em&gt;might&lt;/em&gt; be enough, but also, Twisted is not an island, it has
dependencies, and they might have changed in between.&lt;/p&gt;
&lt;h3 id="dependencies"&gt;Dependencies&lt;/h3&gt;
&lt;p&gt;Port dependencies are also listed in the port&amp;rsquo;s &lt;code&gt;Makefile&lt;/code&gt;, in particular in
the &lt;code&gt;BUILD_DEPENDS&lt;/code&gt; variable.&lt;/p&gt;
&lt;p&gt;How we update this depends a bit on how comfortable we are with the project&amp;rsquo;s
tooling.&lt;/p&gt;
&lt;p&gt;A perfectly valid option could be just to eyeball the code from a web
interface, since I contribute to &lt;a href="https://twistedmatrix.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Twisted&lt;/a&gt; when time allows, I have
the code locally and can use following:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git diff twisted-18.9.0..twisted-20.3.0 -- src/twisted/python/_setup.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which tells me exactly how the dependencies have changed between these two
versions.
Resulting in following diff:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;BUILD_DEPENDS= ${PYTHON_PKGNAMEPREFIX}constantly&amp;gt;=15.1:devel/py-constantly@${PY_FLAVOR} \
&lt;span class="gd"&gt;-       ${PYTHON_PKGNAMEPREFIX}attrs&amp;gt;17.4.0:devel/py-attrs@${PY_FLAVOR} \&lt;/span&gt;
&lt;span class="gi"&gt;+       ${PYTHON_PKGNAMEPREFIX}attrs&amp;gt;19.2.0:devel/py-attrs@${PY_FLAVOR} \&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;       ${PYTHON_PKGNAMEPREFIX}hyperlink&amp;gt;=17.1.1:www/py-hyperlink@${PY_FLAVOR} \
&lt;span class="w"&gt; &lt;/span&gt;       ${PYTHON_PKGNAMEPREFIX}incremental&amp;gt;=16.10.1:devel/py-incremental@${PY_FLAVOR} \
&lt;span class="w"&gt; &lt;/span&gt;       ${PYTHON_PKGNAMEPREFIX}PyHamcrest&amp;gt;=1.9.0:textproc/py-pyhamcrest@${PY_FLAVOR} \
&lt;span class="gu"&gt;@@ -23,7 +22,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;       ${PYTHON_PKGNAMEPREFIX}zope.interface&amp;gt;=4.4.0:devel/py-zope.interface@${PY_FLAVOR} \
&lt;span class="w"&gt; &lt;/span&gt;       ${PYTHON_PKGNAMEPREFIX}Automat&amp;gt;=0.3.0:devel/py-Automat@${PY_FLAVOR}
&lt;span class="w"&gt; &lt;/span&gt;RUN_DEPENDS:=  ${BUILD_DEPENDS}
&lt;span class="gd"&gt;-TEST_DEPENDS=  ${PYTHON_PKGNAMEPREFIX}service_identity&amp;gt;0:security/py-service_identity@${PY_FLAVOR}&lt;/span&gt;
&lt;span class="gi"&gt;+TEST_DEPENDS=  ${PYTHON_PKGNAMEPREFIX}service_identity&amp;gt;=18.1.0:security/py-service_identity@${PY_FLAVOR}&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;USES=      python tar:bzip2
&lt;span class="w"&gt; &lt;/span&gt;USE_PYTHON=    autoplist concurrent distutils
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We might need to update &lt;code&gt;devel/py-attrs&lt;/code&gt; and &lt;code&gt;sercurity/py-service_identity&lt;/code&gt;
as well. Time will tell.&lt;/p&gt;
&lt;p&gt;Since the &lt;em&gt;distfiles&lt;/em&gt; for the port have changed, because we now are building a
different version, we also have to update the checksums file.&lt;/p&gt;
&lt;p&gt;This is easily done by running:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;make makesum
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which downloads all needed distfiles, calculates the checksums and
updates the corresponding file.&lt;/p&gt;
&lt;h2 id="building-the-new-version"&gt;Building the new version&lt;/h2&gt;
&lt;p&gt;Is the same as before our changes, Poudriere will notice that the port&amp;rsquo;s
definition has changed and trigger a rebuild:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;poudriere bulk -j 121-amd64 -p default devel/py-twisted
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Turns out, &lt;code&gt;devel/py-attrs&lt;/code&gt; is already at version &lt;code&gt;19.3.0&lt;/code&gt;, and
&lt;code&gt;security/py-service_identity&lt;/code&gt; at version &lt;code&gt;18.1.0&lt;/code&gt; both of which satisfy Twisted&amp;rsquo;s requirements.
So the port builds without issues \o/.&lt;/p&gt;
&lt;p&gt;Time to party now? Well, not quite. We still have to make sure the port still works
as intended.&lt;/p&gt;
&lt;h2 id="testing-the-port"&gt;Testing the port&lt;/h2&gt;
&lt;p&gt;We can test the port with &lt;code&gt;make test&lt;/code&gt;, which runs upstream&amp;rsquo;s testing suite.&lt;/p&gt;
&lt;p&gt;But also, if we are caring enough to update this port, odds are that we are
using it in way or another, that&amp;rsquo;s the best kind of test.&lt;/p&gt;
&lt;p&gt;In my case, I&amp;rsquo;ll also keep developing my custom software, which depends on
Twisted, but using the new version.&lt;/p&gt;
&lt;h2 id="submitting-the-patch"&gt;Submitting the patch&lt;/h2&gt;
&lt;p&gt;After we have tested the newly built port, we should send the patch; this is
done by adding an attachment to our PR with the diff (obtained by &lt;code&gt;svn diff&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;For a more complex patch than this one, we might want to use the Code Review
flow &lt;strong&gt;as well as this one&lt;/strong&gt;, and keep the patch in Bugzilla updated as it
evolves.&lt;/p&gt;
&lt;p&gt;This is because Bugzilla is where maintainer timeouts and triage happen and
in general things are better tracked.&lt;/p&gt;
&lt;p&gt;When we add the attachment, we&amp;rsquo;ll request maintainer feedback by setting the
flag with the same name to &lt;code&gt;?&lt;/code&gt;, the &lt;code&gt;patch&lt;/code&gt; keyword will be added automatically,
and we&amp;rsquo;ll also remove &lt;code&gt;WIP:&lt;/code&gt; from the PR&amp;rsquo;s title and add the &lt;code&gt;security&lt;/code&gt;
keyword.&lt;/p&gt;
&lt;p&gt;To make things easier for maintainers / reviewers / ports security team,
we&amp;rsquo;ll also add more information in the comments:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Changelog&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/twisted/twisted/blob/twisted-20.3.0/&lt;/span&gt;&lt;span class="n"&gt;NEWS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rst&lt;/span&gt;

&lt;span class="n"&gt;QA&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;portlint&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OK&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;looks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fine&lt;/span&gt;&lt;span class="o"&gt;.)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testport&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OK&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;poudriere&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amd64&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;Related&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Security&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;issues&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;CVE&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10108&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;CVE&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9512&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;CVE&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9514&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;CVE&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9515&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;CVE&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12387&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;CVE&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12855&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="n"&gt;twistedmatrix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/trac/ticket/&lt;/span&gt;&lt;span class="mi"&gt;9420&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And now someone will hopefully check our PR soon and commit it.&lt;/p&gt;
&lt;h1 id="conclusions"&gt;Conclusions&lt;/h1&gt;
&lt;p&gt;Sometimes the quickest way to have something be done is doing it yourself :-D.
This doesn&amp;rsquo;t even take that long, so get into the habit of considering
updating ports and proposing patches yourself.&lt;/p&gt;
&lt;p&gt;Also, these things have effects, e.g. I know for a fact that
&lt;a href="https://matrix.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Matrix&lt;/a&gt;
&lt;a href="https://www.freshports.org/net-im/py-matrix-synapse/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Synapse&lt;/a&gt;
depends on &lt;a href="https://twistedmatrix.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Twisted&lt;/a&gt;! This was probably being a blocker :-p.&lt;/p&gt;</content></entry><entry><title>Switching Android phones -- Getting rid of Google (mostly)</title><link href="https://evilham.eu/en/blog/2020-switching-Android-phones/" rel="alternate"></link><updated>2020-01-05T00:00:00Z</updated><author><name></name></author><id>urn:uuid:871318f0-5283-3713-b5f9-a8a3f900be4c</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;After being fed up with mostly non-serviceable phones and my previous one
suffering a bit of an accident; it was the perfect time to align consumer
values with portable device because the &lt;a href="https://fairphone.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Fairphone 3&lt;/a&gt; started
selling in late 2019.&lt;/p&gt;
&lt;p&gt;This is about setting up an &lt;a href="https://android.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Android&lt;/a&gt; phone with a stock OS, but
getting rid of as much of Google as possible, while still having the option
to use the &amp;ldquo;necessary&amp;rdquo; regular things.&lt;/p&gt;
&lt;p&gt;As a friend put it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;I don&amp;rsquo;t believe in free. Not anymore.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="table-of-contents"&gt;Table of Contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#hardware"&gt;Hardware&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#setting-up-the-phone"&gt;Setting up the phone&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#f-droid-the-libre-app-store"&gt;F-Droid &amp;ndash; The Libre App store&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#disabling-most-google-apps"&gt;Disabling (most) Google apps&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#disable-auto-update"&gt;Disable auto-update&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#disable-and-remove-unnecessary-google-apps"&gt;Disable and remove unnecessary Google apps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#installing-alternative-software"&gt;Installing alternative software&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#list-of-great-apps-on-f-droid"&gt;List of great apps on F-Droid&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#being-a-responsible-consumer"&gt;Being a responsible consumer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="hardware"&gt;Hardware&lt;/h1&gt;
&lt;p&gt;I went with the &lt;a href="https://fairphone.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Fairphone 3&lt;/a&gt;, one of their catchy slogans kind
of say it all:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;If your phone doesn&amp;rsquo;t care about the planet, it&amp;rsquo;s not that smart&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They care about the source of the materials and the working conditions of the
supply chain; besides that, they actively encourage electronics recycling by
taking your old phone against a voucher.&lt;/p&gt;
&lt;p&gt;Besides the ethical bits, a few great points that matter-to-me (tm) as just
a consumer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It supports dual-SIM &lt;em&gt;and&lt;/em&gt; micro-SD card simultaneously&lt;/li&gt;
&lt;li&gt;It is highly serviceable, having a perfect &lt;a href="https://ifixit.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;iFixit&lt;/a&gt; score&lt;/li&gt;
&lt;li&gt;It feels robust&lt;/li&gt;
&lt;li&gt;It does its job&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="setting-up-the-phone"&gt;Setting up the phone&lt;/h1&gt;
&lt;h2 id="f-droid-the-libre-app-store"&gt;&lt;a href="https://f-droid.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;F-Droid&lt;/a&gt; &amp;ndash; The Libre App store&lt;/h2&gt;
&lt;p&gt;Setting up &lt;a href="https://f-droid.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;F-Droid&lt;/a&gt; is about the very first thing I do on any android
device.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://f-droid.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;F-Droid&lt;/a&gt; is an alternative &amp;ldquo;app store&amp;rdquo; for &lt;a href="https://android.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Android&lt;/a&gt;, its
main features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All apps in the main repository are Free and Open Source&lt;/li&gt;
&lt;li&gt;You get warned if they contain &amp;ldquo;undesirable features&amp;rdquo; (ads, relying on
  closed-source servers, &amp;hellip;)&lt;/li&gt;
&lt;li&gt;You can host and setup your own repository.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="disabling-most-google-apps"&gt;Disabling (most) Google apps&lt;/h2&gt;
&lt;h3 id="disable-auto-update"&gt;Disable auto-update&lt;/h3&gt;
&lt;p&gt;The very first thing to do here, is to setup Google&amp;rsquo;s Play Store to &lt;strong&gt;not&lt;/strong&gt;
use automatic updates.&lt;/p&gt;
&lt;p&gt;I discovered that even if you disable apps, &lt;a href="https://android.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Android&lt;/a&gt; will &amp;ldquo;helpfully&amp;rdquo;
reinstall and enable them if there is an update available.&lt;/p&gt;
&lt;p&gt;This is done by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Opening the Play Store app&lt;/li&gt;
&lt;li&gt;Clicking the &amp;ldquo;hamburger&amp;rdquo; menu (the three horizontal lines)&lt;/li&gt;
&lt;li&gt;Picking &amp;ldquo;Settings&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Under &amp;ldquo;General&amp;rdquo; find &amp;ldquo;Automatic app updates&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Pick &amp;ldquo;Do not update apps automatically&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a necessary trade-off between: keeping apps from the Play Store
up-to-date and having Gooogle sneak their apps back in.
Which still does happen from time to time with, e.g. Chrome.&lt;/p&gt;
&lt;h3 id="disable-and-remove-unnecessary-google-apps"&gt;Disable and remove unnecessary Google apps&lt;/h3&gt;
&lt;p&gt;This is done in the phone&amp;rsquo;s settings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Settings&lt;/li&gt;
&lt;li&gt;Apps and notifications&lt;/li&gt;
&lt;li&gt;Show all apps&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once we have the list of all apps, we have to iterate through those that are
unwanted and perform following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Touch the app on the list&lt;/li&gt;
&lt;li&gt;Touch on &amp;ldquo;Deactivate&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Confirm that you want to Deactivate&lt;/li&gt;
&lt;li&gt;When asked if you want to uninstall any updates to the app, say &amp;ldquo;Yes&amp;rdquo;.
  This has the decent side-effect that you get some space back.&lt;/li&gt;
&lt;li&gt;Touch on &amp;ldquo;Force exit&amp;rdquo; and confirm&lt;/li&gt;
&lt;li&gt;Go to &amp;ldquo;Storage&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Delete data and delete cache&lt;/li&gt;
&lt;li&gt;Go back to the apps list&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And this is the list of forced-upon-me apps for which I do this; since the
&lt;a href="https://fairphone.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Fairphone 3&lt;/a&gt; does not contain crapware of their own, it&amp;rsquo;s just
Google&amp;rsquo;s:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Digital Wellbeing&lt;/li&gt;
&lt;li&gt;Drive&lt;/li&gt;
&lt;li&gt;Duo&lt;/li&gt;
&lt;li&gt;Fotos&lt;/li&gt;
&lt;li&gt;Gboard (care! Install an alternative before!)&lt;/li&gt;
&lt;li&gt;Gmail&lt;/li&gt;
&lt;li&gt;Google&lt;/li&gt;
&lt;li&gt;Google Play Films and Series&lt;/li&gt;
&lt;li&gt;Calendar&lt;/li&gt;
&lt;li&gt;Contacts&lt;/li&gt;
&lt;li&gt;Maps&lt;/li&gt;
&lt;li&gt;YouTube&lt;/li&gt;
&lt;li&gt;YouTube Music&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="installing-alternative-software"&gt;Installing alternative software&lt;/h2&gt;
&lt;p&gt;Some of the alternative software on the &lt;a href="https://f-droid.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;F-Droid&lt;/a&gt; repositories is
actually significantly better than that of Google.&lt;/p&gt;
&lt;p&gt;The best example of this would be NewPipe, a replacement for the YouTube app.
Amongst the most appreciated features from people I&amp;rsquo;ve shown this to is the
ability to play videos as a pop-up window or in the background (with the
screen off).&lt;/p&gt;
&lt;h2 id="list-of-great-apps-on-f-droid"&gt;List of great apps on &lt;a href="https://f-droid.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;F-Droid&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A quick and dirty list of apps on &lt;a href="https://f-droid.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;F-Droid&lt;/a&gt; that are a
must for me:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Alarmio: alarms, timers, &amp;hellip;&lt;/li&gt;
&lt;li&gt;andOTP (replaces Google Authenticator): second factor authentication&lt;/li&gt;
&lt;li&gt;AntennaPod: podcasts&lt;/li&gt;
&lt;li&gt;Simple Gallery (replaces Google Fotos): offline photo browser&lt;/li&gt;
&lt;li&gt;Ghost Commander: file browser&lt;/li&gt;
&lt;li&gt;K-9 Mail (replaces Gmail): E-mail client supporting encryption and most
  email providers.
  It looks like Gmail is going to stop being email, and they will stop
  supporting the standard protocols.
  So, you are forced to use Gmail if that&amp;rsquo;s where your mail is.
  Notice how &amp;ldquo;embrace extend extinguish&amp;rdquo; is not from your usual source.&lt;/li&gt;
&lt;li&gt;Simple Calendar (replaces Google Calendar): offline or CalDAV-based
  calendars.&lt;/li&gt;
&lt;li&gt;KISS Launcher (any app launcher): search-based app launcher.
  It&amp;rsquo;s simple, and zeroes the mental burden of finding the app you need.&lt;/li&gt;
&lt;li&gt;Simple Contacts (replaces Google Contacts): offline contacts.&lt;/li&gt;
&lt;li&gt;Nextcloud (Google Drive): cloud-based storage.&lt;/li&gt;
&lt;li&gt;OpenKeychain: for encryption / signature checking while on the go.&lt;/li&gt;
&lt;li&gt;PassAndroid: Manage tickets&lt;/li&gt;
&lt;li&gt;Simple Keyboard (replaces Gboard): it lets you type without having to
  correct every single word.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="being-a-responsible-consumer"&gt;Being a responsible consumer&lt;/h1&gt;
&lt;p&gt;The &lt;a href="https://f-droid.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;F-Droid&lt;/a&gt; project itself and several of these apps are
long-standing Open Source projects, with ways to donate some money to
the developers. Please do so at least for a few!&lt;/p&gt;</content></entry><entry><title>FreeBSD home router -- Legacy IPv4: NAT+DHCP</title><link href="https://evilham.eu/en/blog/2020-FreeBSD-home-router-legacy-ipv4/" rel="alternate"></link><updated>2020-01-02T00:00:00Z</updated><author><name></name></author><id>urn:uuid:6800943c-af4d-3120-9f11-d8dfbe75bd92</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;I&amp;rsquo;ve been writing about setting up a &lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;-based
&lt;a href="https://exo.cat" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;eXO&lt;/a&gt; router, first the &lt;a href="../2019-FreeBSD-eXO-router/"&gt;upstream and PPPoE details&lt;/a&gt; were covered,
then &lt;a href="../2020-FreeBSD-home-router-ipv6/"&gt;how to setup the gateway, access point and an IPv6-only network&lt;/a&gt;.
This builds upon a system that can properly route IPv6 and IPv4 as well as has a
means to connect other clients and possibly providing them with an IPv6 address
and route.&lt;/p&gt;
&lt;p&gt;The goal here is to setup the bits of the router needed for everyday usage.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is part of a series on home routing with FreeBSD.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="../2019-FreeBSD-eXO-router/"&gt;FreeBSD eXO router &amp;ndash; Get to ping6&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="../2020-FreeBSD-home-router-ipv6/"&gt;FreeBSD home router &amp;ndash; Access Point and IPv6-only&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="../2020-FreeBSD-home-router-legacy-ipv4/"&gt;FreeBSD home router &amp;ndash; Legacy IPv4: NAT+DHCP&lt;/a&gt; (this one)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h1 id="table-of-contents"&gt;Table of Contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#home-router"&gt;Home router&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#local-legacy-ipv4-networking-nat-half-rant"&gt;Local legacy IPv4 networking: NAT (half-rant)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#local-legacy-ipv4-networking-nat-implementing"&gt;Local legacy IPv4 networking: NAT (implementing)&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#dhcp-providing-the-local-ipv4-addresses"&gt;DHCP: providing the local IPv4 addresses&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#nat-with-pf-forwarding-packets-properly"&gt;NAT with pf: forwarding packets properly&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#remaining-work"&gt;Remaining work&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#internal-network-bits"&gt;Internal network bits&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="home-router"&gt;Home router&lt;/h1&gt;
&lt;p&gt;As mentioned on the &lt;a href="../2019-FreeBSD-eXO-router/"&gt;first post&lt;/a&gt;, this setup is based on
an APU2d4, with three ethernet interfaces: &lt;code&gt;igb0&lt;/code&gt; for egress/wan, &lt;code&gt;igb1&lt;/code&gt; for
local devices and, &lt;code&gt;igb2&lt;/code&gt; as a management interface.&lt;/p&gt;
&lt;p&gt;The current state is that the machine can setup the connection to &lt;a href="https://exo.cat" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;eXO&lt;/a&gt;
and can correctly accept the IPv6 and IPv4 routes and delegations, furthermore
it creates an access point &lt;code&gt;wlan0&lt;/code&gt; that is bridged to &lt;code&gt;igb1&lt;/code&gt; (bridge is called
&lt;code&gt;lair&lt;/code&gt;) and any devices that connect to these interfaces are able to use only
the IPv6 internet.&lt;/p&gt;
&lt;h1 id="local-legacy-ipv4-networking-nat-half-rant"&gt;Local legacy IPv4 networking: NAT (half-rant)&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Network_address_translation" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Network Address Translation&lt;/a&gt;, one of those things that are secretly
quite complex and a bit of a PITA to debug.&lt;/p&gt;
&lt;p&gt;The Internet was made to be End-To-End, to have all members of the network be
equal in the sense that they can provide and consume services the exact same
way [old knowledge; citation needed].&lt;/p&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Network_address_translation" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;NAT&lt;/a&gt; gets in the way of that and it was a necessary evil.
Virtually all ISPs that provide you with an internet connection, will also
assign you a single IPv4 address (either dynamically or statically), but you
surely have more than one device at any given home/company location.&lt;/p&gt;
&lt;p&gt;Since, except in very specific cases, it&amp;rsquo;s not valid for multiple devices to
work with the same IP, &lt;a href="https://en.wikipedia.org/wiki/Network_address_translation" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;NAT&lt;/a&gt; shows up everywhere.&lt;/p&gt;
&lt;p&gt;In a nutshell, &lt;a href="https://en.wikipedia.org/wiki/Network_address_translation" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;NAT&lt;/a&gt; makes sure that your &lt;code&gt;192.168.X.Y&lt;/code&gt; IPv4 doesn&amp;rsquo;t leave
the local network (because it&amp;rsquo;s &amp;ldquo;non-routable&amp;rdquo;, aka shouldn&amp;rsquo;t leave the
premises) and then connects on your behalf to whatever other peers you
requested.&lt;/p&gt;
&lt;p&gt;Then, when the answer comes back, it&amp;rsquo;s &amp;ldquo;smart enough&amp;rdquo; to know that that reply
has to go to your &lt;code&gt;192.168.X.Y&lt;/code&gt; instead of to some &lt;code&gt;192.168.W.Z&lt;/code&gt; in the
network.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s trickier than it sounds and indeed means that, without some kind of
permanent port-forwarding, it is not possible to reach devices behind the NAT
for better and for worse.&lt;/p&gt;
&lt;p&gt;Since &lt;a href="https://www.ripe.net/manage-ips-and-asns/ipv4/ipv4-run-out" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;RIPE ran out of IPv4 in 2019&lt;/a&gt;, this is specially important:
now ISPs are starting to use &lt;a href="https://en.wikipedia.org/wiki/Carrier-grade_NAT" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Carrier-Grade NAT&lt;/a&gt;, some even &amp;ldquo;sell it&amp;rdquo;
as a feature because it means they can give you a crappier internet at lower
prices.&lt;/p&gt;
&lt;p&gt;Crappier internet? Well, if you or someone you know/trust manage your NAT,
it is possible to do things like port-forwarding, if in reality, your NAT
is behind another NAT that hosts thousands of customers; there is no real
option for that.&lt;/p&gt;
&lt;p&gt;Random trivia: The Nintendo Switch will happily tell you how crappy your NAT
is :-) on a scale from &amp;ldquo;A: mostly works&amp;rdquo; to &amp;ldquo;F: how did this happen?&amp;rdquo;.
&lt;a href="https://en.wikipedia.org/wiki/Carrier-grade_NAT" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;CGNAT&lt;/a&gt; will automatically place you somewhere between &amp;ldquo;C&amp;rdquo; and &amp;ldquo;D&amp;rdquo;.&lt;/p&gt;
&lt;h1 id="local-legacy-ipv4-networking-nat-implementing"&gt;Local legacy IPv4 networking: NAT (implementing)&lt;/h1&gt;
&lt;p&gt;So, &lt;a href="https://exo.cat" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;eXO&lt;/a&gt; is in this regard only different, in that they assign a static
IPv4 address to the connection that is directly addressable by any device
connected to the IPv4 internet.&lt;/p&gt;
&lt;p&gt;Which means, I do need &lt;a href="https://en.wikipedia.org/wiki/Network_address_translation" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;NAT&lt;/a&gt; if I want IPv4 support.&lt;/p&gt;
&lt;p&gt;Side note for some other time: &lt;a href="https://en.wikipedia.org/wiki/NAT64" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;NAT64&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/IPv6_transition_mechanism#DNS64" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;DNS64&lt;/a&gt; make it
mostly unnecessary to have IPv4 (and &lt;a href="https://en.wikipedia.org/wiki/Network_address_translation" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;NAT&lt;/a&gt;!) in the first place.
This has been tested out successfully at length by many, but specially by
the awesome people at &lt;a href="https://ungleich.ch" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;ungleich.ch&lt;/a&gt; at last &lt;a href="https://hack4glarus.ch" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Hack4Glarus&lt;/a&gt;
and with their &lt;a href="https://ipv6onlyhosting.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;IPv6 only hosting&lt;/a&gt; Virtual Private Server
(VPS) offering.&lt;/p&gt;
&lt;h2 id="dhcp-providing-the-local-ipv4-addresses"&gt;DHCP: providing the local IPv4 addresses&lt;/h2&gt;
&lt;p&gt;Before actually translating addresses, we have to provide those addresses
to devices in the network. That&amp;rsquo;s what DHCP is good for.&lt;/p&gt;
&lt;p&gt;The along with the DHCP6 client (&lt;code&gt;dhcp6c&lt;/code&gt;), DHCP server is the second bit that
we need that is not part of the base system.&lt;/p&gt;
&lt;p&gt;In this case, I&amp;rsquo;ll use &lt;a href="https://openbsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;OpenBSD&lt;/a&gt;&amp;lsquo;s &lt;code&gt;dhcpd&lt;/code&gt;, which is packaged as a
port for &lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As with &lt;code&gt;dhcp6c&lt;/code&gt;, installing &lt;code&gt;dhcpd&lt;/code&gt; is a matter of:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pkg install dhcpd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And the magical man pages are &lt;code&gt;man 5 dhcpd.conf&lt;/code&gt; and &lt;code&gt;man 8 dhcpd&lt;/code&gt; and, even if
it is focused on &lt;code&gt;isc-dhcpd-server&lt;/code&gt;, the
&lt;a href="https://www.freebsd.org/doc/en/books/handbook/network-dhcp.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Chapter on DHCP from the FreeBSD Handbook&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Familiar pattern by now, setup &lt;code&gt;/usr/local/etc/dhcpd.conf&lt;/code&gt; (since &lt;code&gt;dhcpd&lt;/code&gt; is
not part of the base system, its config lives in &lt;code&gt;/usr/local/etc&lt;/code&gt;) as follows:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;#&lt;span class="w"&gt; &lt;/span&gt;Add&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;suffix&lt;span class="w"&gt; &lt;/span&gt;for&lt;span class="w"&gt; &lt;/span&gt;local&lt;span class="w"&gt; &lt;/span&gt;name&lt;span class="w"&gt; &lt;/span&gt;queries
option&lt;span class="w"&gt; &lt;/span&gt;domain-name&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;evilham.local&amp;quot;;
#&lt;span class="w"&gt; &lt;/span&gt;TODO:&lt;span class="w"&gt; &lt;/span&gt;change&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;own&lt;span class="w"&gt; &lt;/span&gt;DNS.&lt;span class="w"&gt; &lt;/span&gt;Using&lt;span class="w"&gt; &lt;/span&gt;quad9&lt;span class="w"&gt; &lt;/span&gt;for&lt;span class="w"&gt; &lt;/span&gt;now
option&lt;span class="w"&gt; &lt;/span&gt;domain-name-servers&lt;span class="w"&gt; &lt;/span&gt;9.9.9.9,&lt;span class="w"&gt; &lt;/span&gt;114.112.112.112;

#&lt;span class="w"&gt; &lt;/span&gt;Default&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;1&lt;span class="w"&gt; &lt;/span&gt;day&lt;span class="w"&gt; &lt;/span&gt;lease
default-lease-time&lt;span class="w"&gt; &lt;/span&gt;86400;
#&lt;span class="w"&gt; &lt;/span&gt;Limit&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;lease&lt;span class="w"&gt; &lt;/span&gt;to,&lt;span class="w"&gt; &lt;/span&gt;say&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;month&lt;span class="w"&gt; &lt;/span&gt;if&lt;span class="w"&gt; &lt;/span&gt;someone
#&lt;span class="w"&gt; &lt;/span&gt;requests&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;longer&lt;span class="w"&gt; &lt;/span&gt;lease.
max-lease-time&lt;span class="w"&gt; &lt;/span&gt;2592000;

#&lt;span class="w"&gt; &lt;/span&gt;2^16&lt;span class="w"&gt; &lt;/span&gt;should&lt;span class="w"&gt; &lt;/span&gt;be&lt;span class="w"&gt; &lt;/span&gt;enough&lt;span class="w"&gt; &lt;/span&gt;addresses&lt;span class="w"&gt; &lt;/span&gt;for&lt;span class="w"&gt; &lt;/span&gt;everyone&lt;span class="w"&gt; &lt;/span&gt;(&amp;#39;&lt;span class="w"&gt; &lt;/span&gt;^.^)
option&lt;span class="w"&gt; &lt;/span&gt;subnet-mask&lt;span class="w"&gt; &lt;/span&gt;255.255.0.0;

#&lt;span class="w"&gt; &lt;/span&gt;This&lt;span class="w"&gt; &lt;/span&gt;will&lt;span class="w"&gt; &lt;/span&gt;have&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;be&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;internal&lt;span class="w"&gt; &lt;/span&gt;IPv4&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;router
option&lt;span class="w"&gt; &lt;/span&gt;routers&lt;span class="w"&gt; &lt;/span&gt;192.168.0.1;

subnet&lt;span class="w"&gt; &lt;/span&gt;192.168.0.0&lt;span class="w"&gt; &lt;/span&gt;netmask&lt;span class="w"&gt; &lt;/span&gt;255.255.0.0&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt;  &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;I&lt;span class="w"&gt; &lt;/span&gt;want&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;reserve&amp;quot;&lt;span class="w"&gt; &lt;/span&gt;some&lt;span class="w"&gt; &lt;/span&gt;addresses&lt;span class="w"&gt; &lt;/span&gt;for&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;reasons&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;range&lt;span class="w"&gt; &lt;/span&gt;192.168.77.0&lt;span class="w"&gt; &lt;/span&gt;192.168.99.255;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And setup &lt;code&gt;/etc/rc.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Modify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;existing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IPv4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;subnet&lt;/span&gt;
&lt;span class="nx"&gt;ifconfig_lair&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;inet 192.168.0.1/16 addm igb1 addm wlan0&amp;quot;&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;giving&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IPv4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;addresses&lt;/span&gt;
&lt;span class="nx"&gt;dhcpd_enable&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;YES&amp;quot;&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;And&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;explicitly&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;locally&lt;/span&gt;
&lt;span class="nx"&gt;dhcpd_flags&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;lair&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then start the &lt;code&gt;dhcpd&lt;/code&gt; service:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;service dhcpd start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now any device connected to the bridge interface will get an IPv4 address \o/.&lt;/p&gt;
&lt;p&gt;BUT! We have no NAT yet, so, things will get weird soon! (*)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;(*): they got weirder than expected, &lt;code&gt;ppp&lt;/code&gt; was doing NAT?&lt;/p&gt;
&lt;p&gt;That doesn&amp;rsquo;t sound right :-D. Let&amp;rsquo;s trust &lt;code&gt;pf&lt;/code&gt; more than &lt;code&gt;ppp&lt;/code&gt; for that job.&lt;/p&gt;
&lt;p&gt;Modified &lt;code&gt;/etc/ppp/ppp.conf&lt;/code&gt; so that the &lt;code&gt;exo&lt;/code&gt; profile also contains a line:
&lt;code&gt;nat enable no&lt;/code&gt;
This should have been the default if I understood documentation properly.&lt;/p&gt;
&lt;p&gt;TODO: Figure out why this was the default behaviour and fix
documentation/open a bug&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Besides talking to our local network, these addresses are not good for
anything. Since they are not routable, any routers must refuse to forward the
packets any further.&lt;/p&gt;
&lt;h2 id="nat-with-pf-forwarding-packets-properly"&gt;NAT with pf: forwarding packets properly&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; has two main firewalls, I like &lt;code&gt;pf&lt;/code&gt;.
It originated in &lt;a href="https://openbsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;OpenBSD&lt;/a&gt; like so many great things, and was ported
to &lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=RYtwx7Jj8aE" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Kristof Provost&amp;rsquo;s talk&lt;/a&gt; on automated firewall testing, explains a bit how
it came to be that &lt;a href="https://openbsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;OpenBSD&lt;/a&gt;&amp;lsquo;s &lt;code&gt;pf&lt;/code&gt; and &lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;&amp;lsquo;s &lt;code&gt;pf&lt;/code&gt; are
actually different.
The TL;DR is: nobody has done the work to upgrade it, it works mostly fine, and
the diff is not as small as one would expect.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://openbsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;OpenBSD&lt;/a&gt;&amp;lsquo;s &lt;code&gt;pf&lt;/code&gt; has a bunch of goodies, like support for &lt;code&gt;NAT64&lt;/code&gt; :-).
Hopefully things catch up soon.&lt;/p&gt;
&lt;p&gt;Back to topic: &lt;code&gt;man 4 pf&lt;/code&gt;, &lt;code&gt;man 5 pf.conf&lt;/code&gt; and &lt;code&gt;man 8 pfctl&lt;/code&gt; are the magical
man pages.&lt;/p&gt;
&lt;p&gt;And a super-tiny but permissive example on how to do NAT was already done by
&lt;a href="https://kamila.is" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Kamila&lt;/a&gt; &lt;a href="https://kamila.is/learning/building-my-home-router/#basic-router-setup-gateway--nat-for-v4" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;here&lt;/a&gt;, so go steal that into &lt;code&gt;/etc/pf.conf&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With an important addition for &lt;code&gt;dhcp6c&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TODO&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;provisioning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;In&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;practise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;should&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;limited&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;addresses&lt;/span&gt;
&lt;span class="nx"&gt;pass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;quick&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;proto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;udp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dhcpv6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we have to apply those rules, since I was already loading &lt;code&gt;pf&lt;/code&gt;, albeit with
an empty set of rules, this means running:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And boom, now everything in the network can talk to the outside over IPv4.&lt;/p&gt;
&lt;h1 id="remaining-work"&gt;Remaining work&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Finish securing the firewall with &lt;code&gt;pf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Create and publish &lt;a href="https://cdi.st" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;cdist&lt;/a&gt; types for all the things&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="internal-network-bits"&gt;Internal network bits&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Monitoring (Prometheus)&lt;/li&gt;
&lt;li&gt;DNS (unbound)&lt;/li&gt;
&lt;li&gt;Traffic dashboards (taking into account the percentiles)&lt;/li&gt;
&lt;/ul&gt;</content></entry><entry><title>FreeBSD home router -- Access Point and IPv6-only</title><link href="https://evilham.eu/en/blog/2020-FreeBSD-home-router-ipv6/" rel="alternate"></link><updated>2020-01-01T00:00:00Z</updated><author><name></name></author><id>urn:uuid:a7e2dd78-93c4-3940-ab0f-e7bd31b6396c</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;&lt;a href="../2019-FreeBSD-eXO-router/"&gt;Last time&lt;/a&gt; I wrote about setting up a &lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;-based
&lt;a href="https://exo.cat" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;eXO&lt;/a&gt; router, this builds upon a system that can correctly &lt;code&gt;ping&lt;/code&gt; and
&lt;code&gt;ping6&lt;/code&gt; to the internet.&lt;/p&gt;
&lt;p&gt;The goal here is to setup the bits of the router needed for everyday usage.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is part of a series on home routing with FreeBSD.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="../2019-FreeBSD-eXO-router/"&gt;FreeBSD eXO router &amp;ndash; Get to ping6&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="../2020-FreeBSD-home-router-ipv6/"&gt;FreeBSD home router &amp;ndash; Access Point and IPv6-only&lt;/a&gt; (this one)&lt;/li&gt;
&lt;li&gt;&lt;a href="../2020-FreeBSD-home-router-legacy-ipv4/"&gt;FreeBSD home router &amp;ndash; Legacy IPv4: NAT+DHCP&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h1 id="table-of-contents"&gt;Table of Contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#home-router"&gt;Home router&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#create-the-bridge"&gt;Create the bridge&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#setup-the-wireless-network"&gt;Setup the wireless network&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#become-a-gateway"&gt;Become a gateway&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#provide-ipv6-connectivity-with-slaac"&gt;Provide IPv6 connectivity with SLAAC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#secure-the-firewall"&gt;Secure the firewall&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#remaining-work"&gt;Remaining work&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#legacy-ipv4"&gt;Legacy IPv4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#internal-network-bits"&gt;Internal network bits&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="home-router"&gt;Home router&lt;/h1&gt;
&lt;p&gt;As mentioned on the &lt;a href="../2019-FreeBSD-eXO-router/"&gt;previous post&lt;/a&gt;, this setup is based on
an APU2d4, with three ethernet interfaces: &lt;code&gt;igb0&lt;/code&gt; for egress/wan, &lt;code&gt;igb1&lt;/code&gt; for
local devices and, &lt;code&gt;igb2&lt;/code&gt; as a management interface.&lt;/p&gt;
&lt;p&gt;The current state is that the machine can setup the connection to &lt;a href="https://exo.cat" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;eXO&lt;/a&gt;
and can correctly accept the IPv6 and IPv4 routes and delegations, and is
able to &lt;code&gt;ping&lt;/code&gt; and &lt;code&gt;ping6&lt;/code&gt; devices on the internet.&lt;/p&gt;
&lt;p&gt;This is similar to what &lt;a href="https://kamila.is" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Kamila&lt;/a&gt; did with &lt;a href="https://kamila.is/learning/building-my-home-router/#installation" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;her router&lt;/a&gt;,
but has a few twists and only focuses on the home router bits, leaving the
&lt;a href="https://kamila.is/learning/building-my-home-router/#installation" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;installation and 3G&lt;/a&gt; and &lt;a href="../2019-FreeBSD-eXO-router/"&gt;PPPoE&lt;/a&gt; bits somewhere
else.&lt;/p&gt;
&lt;h2 id="create-the-bridge"&gt;Create the bridge&lt;/h2&gt;
&lt;p&gt;Creating network bridges on &lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; is pretty straightforward with
&lt;code&gt;man 8 ifconfig&lt;/code&gt; and &lt;code&gt;man 5 rc.conf&lt;/code&gt; being the magical man pages.&lt;/p&gt;
&lt;p&gt;To do it once:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# Create the bridge with a deterministic name
ifconfig bridge create name lair
# Add the necessary interfaces
ifconfig lair addm igb1 addm wlan0 inet6 auto_linklocal
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To do it permanently, add in &lt;code&gt;/etc/rc.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;cloned_interfaces&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;bridge0&amp;quot;&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;deterministic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
&lt;span class="nx"&gt;ifconfig_bridge0_name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;lair&amp;quot;&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;necessary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;interfaces&lt;/span&gt;
&lt;span class="nx"&gt;ifconfig_lair&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;addm igb1 addm wlan0&amp;quot;&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;While&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ensure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;we&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;
&lt;span class="nx"&gt;ifconfig_lair_ipv6&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;auto_linklocal&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="setup-the-wireless-network"&gt;Setup the wireless network&lt;/h2&gt;
&lt;p&gt;This is done with &lt;code&gt;hostapd&lt;/code&gt;, which is also in the base system.
The magical man pages for this are &lt;code&gt;man 8 hostapd&lt;/code&gt; and &lt;code&gt;man 5 hostapd.conf&lt;/code&gt;
as well as &lt;code&gt;man 5 rc.conf&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;First of all, we setup things in &lt;code&gt;/etc/rc.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Setup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wireless&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;interface&lt;/span&gt;
&lt;span class="n"&gt;wlans_ath0&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;wlan0&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;ifconfig_wlan0&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;HOSTAP&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Respect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;regulations&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;
&lt;span class="n"&gt;create_args_wlan0&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;wlanmode hostap country ES&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Enable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;access&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;daemon&lt;/span&gt;
&lt;span class="n"&gt;hostapd_enable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;YES&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, we create &lt;code&gt;/etc/hostapd-wlan0.conf&lt;/code&gt; as follows:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;interface&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;wlan0&lt;/span&gt;
&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;ctrl_interface&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;hostapd&lt;/span&gt;
&lt;span class="n"&gt;ctrl_interface_group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;wheel&lt;/span&gt;
&lt;span class="n"&gt;ssid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;eXO&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;wlan&lt;/span&gt;
&lt;span class="n"&gt;wpa&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;wpa_passphrase&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;WirelessPassword&lt;/span&gt;
&lt;span class="n"&gt;wpa_key_mgmt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;WPA&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;PSK&lt;/span&gt;
&lt;span class="n"&gt;wpa_pairwise&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CCMP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="become-a-gateway"&gt;Become a gateway&lt;/h2&gt;
&lt;p&gt;Now most of the things are in place in order to forward traffic for other
clients in the local network.&lt;/p&gt;
&lt;p&gt;A very important step that is missing is becoming a gateway!
Here also &lt;code&gt;man 5 rc.conf&lt;/code&gt; is a magical man page.&lt;/p&gt;
&lt;p&gt;Add following to &lt;code&gt;/etc/rc.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Act&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;gateway&lt;/span&gt;
&lt;span class="nv"&gt;gateway_enable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;YES&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;ipv6_gateway_enable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;YES&amp;quot;&lt;/span&gt;
#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Setup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dependent&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Accept&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Route&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Advertisements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;even&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;with&lt;/span&gt;
#&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;IPv6&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;forwarding&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;enabled&lt;/span&gt;.
#&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;In&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;my&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;case&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;needed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DHCP6&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;used&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;prefix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;delegation&lt;/span&gt;.
#&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;should&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;interface&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ISP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;doing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;RA&lt;/span&gt;.
#&lt;span class="nv"&gt;ipv6_cpe_wanif&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;exoppp&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is effective after a reboot, using &lt;code&gt;sysctl&lt;/code&gt; and so on is possible, but&amp;hellip;&lt;/p&gt;
&lt;h2 id="provide-ipv6-connectivity-with-slaac"&gt;Provide IPv6 connectivity with SLAAC&lt;/h2&gt;
&lt;p&gt;This is also done with the base system, in this case &lt;code&gt;man 5 rtadvd.conf&lt;/code&gt;,
&lt;code&gt;man 5 rc.conf&lt;/code&gt; and &lt;code&gt;man 8 rtadvd&lt;/code&gt; are the magic man pages.&lt;/p&gt;
&lt;p&gt;It further follows the pattern, we setup &lt;code&gt;/etc/rc.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;rtadvd_enable=&amp;quot;YES&amp;quot;
rtadvd_interfaces=&amp;quot;lair&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And the software in &lt;code&gt;/etc/rtadvd.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# rtadvd will try to get the data from the kernel&amp;#39;s routing table
# TODO: use own DNS servers, this uses quad9.
lair:\
    :mininterval#5:\
    :maxinterval#10:\
    :rdnss=&amp;quot;2620:fe::fe,2620:fe::9&amp;quot;\
    :mtu#auto:\
    :rltime#60:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then start it with&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;service rtadvd start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At this point we also modify &lt;code&gt;/usr/local/etc/dhcp6c.conf&lt;/code&gt; to assign a
delegated &lt;code&gt;/64&lt;/code&gt; to the &lt;code&gt;lair&lt;/code&gt; interface as opposed to the &lt;code&gt;igb1&lt;/code&gt; one.
See &lt;a href="../2019-FreeBSD-eXO-router/"&gt;previous post&lt;/a&gt; for the base config.&lt;/p&gt;
&lt;p&gt;Now, when another system connects to the &lt;code&gt;lair&lt;/code&gt; bridge (that includes the
access point on &lt;code&gt;wlan0&lt;/code&gt;), it will automagically receive the &lt;code&gt;/64&lt;/code&gt; prefix
information and derive its addresses as necessary (temporary, non-temporary,
random, it&amp;rsquo;s all black magic).&lt;/p&gt;
&lt;p&gt;BUT! Keep on reading, this is important!&lt;/p&gt;
&lt;h2 id="secure-the-firewall"&gt;Secure the firewall&lt;/h2&gt;
&lt;p&gt;Up until now only our router was accessible from the internet without a
firewall, but it also had no services running (besides SSH with Public Key
based authentication); with the previous step though, besides providing
internet to the network, we provide our internal network to the internet :-).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Remember that up until now &lt;code&gt;/etc/pf.conf&lt;/code&gt; was empty.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;My firewall rules are non-trivial and keep evolving, so they won&amp;rsquo;t be
replicated here, instead there will be some &lt;a href="https://cdi.st" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;cdist&lt;/a&gt; type with them
soon.&lt;/p&gt;
&lt;p&gt;Needless to say, &lt;code&gt;man pf.conf&lt;/code&gt;, &lt;code&gt;man pf&lt;/code&gt; and &lt;code&gt;man pfctl&lt;/code&gt; are the magical
man pages and block all the things.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;TODO: publish the &lt;a href="https://cdi.st" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;cdist&lt;/a&gt; types and link them here&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="remaining-work"&gt;Remaining work&lt;/h1&gt;
&lt;h2 id="legacy-ipv4"&gt;Legacy IPv4&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Setup &lt;code&gt;dhcpd&lt;/code&gt; to hand out local IPv4s&lt;/li&gt;
&lt;li&gt;Setup NAT&lt;/li&gt;
&lt;li&gt;Create &lt;a href="https://cdi.st" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;cdist&lt;/a&gt; types for all the things&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="internal-network-bits"&gt;Internal network bits&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Monitoring (Prometheus)&lt;/li&gt;
&lt;li&gt;DNS (unbound)&lt;/li&gt;
&lt;li&gt;Traffic dashboards (taking into account the percentiles)&lt;/li&gt;
&lt;/ul&gt;</content></entry><entry><title>FreeBSD eXO router -- Get to ping6</title><link href="https://evilham.eu/en/blog/2019-FreeBSD-eXO-router/" rel="alternate"></link><updated>2019-12-31T00:00:00Z</updated><author><name></name></author><id>urn:uuid:dd2df2eb-8bb8-3b0c-8031-ed91ea7d037e</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://exo.cat" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;eXO&lt;/a&gt; &amp;ndash; expansió Xarxa Oberta (open network expansion) is an non-profit
association based in Barcelona and since 2019, it&amp;rsquo;s also a &lt;a href="https://www.ripe.net" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;RIPE&lt;/a&gt; member.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://exo.cat" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;eXO&lt;/a&gt; […] promotes open telecommunication networks,
technological sovereignty, access to wholesale Internet services and reduces
the digital gap.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They are also pretty much the only decent way to access native
&lt;a href="https://ipv6.barcelona" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;IPv6 in Barcelona&lt;/a&gt; and since &lt;a href="https://www.ripe.net/manage-ips-and-asns/ipv4/ipv4-run-out" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;RIPE ran out of IPv4&lt;/a&gt; in 2019,
it&amp;rsquo;s also pretty much the only decent Internet Service Provider around.&lt;/p&gt;
&lt;p&gt;Also, the fact that you are not a customer but a member who can make things
work out if necessary, kicks ass over &lt;code&gt;&amp;lt;rant&amp;gt;&lt;/code&gt; Customer Service that keeps you
waiting for hours and then insults your time and intelligence, or a service
that forces you to use crappy routers, uses &lt;a href="https://en.wikipedia.org/wiki/Carrier-grade_NAT" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;CGNAT&lt;/a&gt; or one that
basically mobs you into joining them by aggressively calling you multiple times
a week.
(Who does these terrible things? Search for &amp;ldquo;biggest ISPs in Spain&amp;rdquo;)
&lt;code&gt;&amp;lt;/rant&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Also: you get to pick your router and, besides something running
&lt;a href="https://openwrt.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;OpenWRT&lt;/a&gt; (default), do it your self and e.g. run &lt;a href="https://openbsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;OpenBSD&lt;/a&gt;
or &lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is roughly about how internet with &lt;a href="https://exo.cat" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;eXO&lt;/a&gt; works and mainly about how
to setup a &lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; system as a router for it.&lt;/p&gt;
&lt;p&gt;PS: &lt;a href="https://exo.cat" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;eXO&lt;/a&gt; does more than just fiber, there are also community-owned
wireless mesh networks, but that&amp;rsquo;s for another day.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is part of a series on home routing with FreeBSD.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="../2019-FreeBSD-eXO-router/"&gt;FreeBSD eXO router &amp;ndash; Get to ping6&lt;/a&gt; (this one)&lt;/li&gt;
&lt;li&gt;&lt;a href="../2020-FreeBSD-home-router-ipv6/"&gt;FreeBSD home router &amp;ndash; Access Point and IPv6-only&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="../2020-FreeBSD-home-router-legacy-ipv4/"&gt;FreeBSD home router &amp;ndash; Legacy IPv4: NAT+DHCP&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h1 id="table-of-contents"&gt;Table of Contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#hardware"&gt;Hardware&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#fiber-network"&gt;Fiber network&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#going-ethernet"&gt;Going Ethernet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#router"&gt;Router&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#software-and-installation"&gt;Software and installation&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#hardware-specific-bits"&gt;Hardware-specific bits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#external-network-bits"&gt;External network bits&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#wholesale-provider-requirements-pcp-vlan"&gt;Wholesale provider requirements (PCP + VLAN)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#pppoe"&gt;PPPoE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#ipv6-connectivity"&gt;IPv6 connectivity&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#remaining-work"&gt;Remaining work&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#optional"&gt;Optional&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#internal-network-bits"&gt;Internal network bits&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="hardware"&gt;Hardware&lt;/h1&gt;
&lt;h2 id="fiber-network"&gt;Fiber network&lt;/h2&gt;
&lt;p&gt;Since Barcelona is one of those regions, particularly cities, that missed the
&amp;ldquo;let&amp;rsquo;s own our digital infrastructure&amp;rdquo; wagon, and it&amp;rsquo;s prohibitively expensive
to install your own, the physical layer gets leased from a wholesale provider
which, in turn, pays to the owner of the network (see &amp;ldquo;biggest ISPs in Spain&amp;rdquo;)
for its use.&lt;/p&gt;
&lt;p&gt;Depending on where in the metropolitan area, that provider changes, in the case
of the city of Barcelona, it&amp;rsquo;s Sarenet.&lt;/p&gt;
&lt;p&gt;Once Sarenet has done its job (which was extremely quick!), there is an optical
fiber cable arriving at its future usage location, &lt;a href="https://exo.cat" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;eXO&lt;/a&gt; gets notified and
receives some details like the connection&amp;rsquo;s required VLAN and they also setup
users and assign a fixed IP block from their pool to the connection.&lt;/p&gt;
&lt;h2 id="going-ethernet"&gt;Going Ethernet&lt;/h2&gt;
&lt;p&gt;Before we can use our regular router, an &lt;a href="https://en.wikipedia.org/wiki/Network_interface_device#Optical_network_terminals" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Optical Network Terminal&lt;/a&gt; is
needed.
In this case &lt;a href="https://exo.cat" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;eXO&lt;/a&gt; offered me a &lt;a href="https://e.huawei.com/uk/products/enterprise-transmission-access/access/ont/echolife-eg8010h" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Huawei EchoLife EG8010H&lt;/a&gt;
which is kind of cute and does the job. Plus side: now I &lt;em&gt;own&lt;/em&gt; the li&amp;rsquo;l box
(no BS equipment rental or crappy hardware).&lt;/p&gt;
&lt;h2 id="router"&gt;Router&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m re-purposing electronics, whose intended initial use has developed beyond
its capabilities.&lt;/p&gt;
&lt;p&gt;In this case, that&amp;rsquo;s an APU2d4 from &lt;a href="https://pcengines.ch" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;PCEngines&lt;/a&gt;, with a 1T mSATA
drive for home storage and booting off a 32G SD card.
Some more drives will be attached but are not relevant.&lt;/p&gt;
&lt;p&gt;I decided to boot off a 32G SD card, because if anything happens, I can have
it back up in no time.
And it slightly simplifies the management of the data drive.&lt;/p&gt;
&lt;h1 id="software-and-installation"&gt;Software and installation&lt;/h1&gt;
&lt;p&gt;Since, because of the somewhat large mSATA, I&amp;rsquo;ll be using the APU as home
storage as well, I will prefer &lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; over &lt;a href="https://openbsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;OpenBSD&lt;/a&gt;
for this use-case, as the former will give me the wonders of ZFS, minus
some of the easy routing awesomeness of the latter, while still being good
at it.&lt;/p&gt;
&lt;h2 id="hardware-specific-bits"&gt;Hardware-specific bits&lt;/h2&gt;
&lt;p&gt;Installing &lt;a href="https://freebsd.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD&lt;/a&gt; on a &lt;a href="https://pcengines.ch" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;PCEngines&lt;/a&gt; APU2 is somewhat easy
with the right equipment and a couple important tips.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://kamila.is" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Kamila&lt;/a&gt; covered that when she did her router, so do
&lt;a href="https://kamila.is/learning/building-my-home-router/#installation" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;read that&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once the OS is installed and before rebooting into the newly installed system,
I prepare a few things for provisioning over local network:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Setup my &lt;code&gt;root&lt;/code&gt; SSH key in &lt;code&gt;/root/.ssh/authorized_keys&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Ensure password logins are disabled in SSH&lt;/li&gt;
&lt;li&gt;Enable &lt;code&gt;root&lt;/code&gt; login over SSH&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;pf_enable="YES"&lt;/code&gt; to &lt;code&gt;/etc/rc.conf&lt;/code&gt; and &lt;code&gt;kldload pf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Create an empty &lt;code&gt;/etc/pf.conf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Make sure things like &lt;code&gt;sendmail&lt;/code&gt; and the like are fully disabled&lt;/li&gt;
&lt;li&gt;I make sure I can use the router as a client over a local network with&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# In /etc/rc.conf
ifconfig_igb2=&amp;quot;DHCP&amp;quot;
ifconfig_igb2_ipv6=&amp;quot;inet6 accept_rtadv&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="external-network-bits"&gt;External network bits&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve been switching to &lt;a href="https://cdi.st" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;cdist&lt;/a&gt; for provisioning, so this might be easier
for you once I finish publishing the types.&lt;/p&gt;
&lt;p&gt;Since I&amp;rsquo;ll be doing the provisioning over local network on &lt;code&gt;igb2&lt;/code&gt; (mangement),
&lt;code&gt;igb0&lt;/code&gt; is assumed to be the egress, wan interface and &lt;code&gt;igb1&lt;/code&gt; will be treated as
a client-facing interface.&lt;/p&gt;
&lt;h3 id="wholesale-provider-requirements-pcp-vlan"&gt;Wholesale provider requirements (PCP + VLAN)&lt;/h3&gt;
&lt;p&gt;The wholesale provider requires a specific IEEE 802.1p Priority Code Point
and VLAN for the connection.&lt;/p&gt;
&lt;p&gt;The magical man pages are &lt;code&gt;man 8 ifconfig&lt;/code&gt; and &lt;code&gt;man 4 vlan&lt;/code&gt; as well as
the &lt;a href="https://www.freebsd.org/doc/handbook/network-vlan.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Chapter on VLANs from the FreeBSD Handbook&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is achieved one time with:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# VLAN 24 -- Called exovlan because of ppp
# We also setup IEEE 802.1p priority code point
ifconfig igb0.24 create name exovlan vlan 24 vlanpcp 3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And permanently by adding following to &lt;code&gt;/etc/rc.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# Wholesale provider requirements
#   Create VLAN inteface
#   It doesn&amp;#39;t look like ppp likes dots in interfaces, let&amp;#39;s give this
#   interface a friendly name.
vlans_igb0=&amp;quot;exovlan&amp;quot;
#   Setup the VLAN number
create_args_exovlan=&amp;quot;vlan 24&amp;quot;
#   IEEE 802.1p priority code point
ifconfig_exovlan=&amp;quot;vlanpcp 3&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="pppoe"&gt;PPPoE&lt;/h3&gt;
&lt;p&gt;This is what is actually used by &lt;a href="https://exo.cat" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;eXO&lt;/a&gt; to provide the service.&lt;/p&gt;
&lt;p&gt;The magical man pages are &lt;code&gt;man 8 ppp&lt;/code&gt; and the
&lt;a href="https://www.freebsd.org/doc/handbook/ppp-and-slip.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Chapter on PPP from the FreeBSD Handbook&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And this is what the resulting &lt;code&gt;/etc/ppp/ppp.conf&lt;/code&gt; looks like:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;default:
&lt;span class="w"&gt;  &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;An&lt;span class="w"&gt; &lt;/span&gt;address&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;required
&lt;span class="w"&gt;  &lt;/span&gt;set&lt;span class="w"&gt; &lt;/span&gt;ifaddr&lt;span class="w"&gt; &lt;/span&gt;0&lt;span class="w"&gt; &lt;/span&gt;0&lt;span class="w"&gt; &lt;/span&gt;255.255.255.255

exo:
&lt;span class="w"&gt;  &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;I&amp;#39;ll&lt;span class="w"&gt; &lt;/span&gt;be&lt;span class="w"&gt; &lt;/span&gt;using&lt;span class="w"&gt; &lt;/span&gt;my&lt;span class="w"&gt; &lt;/span&gt;own&lt;span class="w"&gt; &lt;/span&gt;DNS,&lt;span class="w"&gt; &lt;/span&gt;otherwise&lt;span class="w"&gt; &lt;/span&gt;use&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;enable&lt;span class="w"&gt; &lt;/span&gt;dns&amp;quot;
&lt;span class="w"&gt;  &lt;/span&gt;disable&lt;span class="w"&gt; &lt;/span&gt;dns
&lt;span class="w"&gt;  &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;Reminder&lt;span class="w"&gt; &lt;/span&gt;that&lt;span class="w"&gt; &lt;/span&gt;ppp&lt;span class="w"&gt; &lt;/span&gt;does&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;like&lt;span class="w"&gt; &lt;/span&gt;dots&lt;span class="w"&gt; &lt;/span&gt;in&lt;span class="w"&gt; &lt;/span&gt;interface&lt;span class="w"&gt; &lt;/span&gt;names
&lt;span class="w"&gt;  &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;so&lt;span class="w"&gt; &lt;/span&gt;we&lt;span class="w"&gt; &lt;/span&gt;created&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;VLAN&lt;span class="w"&gt; &lt;/span&gt;24&lt;span class="w"&gt; &lt;/span&gt;as&lt;span class="w"&gt; &lt;/span&gt;exovlan&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;igb0.24
&lt;span class="w"&gt;  &lt;/span&gt;set&lt;span class="w"&gt; &lt;/span&gt;device&lt;span class="w"&gt; &lt;/span&gt;PPPoE:exovlan
&lt;span class="w"&gt;  &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;Set&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;deterministic&lt;span class="w"&gt; &lt;/span&gt;name
&lt;span class="w"&gt;  &lt;/span&gt;iface&lt;span class="w"&gt; &lt;/span&gt;name&lt;span class="w"&gt; &lt;/span&gt;exoppp
&lt;span class="w"&gt;  &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;Replace&lt;span class="w"&gt; &lt;/span&gt;with&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;own&lt;span class="w"&gt; &lt;/span&gt;data
&lt;span class="w"&gt;  &lt;/span&gt;set&lt;span class="w"&gt; &lt;/span&gt;authname&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;EXO_USER&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;set&lt;span class="w"&gt; &lt;/span&gt;authkey&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;EXO_PASS&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;set&lt;span class="w"&gt; &lt;/span&gt;dial
&lt;span class="w"&gt;  &lt;/span&gt;set&lt;span class="w"&gt; &lt;/span&gt;login
&lt;span class="w"&gt;  &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;Use&lt;span class="w"&gt; &lt;/span&gt;LQR&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;echo
&lt;span class="w"&gt;  &lt;/span&gt;set&lt;span class="w"&gt; &lt;/span&gt;echoperiod&lt;span class="w"&gt; &lt;/span&gt;15
&lt;span class="w"&gt;  &lt;/span&gt;set&lt;span class="w"&gt; &lt;/span&gt;lqrperiod&lt;span class="w"&gt; &lt;/span&gt;15
&lt;span class="w"&gt;  &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;mssfixup&lt;span class="w"&gt; &lt;/span&gt;should&lt;span class="w"&gt; &lt;/span&gt;be&lt;span class="w"&gt; &lt;/span&gt;enabled&lt;span class="w"&gt; &lt;/span&gt;by&lt;span class="w"&gt; &lt;/span&gt;default
&lt;span class="w"&gt;  &lt;/span&gt;enable&lt;span class="w"&gt; &lt;/span&gt;echo&lt;span class="w"&gt; &lt;/span&gt;lqr&lt;span class="w"&gt; &lt;/span&gt;mssfixup
&lt;span class="w"&gt;  &lt;/span&gt;set&lt;span class="w"&gt; &lt;/span&gt;timeout&lt;span class="w"&gt; &lt;/span&gt;0
&lt;span class="w"&gt;  &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;Pause&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;5,&lt;span class="w"&gt; &lt;/span&gt;increment&lt;span class="w"&gt; &lt;/span&gt;+5,&lt;span class="w"&gt; &lt;/span&gt;-5&lt;span class="w"&gt; &lt;/span&gt;times
&lt;span class="w"&gt;  &lt;/span&gt;set&lt;span class="w"&gt; &lt;/span&gt;redial&lt;span class="w"&gt; &lt;/span&gt;5+5-5&lt;span class="w"&gt; &lt;/span&gt;0
&lt;span class="w"&gt;  &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;Wait&lt;span class="w"&gt; &lt;/span&gt;random&lt;span class="w"&gt; &lt;/span&gt;(1-30)&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;reconnect
&lt;span class="w"&gt;  &lt;/span&gt;set&lt;span class="w"&gt; &lt;/span&gt;reconnect&lt;span class="w"&gt; &lt;/span&gt;random&lt;span class="w"&gt; &lt;/span&gt;0
&lt;span class="w"&gt;  &lt;/span&gt;add!&lt;span class="w"&gt; &lt;/span&gt;default&lt;span class="w"&gt; &lt;/span&gt;HISADDR
&lt;span class="w"&gt;  &lt;/span&gt;add!&lt;span class="w"&gt; &lt;/span&gt;default&lt;span class="w"&gt; &lt;/span&gt;HISADDR6
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now PPPoE can be used once with:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ppp -ddial exo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And permanently by adding following to &lt;code&gt;/etc/rc.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# PPPoE bits
ppp_enable=&amp;quot;YES&amp;quot;
ppp_mode=&amp;quot;ddial&amp;quot;
ppp_profile=&amp;quot;exo&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now pinging some IPv4 address should work :-).&lt;/p&gt;
&lt;h3 id="ipv6-connectivity"&gt;IPv6 connectivity&lt;/h3&gt;
&lt;p&gt;IPv6 connectivity is slightly more difficult as &lt;a href="https://exo.cat" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;eXO&lt;/a&gt; must delegate the
&lt;code&gt;/56&lt;/code&gt; prefix to us.&lt;/p&gt;
&lt;p&gt;In this case, I&amp;rsquo;ll want to quickly assign a &lt;code&gt;/64&lt;/code&gt; to the remaining ethernet
interface, &lt;code&gt;igb1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Sadly &lt;code&gt;dhclient&lt;/code&gt; in the base system does not include this feature, and
that&amp;rsquo;s the kind of situation when ports are needed.&lt;/p&gt;
&lt;p&gt;Good thing we now have some legacy (v4) network connectivity!&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s get the &lt;code&gt;dhcp6&lt;/code&gt; port:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# The first time pkg runs, it will bootstrap itself, since it&amp;#39;s
# actually maintained as a port.
pkg install dhcp6
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since this does not belong to the base system, its settings are under
&lt;code&gt;/usr/local/etc&lt;/code&gt; and there is indeed a &lt;code&gt;dhcp6c.conf.sample&lt;/code&gt; with nearly all
we need.&lt;/p&gt;
&lt;p&gt;By reviewing that, &lt;code&gt;man 8 dhcp6c&lt;/code&gt; and specially &lt;code&gt;man 5 dhcp6c.conf&lt;/code&gt;,
following results:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;dhcp6c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conf&lt;/span&gt;

&lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;exoppp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;solicit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;non&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;temporary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ia&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;na&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;solicit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;exoppp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ia&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;pd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;An&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;assoc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;always&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;needed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;even&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;NAs&lt;/span&gt;
&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;assoc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;na&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;delegated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;assign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;igb1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ethernet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;assoc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;igb1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;how&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;many&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bits&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;we&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;free&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Since&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IPv6&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;addresses&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bits&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;eXO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;delegates&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;we&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bits&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;freedom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;until&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;remaining&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;used&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SLAAC&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;locally&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;sla&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;len&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;decimal&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;turned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;binary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sla&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;len&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bits&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;representation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;used&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;along&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;delegated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;sla&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In order to use this, following must be added to &lt;code&gt;/etc/rc.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# Just enabling dhcp6c has issues when reconnecting
#dhcp6c_enable=&amp;quot;YES&amp;quot;
dhcp6c_interfaces=&amp;quot;exoppp&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And it can be tested out with&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;service dhcp6c start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now &lt;code&gt;ifconfig igb1&lt;/code&gt; should have a globally routable unicast IPv6 in a &lt;code&gt;/64&lt;/code&gt;
under your control and &lt;code&gt;ping6&lt;/code&gt; should work (real-time update: happy 2020!).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update (2020-01-28):&lt;/strong&gt; Just enabling dhcp6c has issues with IPv6 on
reconnect after connection loss.
Bright side of this is that we detected an anomaly on &lt;a href="https://exo.cat" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;eXO&lt;/a&gt;&amp;lsquo;s
ppp server that will be solved in the upcoming planned maintenance.&lt;/p&gt;
&lt;p&gt;In order to reconnect properly, let&amp;rsquo;s use ppp&amp;rsquo;s hook functionality.&lt;/p&gt;
&lt;p&gt;On &lt;code&gt;/etc/ppp/ppp.linkup&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;MYADDR:
  # This should block as little as possible, we use !bg to keep going
  # &amp;quot;in the background&amp;quot; so ppp is not blocked.
  !bg /usr/sbin/service dhcp6c onerestart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On &lt;code&gt;/etc/ppp/ppp.linkdown&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;MYADDR&lt;/span&gt;:
&lt;span class="w"&gt;  &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dhcp6c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;can&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;take&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;close&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;link&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;came&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;back&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;up&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;meanwhile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;things&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;can&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;messy&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;We&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;won&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;t use !bg here.&lt;/span&gt;
&lt;span class="err"&gt;  ! /usr/sbin/service dhcp6c onestop&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1 id="remaining-work"&gt;Remaining work&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Create a bridge interface for ethernet and wlan.&lt;/li&gt;
&lt;li&gt;Setup the wireless network&lt;/li&gt;
&lt;li&gt;Setup SLAAC&lt;/li&gt;
&lt;li&gt;Secure the firewall&lt;/li&gt;
&lt;li&gt;Create &lt;a href="https://cdi.st" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;cdist&lt;/a&gt; types for all the things&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="optional"&gt;Optional&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Setup &lt;code&gt;dhcpd&lt;/code&gt; to hand out local IPv4s&lt;/li&gt;
&lt;li&gt;Setup NAT&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="internal-network-bits"&gt;Internal network bits&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Monitoring (Prometheus)&lt;/li&gt;
&lt;li&gt;DNS (unbound)&lt;/li&gt;
&lt;li&gt;Traffic dashboards (taking into account the percentiles)&lt;/li&gt;
&lt;/ul&gt;</content></entry><entry><title>ThinkPad A485 and FreeBSD</title><link href="https://evilham.eu/en/blog/2019-ThinkPad-A485-FreeBSD/" rel="alternate"></link><updated>2019-08-03T00:00:00Z</updated><author><name></name></author><id>urn:uuid:ff80fa99-f771-36e4-8f31-942b4898edc3</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;I&amp;rsquo;ve always thought diversity is an important thing, which is why Linux&amp;rsquo; hegemony on servers, as great as it is, can also be a threat for future network stability.&lt;/p&gt;
&lt;p&gt;Most people having something to do with computers have probably come across the BSD term at one point or another, yet not everyone is aware of OpenBSD and FreeBSD being reasonable alternative Operating Systems with their own strengths.&lt;/p&gt;
&lt;p&gt;Since my previous laptop was way too fragile to be a mobile computer any longer, I had to relegate it to a virtualisation server and start the hunt for a powerful, mobile and flexible laptop.&lt;/p&gt;
&lt;p&gt;Easier said than done, after much researching I settled in May 2019 for a ThinkPad A485 as being the closest thing to a good trade-off for me.
Amongst other things it features an AMD Ryzen™ processor and 32G RAM.&lt;/p&gt;
&lt;p&gt;There is a great article covering support for this laptop on &lt;a href="https://deftly.net/posts/2018-10-15-openbsd-on-lenovo-a485.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;OpenBSD&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Since it&amp;rsquo;s not the case for FreeBSD and, at that point, support for this laptop was not ideal, this is intended as some kind of documentation that will hopefully help other people in the near future, that is in the transition to full support.&lt;/p&gt;
&lt;p&gt;The wish to document this started when &lt;a href="https://lists.freebsd.org/pipermail/freebsd-current/2019-July/073859.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;someone asked&lt;/a&gt; on the &lt;code&gt;freebsd-current&lt;/code&gt; Mailing List about getting the A485 to work with FreeBSD.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This can be copied / adapted to be on the FreeBSD wiki. Let me know if you adapt it.
Sadly the username policies go against my strong preference for pseudonymous internet.
Not that figuring out &amp;ldquo;real names&amp;rdquo; would be difficult for really interested parties, it&amp;rsquo;s just a matter of preference and online identity.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="table-of-contents"&gt;Table of Contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#installation"&gt;Installation&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#12-release"&gt;12-RELEASE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#13-current"&gt;13-CURRENT&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#operation"&gt;Operation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#graphics"&gt;Graphics&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#12-release_1"&gt;12-RELEASE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#13-current_1"&gt;13-CURRENT&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#wireless"&gt;Wireless&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#sd-card-reader"&gt;SD Card reader&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#overview-of-relevant-bugs-and-issues"&gt;Overview of relevant bugs and issues&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#231760-freebsd-halts-at-acpi-on-amd-ryzen-laptops"&gt;#231760 - FreeBSD halts at ACPI on AMD Ryzen laptops&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#239351-panic-spin-lock-held-too-long-a485-bios-bug"&gt;#239351 - PANIC spin lock held too long - A485 BIOS bug&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#-rtl8822be-is-not-supported"&gt;? - RTL8822BE is not supported&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#204521-support-for-realtek-sd-card-reader"&gt;#204521 - Support for Realtek SD card reader&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="installation"&gt;Installation&lt;/h1&gt;
&lt;h2 id="12-release"&gt;12-RELEASE&lt;/h2&gt;
&lt;p&gt;Installing FreeBSD on an AMD Ryzen™ laptop is not as easy as it could be these days, support in 12-RELEASE is not quite there due to some peculiarities in ACPI, &lt;a href="https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=231760" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;FreeBSD bug #231760&lt;/a&gt; tracks that.&lt;/p&gt;
&lt;p&gt;That bug report also lists the necessary workaround to manage to boot and install FreeBSD on the A485:&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;hw.pci.mcfg&lt;/code&gt; kernel parameter must be set to &lt;code&gt;0&lt;/code&gt;, that&amp;rsquo;s done from the boot loader by typing &lt;code&gt;set hw.pci.mcfg=0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After installation, it is quite important to edit &lt;code&gt;/boot/loader.conf&lt;/code&gt; and add a line with &lt;code&gt;hw.pci.mcfg=0&lt;/code&gt;, that way the system will be able to boot without manual intervention.&lt;/p&gt;
&lt;h2 id="13-current"&gt;13-CURRENT&lt;/h2&gt;
&lt;p&gt;For those willing and knowledgeable enough to compile and debug things, 13-CURRENT is where development happens, and diff &lt;a href="https://reviews.freebsd.org/D20327" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;D20327&lt;/a&gt; just landed! This means I can stop maintaining my own branch :-).&lt;/p&gt;
&lt;p&gt;As of this writing, the latest installation media dates August 1st 2019, which is about 48 hours too old, so the above kernel parameter is still needed to install FreeBSD on a new A485 for those tracking 13-CURRENT, but it can be removed from &lt;code&gt;/boot/loader.conf&lt;/code&gt; after compiling and installing &lt;code&gt;HEAD&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Whenever an image is built after August 3rd 2019, it should not need the kernel parameter, &lt;a href="https://download.freebsd.org/ftp/snapshots/amd64/amd64/ISO-IMAGES/13.0/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;check here&lt;/a&gt; to see if it has happened.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; 13-CURRENT is not intended to be used if you can&amp;rsquo;t fix things yourself or are not willing to debug them.
If, however, like me you are curious about the development process and want to be detecting issues before they hit 12-RELEASE, it can useful to use 13-CURRENT.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="operation"&gt;Operation&lt;/h1&gt;
&lt;p&gt;After installing FreeBSD on the A485, the system behaved well over all but I was experiencing some random kernel panics, which I could reliably trigger by stressing a WireGuard VPN connection; that sent me down the wrong path until Konstantin mentioned in &lt;a href="https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=239351" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;bug #239351&lt;/a&gt; that AMD is very secretive about processor features and erratas, which are sometimes fixed in BIOS, so I should make absolutely sure I had the &lt;a href="https://support.lenovo.com/de/en/downloads/ds504078" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;latest BIOS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In early June 2019, my A485 came with BIOS version 1.16, but in July 2019 versions 1.22 and 1.24 came out, apparently one of these versions fixed something in the way the processor is used, which totally fixed these random panics.&lt;/p&gt;
&lt;h1 id="graphics"&gt;Graphics&lt;/h1&gt;
&lt;p&gt;X is the best terminal multiplexer around, luckily this is supported out of the box these days both in 12-RELEASE and 13-CURRENT.&lt;/p&gt;
&lt;p&gt;Support for the graphics card is done through the &lt;code&gt;drm-kmod&lt;/code&gt; port, which automagically installs and loads the appropriate drivers.&lt;/p&gt;
&lt;p&gt;Without this, external screens or graphics acceleration cannot be used.&lt;/p&gt;
&lt;h2 id="12-release_1"&gt;12-RELEASE&lt;/h2&gt;
&lt;p&gt;Under 12-RELEASE this worked perfectly fine with &lt;code&gt;drm-kmod&lt;/code&gt; version &lt;code&gt;4.16.g20190722&lt;/code&gt; until I upgraded the BIOS to 1.24.
After this upgrade, the system started hanging after boot when trying to load the graphics driver.&lt;/p&gt;
&lt;p&gt;On a &lt;a href="https://lists.freebsd.org/pipermail/freebsd-current/2019-July/073864.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;message&lt;/a&gt; on the mailing list someone had suggested previously to test version &lt;code&gt;5.0&lt;/code&gt; of &lt;code&gt;drm-kmod&lt;/code&gt;, as the first wrong hint at tackling the random panics involved this port.&lt;/p&gt;
&lt;p&gt;Getting &lt;code&gt;5.0&lt;/code&gt; on 12-RELEASE involves building the port from source, which I didn&amp;rsquo;t try, but should be doable.&lt;/p&gt;
&lt;h2 id="13-current_1"&gt;13-CURRENT&lt;/h2&gt;
&lt;p&gt;After the BIOS upgrade to 1.24, 13-CURRENT with &lt;code&gt;drm-kmod&lt;/code&gt; also stopped booting, since this is a development release, it was slightly easier to fix than compiling a newer version of the port, indeed there is a &lt;code&gt;drm-devel-kmod&lt;/code&gt; port, which has version &lt;code&gt;5.0.g20190722&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With this newer version of &lt;code&gt;drm-kmod&lt;/code&gt;, FreeBSD boots into X without issues.&lt;/p&gt;
&lt;h1 id="wireless"&gt;Wireless&lt;/h1&gt;
&lt;p&gt;The A485 comes with a Realtek RTL8822BE, which is not supported, luckily enough it can be swapped by an Intel Dual Band Wireless-AC 8265, which is.&lt;/p&gt;
&lt;p&gt;Adding support for this wireless card is as simple as adding following to &lt;code&gt;/boot/loader.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Wireless Intel AC8265&lt;/span&gt;
&lt;span class="c1"&gt;# Not strictly necessary, but the Realtek that is shipped is not&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;supported&lt;/span&gt;
&lt;span class="c1"&gt;# You can easily (and carefully) change the wireless cards.&lt;/span&gt;
&lt;span class="n"&gt;if_iwm_load&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;YES&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;iwm8265fw_load&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;YES&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1 id="sd-card-reader"&gt;SD Card reader&lt;/h1&gt;
&lt;p&gt;The SD card reader is currently not supported, &lt;a href="https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=204521" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;bug #204521&lt;/a&gt; is tracking this and has a bounty of 175 USD (mostly from arrowd, but I added to the bounty).&lt;/p&gt;
&lt;p&gt;Basically, driver &lt;code&gt;rtsx&lt;/code&gt; should be ported from OpenBSD.&lt;/p&gt;
&lt;h1 id="overview-of-relevant-bugs-and-issues"&gt;Overview of relevant bugs and issues&lt;/h1&gt;
&lt;h2 id="231760-freebsd-halts-at-acpi-on-amd-ryzen-laptops"&gt;&lt;a href="https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=231760" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;#231760 - FreeBSD halts at ACPI on AMD Ryzen laptops&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Solved by &lt;a href="https://reviews.freebsd.org/D20327" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;D20327&lt;/a&gt;, and therefore in 13-CURRENT images built after August 3rd 2019.&lt;/p&gt;
&lt;p&gt;Workaround for 12-RELEASE is booting with &lt;code&gt;set hw.pci.mcfg=0&lt;/code&gt; and setting &lt;code&gt;hw.pci.mcfg=0&lt;/code&gt; in &lt;code&gt;/boot/loader.conf&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="239351-panic-spin-lock-held-too-long-a485-bios-bug"&gt;&lt;a href="https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=239351" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;#239351 - PANIC spin lock held too long - A485 BIOS bug&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Solved by upgrading BIOS to 1.24 or newer.&lt;/p&gt;
&lt;h2 id="-rtl8822be-is-not-supported"&gt;? - RTL8822BE is not supported&lt;/h2&gt;
&lt;p&gt;Can be swapped by an Intel AC8265, ideally there would be drivers for Realtek cards, but that&amp;rsquo;s harder to manage.&lt;/p&gt;
&lt;h2 id="204521-support-for-realtek-sd-card-reader"&gt;&lt;a href="https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=204521" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;#204521 - Support for Realtek SD card reader&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bounty of 175 USD, &lt;code&gt;rtsx&lt;/code&gt; needs porting from OpenBSD to FreeBSD.&lt;/p&gt;</content></entry><entry><title>AcCoreConsole: Simple plugin for workload simulation</title><link href="https://evilham.eu/en/blog/2016-AcCorePlugin-Workload-simulator/" rel="alternate"></link><updated>2016-03-15T00:00:00Z</updated><author><name></name></author><id>urn:uuid:7bad2f26-f54e-3da5-afd8-592d49ae82ea</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;Following up on my &lt;a href="/en/blog/2016-AutoCAD-.net-cloud.html"&gt;previous post&lt;/a&gt;, I&amp;rsquo;ll introduce a small AutoCAD &lt;a href="http://usa.autodesk.com/adsk/servlet/index?id=18162650&amp;amp;siteID=123112" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;.Net plugin&lt;/a&gt; that can run on the &lt;a href="http://through-the-interface.typepad.com/through_the_interface/2012/02/the-autocad-2013-core-console.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;AcCoreConsole&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This could work as a hands-on, quick and dirty tutorial to writing .Net plugins for AutoCAD, I recommend reading the &lt;a href="http://usa.autodesk.com/adsk/servlet/index?id=18162650&amp;amp;siteID=123112" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;official documentation&lt;/a&gt; though. If you are already comfortable writing .Net plugins for AutoCAD, you can skip to the &lt;a href="#customising-execution-command-line-arguments"&gt;Customising execution (Command Line Arguments)&lt;/a&gt; section.&lt;/p&gt;
&lt;p&gt;The idea of this plugin is to perform a computation that takes &lt;em&gt;a while&lt;/em&gt; and exit; nothing fancy.&lt;/p&gt;
&lt;p&gt;The reason for this, is to test my implementation of something similar to &lt;a href="https://developer.autodesk.com/api/autocadio/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;AutocadIO&lt;/a&gt; that runs locally and in a much smaller scale; the main motivation for that is that many companies are not (yet) ready to handle over their files to Autodesk and having the files processed in their amazing cloud.&lt;/p&gt;
&lt;p&gt;At some point, my implementation should provide for an easy way to choose between using all local resources and sending jobs to &lt;a href="https://developer.autodesk.com/api/autocadio/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;AutocadIO&lt;/a&gt;; that would give maximum flexibility to both users and developers as well, all while giving end-users (companies) a peek into the potential of massively parallel running processes.&lt;/p&gt;
&lt;h1 id="table-of-contents"&gt;Table of Contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#previous-setup"&gt;Previous setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#project-setup"&gt;Project setup&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#code"&gt;Code&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#the-plugin-class"&gt;The Plugin class&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-commands-class"&gt;The Commands class&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-class-attributes"&gt;The class attributes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-actual-workload-simulation-plugin"&gt;The actual workload simulation plugin&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#the-workload"&gt;The workload&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#first-plugin-version"&gt;First Plugin version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#customising-execution-command-line-arguments"&gt;Customising execution (Command Line Arguments)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#using-the-plugin"&gt;Using the plugin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#closing"&gt;Closing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="previous-setup"&gt;Previous setup&lt;/h1&gt;
&lt;p&gt;The officially supported way to write .Net plugins for Autocad is using Visual Studio, something like &lt;a href="http://mono-project.com" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Mono&lt;/a&gt; would probably work as well.&lt;/p&gt;
&lt;p&gt;Basically, you need a copy of the &lt;a href="http://usa.autodesk.com/adsk/servlet/index?siteID=123112&amp;amp;id=773204" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;ObjectARX SDK&lt;/a&gt; which is a set of dlls that define AutoCAD&amp;rsquo;s API.&lt;/p&gt;
&lt;p&gt;There are two ways to get (and license!) the ObjectARX SDK:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Downloading it directly from the &lt;a href="http://usa.autodesk.com/adsk/servlet/index?siteID=123112&amp;amp;id=773204" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;official website&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Using &lt;a href="https://www.nuget.org/packages/AutoCAD.NET/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;nuget&lt;/a&gt;, which sets up everything for you. This currently only supports AutoCAD 2016; the binaries will usually be compatible with AutoCAD 2014 and 2015 though.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I recommend using nuget since it&amp;rsquo;s the most straightforward way to set up the development environment.&lt;/p&gt;
&lt;p&gt;If you do choose the other way, you will get a zip file with some samples, documentation and lots of other files.
Most of these files are for ObjectARX developers, which is the underlying C++ API for AutoCAD.&lt;/p&gt;
&lt;p&gt;The most important files in the zip are in the &lt;code&gt;inc&lt;/code&gt; directory: &lt;code&gt;AcCoreMgd.dll&lt;/code&gt;, &lt;code&gt;AcDbMgd.dll&lt;/code&gt;, &lt;code&gt;AcTcMgd.dll&lt;/code&gt;. You should make sure to add references to these dlls in your Visual Studio project.&lt;/p&gt;
&lt;h1 id="project-setup"&gt;Project setup&lt;/h1&gt;
&lt;p&gt;The Visual Studio project needs to have &lt;em&gt;Class Library&lt;/em&gt; as its output type, this means that after compiling we will get a dll file containing the classes that make up the plugin.&lt;/p&gt;
&lt;p&gt;This dll gets imported in runtime by AutoCAD or the AcCoreConsole, which will also call the relevant bits of your code.&lt;/p&gt;
&lt;h2 id="code"&gt;Code&lt;/h2&gt;
&lt;p&gt;There are basically two classes that make a .Net plugin for AutoCAD: the &lt;code&gt;Plugin&lt;/code&gt; class and the &lt;code&gt;Commands&lt;/code&gt; class.&lt;/p&gt;
&lt;h3 id="the-plugin-class"&gt;The &lt;code&gt;Plugin&lt;/code&gt; class&lt;/h3&gt;
&lt;p&gt;This class is optional, the basic idea is that this class is instantiated &lt;strong&gt;once&lt;/strong&gt; and should contain data that is static for the plugin.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Plugin&lt;/code&gt; class makes more sense when running a command inside AutoCAD as opposed to the AcCoreConsole; the reason for that is that this class gets instantiated just once for the whole AutoCAD session, allowing you to share data across command executions in different documents and so on.&lt;/p&gt;
&lt;p&gt;Since in AcCoreConsole you only open one document, I think its usefulness is rather limited.&lt;/p&gt;
&lt;p&gt;A typical &lt;code&gt;Plugin&lt;/code&gt; class would look like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Autodesk.AutoCAD.Runtime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="na"&gt;[assembly: ExtensionApplication(typeof(MyPlugin.Plugin))]&lt;/span&gt;
&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;MyPlugin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Plugin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IExtensionApplication&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Initialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// Initialize some resources to be shared across documents&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// and executions of any commands define by this plugin.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Terminate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// Dispose of any resources that need to be manually released.&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// Remember that AutoCAD is already closing at this point!&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The class name &lt;code&gt;Plugin&lt;/code&gt; is totally arbitrary and could easily be something else.&lt;/p&gt;
&lt;p&gt;The important part here is that the class implements the &lt;code&gt;IExtensionApplication&lt;/code&gt; interface.&lt;/p&gt;
&lt;p&gt;Signalising the &lt;code&gt;ExtensionApplication&lt;/code&gt; attribute on a class is recommended, but not mandatory.&lt;/p&gt;
&lt;h2 id="the-commands-class"&gt;The &lt;code&gt;Commands&lt;/code&gt; class&lt;/h2&gt;
&lt;p&gt;This doesn&amp;rsquo;t have to be a class &lt;em&gt;per se&lt;/em&gt; but I do like to create a unique class that contains any custom defined commands.&lt;/p&gt;
&lt;p&gt;My typical &lt;code&gt;Commands&lt;/code&gt; class looks like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Autodesk.AutoCAD.Runtime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Autodesk.AutoCAD.ApplicationServices.Core&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="na"&gt;[assembly: CommandClass(typeof(MyPlugin.Commands))]&lt;/span&gt;
&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;MyPlugin&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Commands&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;[CommandMethod(&amp;quot;MyCommand&amp;quot;, &amp;quot;MyCommand&amp;quot;, &amp;quot;MyCommand&amp;quot;,&lt;/span&gt;
&lt;span class="na"&gt;            CommandFlags.Modal &amp;amp; CommandFlags.Session)]&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;MyCommandMethod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;// Do something with the current drawing&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In addition to the class name &lt;code&gt;Commands&lt;/code&gt;, the method name &lt;code&gt;MyCommandMethod&lt;/code&gt; is arbitrary. The defined commands don&amp;rsquo;t even have to be all in a single class.&lt;/p&gt;
&lt;p&gt;To define a command, we just use the &lt;code&gt;CommandMethod&lt;/code&gt; attribute on a &lt;code&gt;static&lt;/code&gt; method in a class as shown.&lt;/p&gt;
&lt;p&gt;As before, the &lt;code&gt;CommandClass&lt;/code&gt; attribute is recommended but not mandatory&lt;/p&gt;
&lt;p&gt;To my understanding, the &lt;code&gt;Commands&lt;/code&gt; class never gets instantiated since AutoCAD and AcCoreConsole invoke the static method directly.&lt;/p&gt;
&lt;h2 id="the-class-attributes"&gt;The class attributes&lt;/h2&gt;
&lt;p&gt;The main benefit of using the &lt;code&gt;ExtensionApplication&lt;/code&gt; and &lt;code&gt;CommandClass&lt;/code&gt; attributes is load time. That way AutoCAD (and AcCoreConsole) know exactly where the Plugin&amp;rsquo;s entry point is and where any commands are defined.&lt;/p&gt;
&lt;p&gt;If you use class attributes once, you must use them all the time in the plugin, otherwise any commands defined in a class not signalised with the &lt;code&gt;CommandClass&lt;/code&gt; attribute won&amp;rsquo;t be defined.&lt;/p&gt;
&lt;p&gt;More information on the class and method attributes can be found in the official documentation, as well as in &lt;a href="https://about.me/keanw" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Kean&lt;/a&gt;&amp;lsquo;s incredibly informative &lt;a href="http://through-the-interface.typepad.com/through_the_interface/2006/09/optimizing_the_.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;blog&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id="the-actual-workload-simulation-plugin"&gt;The actual workload simulation plugin&lt;/h1&gt;
&lt;h2 id="the-workload"&gt;The workload&lt;/h2&gt;
&lt;p&gt;Because of my Math background, I love the &lt;a href="https://en.wikipedia.org/wiki/Fibonacci_number" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Fibonacci sequence&lt;/a&gt;. Turns out, it&amp;rsquo;s also a very popular programming exercise that shows how badly recursive algorithms can make something easy complicated.&lt;/p&gt;
&lt;p&gt;Which is why I picked this as a way to occupy the CPU in the plugin.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Workload simulation. Naïve calculation of the n-th Fibonacci number.&lt;/span&gt;
&lt;span class="c1"&gt;/// A significant workload can be achieved with n &amp;gt;= 42.&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;param name=&amp;quot;n&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;ulong&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="first-plugin-version"&gt;First Plugin version&lt;/h2&gt;
&lt;p&gt;Now, a very quick way to have a plugin that simulates workload would be to calculate &lt;code&gt;Fib(60)&lt;/code&gt;; that would look something like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Autodesk.AutoCAD.Runtime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Autodesk.AutoCAD.ApplicationServices.Core&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="na"&gt;[assembly: CLSCompliant(true)]&lt;/span&gt;
&lt;span class="na"&gt;[assembly: CommandClass(typeof(TestAcCorePlugin.Commands))]&lt;/span&gt;
&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;TestAcCorePlugin&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Commands&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;[CommandMethod(&amp;quot;TestAcCore&amp;quot;, &amp;quot;TestAcCore&amp;quot;, &amp;quot;TestAcCore&amp;quot;,&lt;/span&gt;
&lt;span class="na"&gt;            CommandFlags.Modal &amp;amp; CommandFlags.Session)]&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;TestAcCoreCommand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DocumentManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MdiActiveDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Editor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;\nFib {0}:\t{1}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;/// Workload simulation. Naïve calculation of the n-th Fibonacci number.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;/// A significant workload can be achieved with n &amp;gt;= 42.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;/// &amp;lt;param name=&amp;quot;n&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;ulong&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But I want this plugin to be a bit more flexible than that and since it will be running in the AcCoreConsole&amp;hellip;&lt;/p&gt;
&lt;h2 id="customising-execution-command-line-arguments"&gt;Customising execution (Command Line Arguments)&lt;/h2&gt;
&lt;p&gt;To my knowledge, there is no defined way to pass information to AcCoreConsole plugins; a suggestion &lt;a href="https://about.me/keanw" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Kean&lt;/a&gt; made when we met last year in Prague was to pass arguments in the Command Line to the plugin.&lt;/p&gt;
&lt;p&gt;This is possible because the AcCoreConsole ignores any arguments it doesn&amp;rsquo;t recognise. That leaves us with the issue of telling AcCoreConsole&amp;rsquo;s arguments and those for our plugin apart.&lt;/p&gt;
&lt;p&gt;One way to do that is the good ol&amp;rsquo; UNIX way of using two consecutive dashes: &lt;code&gt;--&lt;/code&gt; to signalise the beginning of our arguments.&lt;/p&gt;
&lt;p&gt;That leads us to this method:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Ignore any command line arguments before the last &amp;quot;--&amp;quot; word.&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;A list of the gotten arguments.&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ParseArgs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetCommandLineArgs&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parsedArgs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lastIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindLastIndex&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lastIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lastIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;parsedArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lastIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lastIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parsedArgs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This method reads the arguments from the &lt;code&gt;Environment&lt;/code&gt; variable, finds the last appearance of &lt;code&gt;--&lt;/code&gt; and ignores everything behind that. That way we don&amp;rsquo;t have to worry about parsing and understanding AcCoreConsole&amp;rsquo;s arguments.&lt;/p&gt;
&lt;p&gt;Furthermore, it provides us with a (probably) forward compatible way of passing arguments. If Autodesk decides to add more Command Line arguments to the AcCoreConsole in the future, that&amp;rsquo;s OK since we just ignore everything before the last &lt;code&gt;--&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, to use the gotten arguments we have to modify the &lt;code&gt;TestAcCoreCommand&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;TestAcCoreCommand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ParseArgs&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;uint&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;45&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DocumentManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MdiActiveDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Editor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;\nFib {0}:\t{1}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this case, we treat the arguments as a list of integers that tells us which members of the Fibonacci sequence are to be calculated.&lt;/p&gt;
&lt;p&gt;When no argument is passed (or if the command runs inside AutoCAD), we calculate the first 45 Fibonacci numbers.&lt;/p&gt;
&lt;h2 id="using-the-plugin"&gt;Using the plugin&lt;/h2&gt;
&lt;p&gt;After compiling, a &lt;code&gt;TestAcCorePlugin.dll&lt;/code&gt; file will be generated. This file can be loaded with &lt;code&gt;netload&lt;/code&gt; from both inside AutoCAD and the AcCoreConsole; that will define the &lt;code&gt;testaccore&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;To automate loading and command calling, I created a Script file called &lt;code&gt;ac.scr&lt;/code&gt;, with following content:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;secureload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;netload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;${PathToDll}\TestAcCorePlugin.dll&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;testaccore&lt;/span&gt;
&lt;span class="n"&gt;secureload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;secureload&lt;/code&gt; part is because we are not (yet) signing this dll; which we should.&lt;/p&gt;
&lt;p&gt;Now with an open Command Prompt, I can invoke my plugin with:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;quot;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;PathToAutoCAD&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;\accoreconsole.exe&amp;quot;&lt;span class="w"&gt; &lt;/span&gt;/s&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;%{PathToScript}\ac.scr&amp;quot;&lt;span class="w"&gt; &lt;/span&gt;--&lt;span class="w"&gt; &lt;/span&gt;30&lt;span class="w"&gt; &lt;/span&gt;40&lt;span class="w"&gt; &lt;/span&gt;45
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which will produce following output:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;AutoCAD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Core&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Engine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Copyright&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Autodesk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Inc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2009&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;2013.&lt;/span&gt;
&lt;span class="n"&gt;Regenerating&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;netload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Assembly&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;${PathToDll}\TestAcCorePlugin.dll&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testaccore&lt;/span&gt;

&lt;span class="n"&gt;Fib&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;832040&lt;/span&gt;
&lt;span class="n"&gt;Fib&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;102334155&lt;/span&gt;
&lt;span class="n"&gt;Fib&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1134903170&lt;/span&gt;

&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_quit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Great! We just used AcCoreConsole to calculate Fibonacci numbers.&lt;/p&gt;
&lt;h1 id="closing"&gt;Closing&lt;/h1&gt;
&lt;p&gt;The main point of this post is the usage of Command Line Arguments to customise the execution of a given script running in AcCoreConsole.&lt;/p&gt;
&lt;p&gt;This allows for many interesting use-cases: we can, for example, run three different AcCoreConsoles, each with a different number as an argument; that would make the calculations in parallel as opposed to serially.&lt;/p&gt;
&lt;p&gt;As a matter of fact, I am doing just that; next post will start introducing what I call &lt;strong&gt;OwnACCCL&lt;/strong&gt; (OwnAcCoreConsoleLauncher).
Which takes care of starting the AcCoreConsoles with the proper arguments and not exhausting the system&amp;rsquo;s resources while at it.&lt;/p&gt;</content></entry><entry><title>AutoCAD, .Net and Cloud technologies</title><link href="https://evilham.eu/en/blog/2016-AutoCAD-dotnet-cloud/" rel="alternate"></link><updated>2016-03-14T00:00:00Z</updated><author><name></name></author><id>urn:uuid:e080f626-b1a5-3acb-9eae-0a1d7ca7990c</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;It has been quite a while since I last updated this blog and in between a lot has changed personally, professionally and in technology.
For instance: German is now the language I speak the most and it has taken its toll on my English, I apologise for that in advance.&lt;/p&gt;
&lt;p&gt;Another thing that has changed is my preference of technology when it comes to AutoCAD automation: back when I wrote &lt;a href="[[en/blog/2012-AutoCAD-automation.html]]"&gt;this post&lt;/a&gt; it was Lisp, nowadays I use mostly &lt;a href="http://usa.autodesk.com/adsk/servlet/index?id=18162650&amp;amp;siteID=123112" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;.Net plugins&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The downside [of Lisp, back in 2013 my go-to AutoCAD automation tech] is that it&amp;rsquo;s quite a mess to have a GUI for the scripts or routines you develop. I mean, sure, it can be done (there&amp;rsquo;s DCL after all) but it&amp;rsquo;s not as flexible or easy as one would hope. Luckily, for most use cases a simple custom toolbar and text input will make users happy.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Turns out, all those issues with GUI development along with some caveats with batch scripting (on hundreds and thousands of DWG files) made Lisp more of a burden than a blessing.&lt;/p&gt;
&lt;p&gt;Also, &lt;a href="http://through-the-interface.typepad.com/through_the_interface/2012/02/the-autocad-2013-core-console.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;AcCoreConsole&lt;/a&gt; happened and Autodesk has been investing in cloud technologies heavily, so &lt;a href="https://developer.autodesk.com/api/autocadio/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;AutocadIO&lt;/a&gt; and &lt;a href="https://developer.autodesk.com/api/view-and-data-api/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;View and Data&lt;/a&gt; APIs will be coming strong.&lt;/p&gt;
&lt;h1 id="table-of-contents"&gt;Table of Contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#why-net"&gt;Why .Net?&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#performance-and-flexibility"&gt;Performance and flexibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#better-tools-and-support"&gt;Better tools and support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#purpose-oriented-routines"&gt;Purpose oriented routines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#packaging-distribution-and-deployment"&gt;Packaging, distribution and deployment&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#accoreconsole-and-the-cloud"&gt;AcCoreConsole and the Cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#closing"&gt;Closing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="why-net"&gt;Why .Net?&lt;/h1&gt;
&lt;h2 id="performance-and-flexibility"&gt;Performance and flexibility&lt;/h2&gt;
&lt;p&gt;Autodesk invested very heavily in creating a powerful, flexible, yet simple .Net API for AutoCAD.
It allows developers to do nearly everything that is possible with ObjectARX (the low level C++ API) with a higher level language like C#, F# or VB.Net.&lt;/p&gt;
&lt;h2 id="better-tools-and-support"&gt;Better tools and support&lt;/h2&gt;
&lt;p&gt;If you want to write Lisp for Autocad, you will most likely use Notepad++ or the Visual Lisp IDE, which has probably not received significant update since before year 2000; it looks like it&amp;rsquo;s basically supported for backwards compatibility and to avoid annoying the remaining Lispers amongst us.&lt;/p&gt;
&lt;p&gt;With .Net, you can use Visual Studio and its debugging features which are, in my opinion, far better.&lt;/p&gt;
&lt;p&gt;It is also easier to structure, maintain and reuse code thanks to shared class libraries.&lt;/p&gt;
&lt;h2 id="purpose-oriented-routines"&gt;Purpose oriented routines&lt;/h2&gt;
&lt;p&gt;Most of my Lisp routines were very generic and defined a couple AutoCAD commands with the most common options. These routines could be quickly extended to do &lt;em&gt;exactly&lt;/em&gt; what the user wanted with Lisp one-liners; my experience so far has been that end-users &lt;em&gt;never&lt;/em&gt; do that and they end up forgetting commands they don&amp;rsquo;t use that often.&lt;/p&gt;
&lt;p&gt;The reason for that is that most end-users have no experience with simple programming, left alone with Lisp; you can&amp;rsquo;t really blame them.&lt;/p&gt;
&lt;p&gt;Which is why I now focus on providing users with a GUI application that allows them to perform a specific task and adjust the parameters for that in the process. This is much easier in .Net, specially when using WPF for the GUIs.&lt;/p&gt;
&lt;p&gt;That being said, I still provide commands that allow programmatic usage of the routines; that means I sometimes use Lisp as some kind of &lt;em&gt;meta-scripting&lt;/em&gt; to further automate .Net routines.&lt;/p&gt;
&lt;h2 id="packaging-distribution-and-deployment"&gt;Packaging, distribution and deployment&lt;/h2&gt;
&lt;p&gt;Common issues with Lisp scripts were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Users found routine installation too complicated.&lt;/li&gt;
&lt;li&gt;It was difficult to provide users with updates.&lt;/li&gt;
&lt;li&gt;As long as users were in our office, everything worked fine because of custom AutoCAD workspaces; remote or in-client-premises workers were sometimes not as lucky.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some of these issues were addressed by Autodesk with the introduction of the Autoloader (see the &lt;a href="http://adndevblog.typepad.com/autocad/2013/01/autodesk-autoloader-white-paper.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;white paper&lt;/a&gt;), but others (e.g. updates) persist and are quite difficult do solve with Lisp but not so in .Net.&lt;/p&gt;
&lt;h1 id="accoreconsole-and-the-cloud"&gt;AcCoreConsole and the Cloud&lt;/h1&gt;
&lt;p&gt;Back in 2013, Autodesk had already been separating AutoCAD&amp;rsquo;s GUI from the logic; one of the reasons for that, was to provide a Mac version of AutoCAD.
As a result of that separation, the &lt;a href="http://through-the-interface.typepad.com/through_the_interface/2012/02/the-autocad-2013-core-console.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;AcCoreConsole&lt;/a&gt; was born.&lt;/p&gt;
&lt;p&gt;The &lt;a href="http://through-the-interface.typepad.com/through_the_interface/2012/02/the-autocad-2013-core-console.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;AcCoreConsole&lt;/a&gt; is a headless, minimalistic version of AutoCAD, that can fully interact with a DWG database.
For regular users, this means nothing; for developers however, this opened up a world of possibilities.&lt;/p&gt;
&lt;p&gt;Suddenly, running a Lisp script in a few hundred files was as easy as preparing a small &lt;code&gt;.bat&lt;/code&gt; file that called the AcCoreConsole with the right arguments and invoked the script. All with the performance gains of not having any GUI and not having to render each opened drawing &amp;mdash; these performance gains are &lt;em&gt;huge&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This, along with the rise of Amazon Web Services, gave some very clever people in Autodesk the base idea for &lt;a href="https://developer.autodesk.com/api/autocadio/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;AutocadIO&lt;/a&gt; and &lt;a href="https://developer.autodesk.com/api/view-and-data-api/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;View and Data&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://developer.autodesk.com/api/autocadio/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;AutocadIO&lt;/a&gt; is basically a way of running scripts massively in the cloud and &lt;a href="https://developer.autodesk.com/api/view-and-data-api/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;View and Data&lt;/a&gt; is a way of accessing or displaying drawings in a browser.&lt;/p&gt;
&lt;p&gt;The potential of these APIs both in and outside of the engineering world  is enormous and they will surely gain importance in the next couple years.&lt;/p&gt;
&lt;p&gt;A very cool example prepared by &lt;a href="https://about.me/keanw" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Kean Walmsley&lt;/a&gt; is &lt;a href="http://www.jigsawify.com/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Jigsawify&lt;/a&gt;; information on some design decisions and technical details can be found on &lt;a href="http://through-the-interface.typepad.com/through_the_interface/autocad-io/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;his blog&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id="closing"&gt;Closing&lt;/h1&gt;
&lt;p&gt;This post serves as a bit of an update and introduction for the work I&amp;rsquo;ve been doing in the past few months, stay tuned.&lt;/p&gt;</content></entry><entry><title>AutoCAD: Blocks. Part I: Basics</title><link href="https://evilham.eu/en/blog/2013-blocks-part-1-basics/" rel="alternate"></link><updated>2013-01-31T00:00:00Z</updated><author><name></name></author><id>urn:uuid:2d6a60ce-93dd-3eba-af25-efecb97bd7c3</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;If you&amp;rsquo;re a somewhat advanced AutoCAD user, you probably fully understand blocks and its usage. If that&amp;rsquo;s the case, you probably should skip to the next part of the series on &lt;strong&gt;blocks&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Basically, a block is a way of grouping objects (e.g. lines, texts, circles, other blocks, etc.), giving such group a name and it allows for easy manipulation of a big amount of objects.&lt;/p&gt;
&lt;p&gt;On this part, we&amp;rsquo;ll see some advantages of using blocks and lastly but more importantly: &lt;strong&gt;block attributes&lt;/strong&gt;.&lt;/p&gt;
&lt;h1 id="table-of-contents"&gt;Table of Contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#advantages"&gt;Advantages&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#smaller-file-size"&gt;Smaller file size&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#instant-update-of-symbols"&gt;Instant update of symbols&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#conceptual-and-graphic-separation"&gt;Conceptual and graphic separation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#block-attributes"&gt;Block Attributes&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#what-are-attributes"&gt;What are attributes?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#example"&gt;Example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-catch"&gt;The catch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#closing"&gt;Closing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="advantages"&gt;Advantages&lt;/h1&gt;
&lt;p&gt;Use of blocks has several advantages, here are some I came up with:&lt;/p&gt;
&lt;h2 id="smaller-file-size"&gt;Smaller file size&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;block definition&lt;/strong&gt; is saved just once and every graphic instance of a block (called &lt;strong&gt;block reference&lt;/strong&gt;) consists only of basic properties like: block name, insertion point, scale (x,y,z) and rotation (plus, the general graphic object properties). That means that when a block is used several times, less information has to be stored in the file.&lt;/p&gt;
&lt;h2 id="instant-update-of-symbols"&gt;Instant update of symbols&lt;/h2&gt;
&lt;p&gt;Again, because the block definition is saved just once, if you need to modify a symbol you have used a hundred times, you need only to modify the block definition in order to reflect those changes &lt;em&gt;consistently&lt;/em&gt; and &lt;em&gt;instantly&lt;/em&gt; across the drawing.&lt;/p&gt;
&lt;h2 id="conceptual-and-graphic-separation"&gt;Conceptual and graphic separation&lt;/h2&gt;
&lt;p&gt;If a drawing is complex and &lt;em&gt;layers&lt;/em&gt; are not enough for conceptual separation, it may be useful to create blocks for things that represent different parts of the same object. Done correctly, it can avoid confusion and create a better workflow.&lt;/p&gt;
&lt;h1 id="block-attributes"&gt;Block Attributes&lt;/h1&gt;
&lt;h2 id="what-are-attributes"&gt;What are attributes?&lt;/h2&gt;
&lt;p&gt;Attributes are a way of having some text associated with a block, the beautiful thing of attributes is that, unlike the other parts of a block (excluding dynamic blocks), they can change from one block reference to another.&lt;/p&gt;
&lt;p&gt;These attributes may or may not be visible, constant, you can change their position, even change their aspect for each block reference, they are quite flexible.&lt;/p&gt;
&lt;h2 id="example"&gt;Example&lt;/h2&gt;
&lt;p&gt;Consider a valve and its &lt;strong&gt;TAG&lt;/strong&gt; number, it may be desirable to use a block for the symbol but texts are something static that looks exactly the same in all of the instances. One solution could be to use a different block for each &lt;em&gt;tag&lt;/em&gt;, but that&amp;rsquo;d defeat the point of using blocks.
Here is where attributes come handy, we just add one to the block definition, place it where we want it, insert it and change the text at will.&lt;/p&gt;
&lt;p&gt;&lt;img alt="valves" src="/media/img/valves.gif" title="Valves" /&gt;&lt;/p&gt;
&lt;h2 id="the-catch"&gt;The catch&lt;/h2&gt;
&lt;p&gt;The flexibility of attributes comes with a cost, since you are able to relocate them, change their colour, text style, angle, alignment, etc. for each block reference, there&amp;rsquo;s a bit of a disconnection between the block definition and the block reference when it comes to attributes.&lt;/p&gt;
&lt;p&gt;For instance, say you define a block &lt;code&gt;valve&lt;/code&gt; without the &lt;em&gt;tag&lt;/em&gt; attribute, make a couple insertions of the block and then realise you need that tag attribute. So you go to the block definition, add it and save the definition.&lt;/p&gt;
&lt;p&gt;What happens to the &lt;em&gt;existing&lt;/em&gt; block references? Absolutely nothing, they won&amp;rsquo;t have your &lt;em&gt;tag&lt;/em&gt; attribute. Similarly, relocating an attribute or changing its aspect won&amp;rsquo;t affect the existing block references. Instead, all of the new ones will be exactly as the block definition.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a way to force a global update of a particular attribute natively with AutoCAD, it&amp;rsquo;s under: Modify / Object / Attribute / Block Attribute Manager.
The downside is that it will reset &lt;em&gt;all&lt;/em&gt; of the properties for a given attribute, that is, if you relocated the attribute in a cluttered zone so that it&amp;rsquo;s visible, it&amp;rsquo;ll go back to the default position and overlap with something else, so be careful!&lt;/p&gt;
&lt;p&gt;You could, of course, write your own Lisp routines to update just a given property like colour or layer.&lt;/p&gt;
&lt;h1 id="closing"&gt;Closing&lt;/h1&gt;
&lt;p&gt;In a few follow up posts, I&amp;rsquo;ll cover how to create and modify blocks, how to insert them from an existing file, how to access and modify its attributes, and a few other things, all with &lt;strong&gt;lisp scripting&lt;/strong&gt;. Stay tuned.&lt;/p&gt;</content></entry><entry><title>AutoCAD automation</title><link href="https://evilham.eu/en/blog/2012-AutoCAD-automation/" rel="alternate"></link><updated>2012-06-27T00:00:00Z</updated><author><name></name></author><id>urn:uuid:957e84bc-b449-34ed-a4d0-5f7022cc50ad</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;As you can check on my CV, I&amp;rsquo;ve been working as a draughtsman/IT guy/CAD manager for around five years now.&lt;/p&gt;
&lt;p&gt;We spend most of our time &amp;ldquo;drawing&amp;rdquo; things with &lt;a href="https://en.wikipedia.org/wiki/AutoCAD" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Autodesk&amp;rsquo;s AutoCAD&lt;/a&gt;.
Be it a P&amp;amp;ID, an electric diagram, civil, structural or piping drafts, a 3D model, you name it!&lt;/p&gt;
&lt;p&gt;Being a somewhat small company, in five years we&amp;rsquo;ve faced different challenges, like getting projects that would require more human hours than we have the means to cover — either by lack of personnel or deadlines.&lt;/p&gt;
&lt;p&gt;Sometimes we&amp;rsquo;ve solved those challenges by getting more people, sometimes by working more hours than usual. Sometimes though those things are just not possible — something &lt;em&gt;else&lt;/em&gt; is needed.&lt;/p&gt;
&lt;h1 id="table-of-contents"&gt;Table of Contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#automation-in-autocad"&gt;Automation in AutoCAD&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#small-sample"&gt;Small sample&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#further-reading"&gt;Further reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="automation-in-autocad"&gt;Automation in AutoCAD&lt;/h1&gt;
&lt;p&gt;That something &lt;em&gt;else&lt;/em&gt;, would be automation in AutoCAD.
That is, using some amount of programming to make tasks easier, faster and less monotonous.&lt;/p&gt;
&lt;p&gt;There are plenty of APIs for AutoCAD, the eldest (and my favourite!)  being &lt;a href="https://en.wikipedia.org/wiki/Lisp_programming_language" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Lisp&lt;/a&gt; (more accurately &lt;a href="https://en.wikipedia.org/wiki/AutoLISP" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;AutoLisp/Visual Lisp&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Other options include VBA, .NET and &lt;a href="https://en.wikipedia.org/wiki/ObjectARX" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;ObjectARX&lt;/a&gt;. VBA for AutoCAD is deprecated though, so that leaves us with either .NET and ObjectARX as alternatives.&lt;/p&gt;
&lt;p&gt;I mentioned that my favourite option is &lt;strong&gt;Lisp&lt;/strong&gt; and that it&amp;rsquo;s the oldest; the thing is that ever since the Visual Lisp extensions, it&amp;rsquo;s incredibly easy to program in a somewhat object-oriented way and to manipulate graphic objects without having to worry too much about AutoCAD&amp;rsquo;s inner functioning.&lt;/p&gt;
&lt;p&gt;The downside is that it&amp;rsquo;s quite a mess to have a GUI for the scripts or routines you develop. I mean, sure, it can be done (there&amp;rsquo;s DCL after all) but it&amp;rsquo;s not as flexible or easy as one would hope. Luckily, for most use cases a simple custom toolbar and text input will make users happy.&lt;/p&gt;
&lt;p&gt;If you really need GUIs or deep control of what&amp;rsquo;s going on under the hood, I&amp;rsquo;d strongly recommend learning about the &lt;a href="https://en.wikipedia.org/wiki/ObjectARX" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;ObjectARX&lt;/a&gt; API. It&amp;rsquo;s well-documented and it&amp;rsquo;s incredibly powerful — you have to program in C++ though.&lt;/p&gt;
&lt;h2 id="small-sample"&gt;Small sample&lt;/h2&gt;
&lt;p&gt;Here are some bits from what I usually have on my &lt;code&gt;acaddoc.lsp&lt;/code&gt; file.
That is, the customisations that are loaded with each drawing.&lt;/p&gt;
&lt;p&gt;Most are really silly things that happen to save little time per single use but one gets to use quite a lot depending on the kind of work currently at hand.&lt;/p&gt;
&lt;p&gt;You may download this file &lt;a href="/media/lsp/small-sample.lsp"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;; small-sample.lsp: Is a small sample of things that can be done with&lt;/span&gt;
&lt;span class="c1"&gt;; Visual Lisp. It includes part of Evilham&amp;#39;s acaddoc.lsp.&lt;/span&gt;
&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;; LICENSE:&lt;/span&gt;
&lt;span class="c1"&gt;; Copyright (c) 2012, Evilham (https://evilham.com) &lt;/span&gt;
&lt;span class="c1"&gt;; BSD 2-Clause copyright and disclaimer apply.&lt;/span&gt;
&lt;span class="c1"&gt;; See: http://www.opensource.org/licenses/bsd-license.php&lt;/span&gt;


&lt;span class="c1"&gt;; This loads the Visual Lisp extensions for AutoLisp&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;vl-load-com&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;c:&lt;/span&gt;&lt;span class="o"&gt;``&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;; Toggle isometric / orthogonal axis (called snap in AutoCAD)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;setvar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;SNAPSTYL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;getvar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;SNAPSTYL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;c:&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;z&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;; Print the Z coordinate of a given point:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;setq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;z&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;nth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;getpoint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;\nPoint to get z from: &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;princ&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;strcat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;\nz: &amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;rtos&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;z&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;princ&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ss-&amp;gt;vla-objectlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ss&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lst&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lim&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;; Convert a selection set to a list of vla-objects&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;setq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;sslength&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;setq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;1+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lim&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;setq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lst&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cons&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;vlax-ename-&amp;gt;vla-object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ssname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ss&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;reverse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;txtnum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ss&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;prefix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;suffix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nstart&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nstep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ndecimals&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;; Takes a selection set / vla-object list, two strings (prefix/suffix)&lt;/span&gt;
&lt;span class="c1"&gt;;   and three numbers nstart, nstep, ndecimals.&lt;/span&gt;
&lt;span class="c1"&gt;; It puts in the text/mtext objects&amp;#39; TextString property the following:&lt;/span&gt;
&lt;span class="c1"&gt;;   (strcat prefix NUM suffix)&lt;/span&gt;
&lt;span class="c1"&gt;;   where NUM is nstart + POS_IN_QUEUE * nstep. With ndecimals decimals.&lt;/span&gt;

&lt;span class="c1"&gt;; If ss is a selection set, convert it to a list.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;PICKSET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;setq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ss&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ss-&amp;gt;vla-objectlist&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="c1"&gt;; If ss is not a list at this point. Or nstart, nstep, ndecimals are not all&lt;/span&gt;
&lt;span class="c1"&gt;; numbers, set ss to nil. That skips the code and avoids errors.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;listp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;numberp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nstart&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;numberp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nstep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;numberp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ndecimals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;setq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ndecimals&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;fix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ndecimals&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;setq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ss&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;setq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;obj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ss&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;vl-catch-all-error-p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;vl-catch-all-apply&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;vlax-put-property&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;obj&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;TextString&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;strcat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;prefix&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;rtos&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nstart&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nstep&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ndecimals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nv"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;setq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;1+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;c:txtnum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ss&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;prefix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;; Asks for a prefix and a suffix.&lt;/span&gt;
&lt;span class="c1"&gt;; Calls txtnum with nstart=1 nstep=1 ndecimals=0&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;setq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ss&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ssget&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;prefix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;getstring&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Prefix: &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;suffix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;getstring&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Suffix: &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;txtnum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ss&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;prefix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;suffix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;princ&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;r:gen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ang&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ss&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;po&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lim&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;; Rotates all selected items by ang &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;setq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ss&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ss-&amp;gt;vla-objectlist&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ssget&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;setq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;po&lt;/span&gt;
&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;vlax-make-variant&lt;/span&gt;
&lt;span class="w"&gt;                   &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;vlax-safearray-fill&lt;/span&gt;
&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;vlax-make-safearray&lt;/span&gt;
&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="nv"&gt;vlax-vbDouble&lt;/span&gt;
&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;getpoint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Base point: &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;numberp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ang&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;obj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ss&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;vl-catch-all-apply&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;vlax-invoke-method&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nv"&gt;obj&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;Rotate&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nv"&gt;po&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nv"&gt;ang&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;deg-&amp;gt;rad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ang&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;; Conversion from degrees to radians.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ang&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;180.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;pi&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;; The following commands rotate by 90, 180 and 270 degrees respectively:&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;c:r1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;r:gen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;deg-&amp;gt;rad&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;princ&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;c:r2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;r:gen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;deg-&amp;gt;rad&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;princ&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;c:r3&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;r:gen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;deg-&amp;gt;rad&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;270&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;princ&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;princ&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Some of these commands look really silly, like &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt; and &lt;code&gt;r3&lt;/code&gt;, however once you get used to them it&amp;rsquo;s much faster to just type &lt;code&gt;r1&lt;/code&gt;, select the objects, and click on the base point than having to &lt;em&gt;also&lt;/em&gt; type in the degrees or activating &lt;code&gt;ORTHO&lt;/code&gt; and clicking on the good direction (also minding the fact that &lt;code&gt;OSNAP&lt;/code&gt; may get you a different point and hence a wrong angle).&lt;/p&gt;
&lt;p&gt;In others, like &lt;code&gt;txtnum&lt;/code&gt;, you&amp;rsquo;ll notice how a much more generic function is also defined. That&amp;rsquo;s meant to allow for quick in-place modifications to satisfy slightly different needs.&lt;/p&gt;
&lt;p&gt;For example, if you wanted the text &amp;ldquo;X meters&amp;rdquo; with X starting at 0 and increasing by 0.5 each time, you could just type:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;txtnum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ssget&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot; meters&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And if it happens to be something you need a lot in a session, just assign that to a temporary alias like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;c:&amp;lt;1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;txtnum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ssget&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot; meters&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;princ&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And if you &lt;em&gt;really&lt;/em&gt; use it a lot, just add that line to your &lt;code&gt;acaddoc.lsp&lt;/code&gt; and now you have yet another silly command to make your life easier.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;There&amp;rsquo;s much power in AutoCAD&amp;rsquo;s APIs, basically the limit between what you can and cannot do is in your imagination (albeit it&amp;rsquo;s cliché, it&amp;rsquo;s also mostly true).&lt;/p&gt;
&lt;p&gt;Any seemingly simple task that is bothering you because it&amp;rsquo;s too boring (read, easy and mechanical) can most likely be automated in plenty of different ways!
Heck, even pretty complex things can be automated (up to a certain point) given enough analysis and understanding of the needs.&lt;/p&gt;
&lt;p&gt;If you find yourself in such a case, do &lt;a href="/en/about/contact"&gt;drop me a line&lt;/a&gt;! If you provide me with a good description of what you have to do (hopefully with a file example; &lt;strong&gt;example&lt;/strong&gt;, I won&amp;rsquo;t do your job for you!), how you &lt;em&gt;think&lt;/em&gt; you can automate it (I reserve the right to do it in a totally different way ;)), and I happen to: find it interesting enough, have the time and disposition to do what you&amp;rsquo;re asking for; I may as well just do it!&lt;/p&gt;
&lt;h1 id="further-reading"&gt;Further reading&lt;/h1&gt;
&lt;p&gt;It has been a while since last time I needed online help developing for AutoCAD, so I can&amp;rsquo;t remember that many pages;
I&amp;rsquo;ll be adding some resources that will help you get started with automation in AutoCAD. So far I&amp;rsquo;ve remembered the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.afralisp.net" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Afralisp&lt;/a&gt;: good place full of tutorials and guides. Beginner-friendly!&lt;/li&gt;
&lt;li&gt;&lt;a href="http://docs.autodesk.com/ACD/2011/ENU/filesALG/WSfacf1429558a55de1a7524c1004e616f8b-5bb9.htm" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;AutoCAD Help&lt;/a&gt;: Official documentation. Whenever you have a doubt or are looking for something, go there first!&lt;/li&gt;
&lt;/ul&gt;</content></entry><entry><title>How I got into programming</title><link href="https://evilham.eu/en/blog/2012-How-I-got-into-programming/" rel="alternate"></link><updated>2012-05-28T00:00:00Z</updated><author><name></name></author><id>urn:uuid:c93235e0-26d5-3f9c-9c2a-b0dcb3924d7b</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;At first, I wanted to start blogging about things related to my job but then I realised how introducing the topic out of the blue would have required explaining &lt;em&gt;How I got into programming&lt;/em&gt; and how I use it at work.&lt;/p&gt;
&lt;p&gt;So, here is my attempt at doing just that.&lt;/p&gt;
&lt;p&gt;I just want to add, that I program for fun and because it makes my life easier. Quite often someone else&amp;rsquo;s as well. That does mean that I program at work, but as a mean to help getting my job (or someone else&amp;rsquo;s) done &lt;strong&gt;faster&lt;/strong&gt; &lt;em&gt;and&lt;/em&gt; &lt;strong&gt;better&lt;/strong&gt;, i.e. it&amp;rsquo;s not my main function — at least it isn&amp;rsquo;t as of this writing.&lt;/p&gt;
&lt;h1 id="table-of-contents"&gt;Table of Contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#early-years"&gt;Early years&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#logo-parenthesis"&gt;Logo parenthesis&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#maturing-and-serious-programming"&gt;Maturing and serious programming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#at-work"&gt;At work&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#summing-up"&gt;Summing up&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="early-years"&gt;Early years&lt;/h1&gt;
&lt;p&gt;I belong to the new generations —which means that my &lt;em&gt;early years&lt;/em&gt; are actually kind of the new days— hence I have no cool stories about computers that needed a whole room in an University, had no experience with &lt;a href="http://puzzlesbyjoe.com/programming/why-i-love-programming-then/" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;keypunch cards&lt;/a&gt;; heck, I hadn&amp;rsquo;t even been conceived by the time the internet started to get mainstream!&lt;/p&gt;
&lt;p&gt;That means that my very first experience with a computer… Was actually an IBM PC! Yep, a somewhat &lt;em&gt;small&lt;/em&gt; thing with 64MB RAM and a 15&amp;rdquo; screen that was able to handle a 800x600 pixel resolution (and 16bit colour depth!).&lt;/p&gt;
&lt;p&gt;As the curious kid I was, the first thing I did when we got that thing (running Windows 95!) was to… try and find out what to do with it. Yeah sure, it turned on, you could move the mouse around, make a few drawings on Paint or whatever but… these things had to be able to do something else.&lt;/p&gt;
&lt;p&gt;It wasn&amp;rsquo;t long after its arrival that I learned by heart every single configuration that was available and got bored of the computer. Luckily, a time after that I received a copy of &lt;a href="http://www.nostalgia8.nl/logoversies.htm#pc" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;some version of Logo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Now&lt;/em&gt; the fun started! Why draw geometric figures that won&amp;rsquo;t be precise with Paint, when I had the opportunity of drawing &lt;em&gt;anything&lt;/em&gt; I could imagine just by telling that agreeable turtle how to move?&lt;/p&gt;
&lt;p&gt;Maybe it was a hidden desire to rule the world and have everyone do as I said but it was a damn good feeling to see that turtle do as commanded. Even when it made something unexpected because, hey, &lt;em&gt;I&lt;/em&gt; had told it to do &lt;em&gt;that&lt;/em&gt; even if it wasn&amp;rsquo;t what I wanted.&lt;/p&gt;
&lt;p&gt;In other words, it wasn&amp;rsquo;t the turtle that was an idiot, the problem was the 7 or 8 years old creature between the chair and the keyboard — and at the time I &lt;em&gt;knew&lt;/em&gt; it as well.&lt;/p&gt;
&lt;p&gt;Later on, I discovered that it wasn&amp;rsquo;t only that turtle&amp;rsquo;s nature, it was computer&amp;rsquo;s nature to do &lt;em&gt;exactly&lt;/em&gt; as they&amp;rsquo;re told (even if you tell them wrong!); the little dictator inside of me was &lt;em&gt;very&lt;/em&gt; pleased.&lt;/p&gt;
&lt;h2 id="logo-parenthesis"&gt;Logo parenthesis&lt;/h2&gt;
&lt;p&gt;While writing this, nostalgia kicked in and I found a version of &lt;a href="http://www.nostalgia8.nl/logoversies.htm#pc" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;logo writer&lt;/a&gt;. Here&amp;rsquo;s a little something I did in the time needed to remember syntax and basic commands:&lt;/p&gt;
&lt;p&gt;&lt;img alt="logo_hex" src="/media/img/logo_hex.gif" title="Something simple in Logo" /&gt;&lt;/p&gt;
&lt;p&gt;And here&amp;rsquo;s the code for that:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cg pu setpos [-200 -95] pd setc 8
repeat 125 [repeat 6 [left 120 repeat 7 [fd 20 right 60]] \
pu fd 60 left 60 fd 40 right 120 fd 20 left 60 pd] pu home setc 1 pd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As a side note, this turtle&amp;rsquo;s space is topologically equivalent to a donut!&lt;/p&gt;
&lt;h1 id="maturing-and-serious-programming"&gt;Maturing and serious programming&lt;/h1&gt;
&lt;p&gt;While starting high school, I felt I had tons of time in my hands and learned how to program in &lt;em&gt;Pascal&lt;/em&gt; and &lt;em&gt;Basic&lt;/em&gt; (because it&amp;rsquo;s what I had at hand!), a couple years later discovered &lt;em&gt;Visual Basic&lt;/em&gt; and learned to love it, hate it and eventually forgot it — I thought, erroneously, that I&amp;rsquo;d never need it again.&lt;/p&gt;
&lt;p&gt;After that, I went to the University (not &lt;em&gt;this&lt;/em&gt; time, the &lt;em&gt;first&lt;/em&gt; time) and learned about the existence of &lt;em&gt;Linux&lt;/em&gt;. &lt;em&gt;Free Software&lt;/em&gt; philosophy, &lt;em&gt;bash&lt;/em&gt;, &lt;em&gt;Python&lt;/em&gt; amongst other things followed that discovery. I almost immediately fell in love with the Operative System —specially when one of the best professors I&amp;rsquo;ve ever had introduced me to &lt;em&gt;Debian&lt;/em&gt;—. Have used nearly nothing else for my daily life since then.&lt;/p&gt;
&lt;p&gt;When I got back to study at another University, notions of scientific programming turned out to be part of the Mathematics curriculum which got me to learn how to program in &lt;em&gt;C&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Currently, I&amp;rsquo;m at yet &lt;em&gt;another&lt;/em&gt; University (this time just an abroad semester) where I get to learn how to program re-usable, generic and efficient scientific software in &lt;em&gt;C++&lt;/em&gt; and some &lt;em&gt;Java&lt;/em&gt; which I had already learned for personal purposes.&lt;/p&gt;
&lt;h1 id="at-work"&gt;At work&lt;/h1&gt;
&lt;p&gt;I&amp;rsquo;m lazy… yep, not a good thing to say (specially if the section is called &lt;em&gt;&amp;ldquo;At &lt;strong&gt;work&lt;/strong&gt;&amp;ldquo;&lt;/em&gt;) but there&amp;rsquo;s more to it than just the definition of lazy.
The thing is: I strongly dislike doing things inefficiently.&lt;/p&gt;
&lt;p&gt;The way I see it: the faster I finish doing the things I don&amp;rsquo;t enjoy as much, the faster I get to do something more interesting — or even &lt;em&gt;nothing&lt;/em&gt; at all!&lt;/p&gt;
&lt;p&gt;That would be optimal in an objective-driven environment, which sadly is not the case at my current job, therefore I&amp;rsquo;m just… well, more productive.&lt;/p&gt;
&lt;p&gt;So, how do I get to be more productive? I just assume that for every single repetitive thing that has to be done than once manually, someone will probably make a mistake at some point — most likely more than once and each time in different sneaky ways.&lt;/p&gt;
&lt;p&gt;If the cost in time of automating the task (and then finishing the task in this more automatic way) is roughly equal to the time it takes someone to do the task by hand (including an error check). I just go for it and automate it… Not only I get to have some fun, but I get to be sure there are no errors (or know the kind of errors there may be) and the best thing: it may even be useful later and save us a few hours!&lt;/p&gt;
&lt;p&gt;At first, of course, I had no idea how to do such things, nor was I able to estimate the time cost of a task accurately, the latter ability came with time and experience, the former was product of my already mentioned insatiable curiosity and a few hours of research plus tons of hours of practise — most of those hours were my own personal time or dead time at a company.&lt;/p&gt;
&lt;h1 id="summing-up"&gt;Summing up&lt;/h1&gt;
&lt;p&gt;I had the privilege of being in touch with computers from an early age and always had an interest for programming, being something that somehow suits my personality and way of doing things.&lt;/p&gt;
&lt;p&gt;My studies have brought me somewhat closer to the programming world, and by chance my job allows me to do some programming somewhat regularly…
Because, who on their right mind would argue against doing something in a day rather than three or four?&lt;/p&gt;</content></entry><entry><title>Migrating Windows (or any partition) to a Virtual Machine</title><link href="https://evilham.eu/en/blog/2012-Migrating-Windows-to-a-virtual-machine/" rel="alternate"></link><updated>2012-05-09T00:00:00Z</updated><author><name></name></author><id>urn:uuid:0203eaf3-68c3-341d-8f58-257283168c5c</id><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;At the company I work for, being sick of making clean Windows installs, we decided to willingly violate Windows XP&amp;rsquo;s EULA for the greater good and put together a few open-source tools (basically &lt;code&gt;ntfsclone&lt;/code&gt;, &lt;code&gt;ntfsreloc&lt;/code&gt;, &lt;code&gt;ntfsresize&lt;/code&gt;, &lt;code&gt;gparted&lt;/code&gt; and of course, Linux), wrote a couple of witty scripts and came out with a &amp;ldquo;free&amp;rdquo; and nearly legal way of (re)installing Windows on our machines.&lt;/p&gt;
&lt;p&gt;Such a method consists basically of having in each machine besides the live Windows installation, a striped out Linux system with a backup image of its Windows (legally registered!). Of course, to save time, we sometimes use that same image to install Windows in more than one than one machine and once it&amp;rsquo;s been installed, we change the license data and create a new internal backup image with its own license info.&lt;/p&gt;
&lt;p&gt;So, I have that set up on my machine as well, except that I have a full, lovely, amazingly useful &lt;a href="http://debian.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Debian&lt;/a&gt; installation. The problem is, that I can&amp;rsquo;t be bothered to close, abandon whatever I&amp;rsquo;m doing and leave my happy place just to boot 5 minutes into Windows, figure out how to do something or test a new script and boot back into Linux to resume my other activities.&lt;/p&gt;
&lt;p&gt;Here comes &lt;strong&gt;Virtualisation&lt;/strong&gt; to the rescue, being something I had played with in the past, it wasn&amp;rsquo;t totally new and I already knew about the different options out there and their pros and cons.&lt;/p&gt;
&lt;p&gt;So I decided to give it a shot, but once again, making a fresh Windows install with all of the software needed to make it useful… is just too much of a burden.&lt;/p&gt;
&lt;h1 id="table-of-contents"&gt;Table of Contents&lt;/h1&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#issue"&gt;Issue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#solution"&gt;Solution&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#analysis"&gt;Analysis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#hard-drives"&gt;Hard drives&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#master-boot-record"&gt;Master Boot Record&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#partition-table"&gt;Partition Table&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#real-case-mbr"&gt;Real case MBR&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#generating-the-mbr"&gt;Generating the MBR&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#joining-the-new-mbr-and-the-image"&gt;Joining the new MBR and the image&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#use-the-image"&gt;Use the image&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1 id="issue"&gt;Issue&lt;/h1&gt;
&lt;p&gt;Summing up, I&amp;rsquo;ve got a Linux installation (which will be the Host OS) and a gzipped &lt;code&gt;ntfsclone&lt;/code&gt; Windows XP image (which will, of course, be the Guest OS).&lt;/p&gt;
&lt;p&gt;That being, nearly the equivalent of having a &lt;em&gt;live&lt;/em&gt; Windows installation, and wanting to migrate it to a Virtual Machine.&lt;/p&gt;
&lt;p&gt;There are already several articles about how to do that, but none of the ones I found solved a very simple issue:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can&amp;rsquo;t just get the image of a disk partition and boot it!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As a matter of fact, the lovely guys at &lt;a href="http://virtualbox.org" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;VirtualBox&lt;/a&gt; already tell you &lt;a href="https://virtualbox.org/wiki/Migrate_Windows" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;that exactly&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Either pull the drive from the windows machine or copy the data with a low level image tool (like dd) to a USB drive or other removable media. If making an image, DO NOT image just the partition, this will not work!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What they seem to be suggesting here, is that you make the image of the full hard drive (as opposed of just one partition), just to get the piece of it you want. That may mean having a 240 Gb of raw data, instead of just the 25 Gb you&amp;rsquo;re interested in… That&amp;rsquo;s nearly 90% inefficiency.&lt;/p&gt;
&lt;h1 id="solution"&gt;Solution&lt;/h1&gt;
&lt;h2 id="analysis"&gt;Analysis&lt;/h2&gt;
&lt;p&gt;To get around this, we need to understand why there is this problem to begin with.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll see, it&amp;rsquo;s not as simple as &lt;em&gt;that&lt;/em&gt; virtualisation software not wanting to boot your image because it is a faulty, buggy or incomplete program (as many would dare to suggest without digging in any further!); the problem here, is how hard drives work, how they were designed and how a &lt;em&gt;real&lt;/em&gt; computer understands them.&lt;/p&gt;
&lt;p&gt;There are many good articles around, and it&amp;rsquo;s not my intention at all to duplicate that information, so I&amp;rsquo;m just going to make a quick introduction to the topics, in order for the solution to make a bit of sense.&lt;/p&gt;
&lt;h2 id="hard-drives"&gt;Hard drives&lt;/h2&gt;
&lt;p&gt;So, nowadays, everyone knows that a hard drive can have several partitions that look like &amp;ldquo;different hard drives&amp;rdquo; inside of the Operative System, but the information about the different partitions, their format, size, position in the physical hard drive, etc. has to be stored &lt;em&gt;somewhere&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Where? Well, most likely you&amp;rsquo;ve heard about the infamous &lt;a href="http://en.wikipedia.org/wiki/Partition_table" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Partition Table&lt;/a&gt; or &lt;a href="http://en.wikipedia.org/wiki/Master_boot_record" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Master Boot Record (MBR)&lt;/a&gt;, and I say &lt;strong&gt;infamous&lt;/strong&gt; because probably the one time you heard about those things, you had to curse a lot due to data loss or all those wasted hours.&lt;/p&gt;
&lt;p&gt;There we have it, there&amp;rsquo;s a mystical thing at the very beginning of our hard drive describing where our partitions are and how they are!&lt;/p&gt;
&lt;p&gt;Then, when we try to get our Virtual Machine to boot that partition image we made, it&amp;rsquo;ll complain about it not being a properly formatted disk or something amongst those lines. Of course, there&amp;rsquo;s no MBR!&lt;/p&gt;
&lt;h2 id="master-boot-record"&gt;Master Boot Record&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s pretty well &lt;a href="http://en.wikipedia.org/wiki/Master_boot_record" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;explained&lt;/a&gt; on Wikipedia, but it&amp;rsquo;s full of historical data and things that, whilst being interesting, are not related to our goal. I&amp;rsquo;d recommend then, taking a look at &lt;a href="http://www.datarescue.com/laboratory/partition.htm" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;these&lt;/a&gt; &lt;a href="http://thestarman.pcministry.com/asm/mbr/PartTables.htm" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;articles&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In short, the MBR is a 512 byte long section with a standard structure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Code area (0x000&amp;mdash;0x1BD)&lt;/li&gt;
&lt;li&gt;Partition Table (0x1BE&amp;mdash;0x1FD)&lt;/li&gt;
&lt;li&gt;Boot Record Signature (0x1FE-0x1FF).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those hex numbers in parentheses, correspond to the &lt;strong&gt;offset&lt;/strong&gt; within the MBR in which the sections are located, which is also the absolute offset.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll see later that we only have to worry about the Partition Table, so let&amp;rsquo;s take a look into it.&lt;/p&gt;
&lt;h2 id="partition-table"&gt;Partition Table&lt;/h2&gt;
&lt;p&gt;The Partition Table is really where the information about our disk partitions is written, it has enough room to define &lt;strong&gt;four&lt;/strong&gt; partitions called &lt;strong&gt;primary&lt;/strong&gt; partitions, one of those can be an &lt;strong&gt;extended&lt;/strong&gt; partition, which will contain another partition table with information about all the &lt;strong&gt;logical&lt;/strong&gt; partitions, but we don&amp;rsquo;t really care much about it right now, if interested read the articles above or make a quick internet search.&lt;/p&gt;
&lt;p&gt;This sector then, has also a standard structure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Entry for Primary Partition #1 (0x1BE&amp;mdash;0x1CD).&lt;/li&gt;
&lt;li&gt;Entry for Primary Partition #2 (0x1CE&amp;mdash;0x1DD).&lt;/li&gt;
&lt;li&gt;Entry for Primary Partition #3 (0x1DE&amp;mdash;0x1ED).&lt;/li&gt;
&lt;li&gt;Entry for Primary Partition #4 (0x1EE&amp;mdash;0x1FD).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That means, that whatever defines the first primary partition, is between sectors 0x1BE and 0x1CD of the MBR.&lt;/p&gt;
&lt;p&gt;Those entries have, of course, a structure that is better explained &lt;a href="http://thestarman.pcministry.com/asm/mbr/PartTables.htm" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;here&lt;/a&gt;, but here it goes for completeness&amp;rsquo; sake:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Partition State: 0x80 if it&amp;rsquo;s the boot partition, 0x00 otherwise (1 byte).&lt;/li&gt;
&lt;li&gt;Starting sector CHS coordinates (3 bytes).&lt;/li&gt;
&lt;li&gt;Partition Type (1 byte).&lt;/li&gt;
&lt;li&gt;Ending sector CHS coordinates (3 bytes).&lt;/li&gt;
&lt;li&gt;Starting sector LBA coordinates (4 bytes).&lt;/li&gt;
&lt;li&gt;Partition length in sectors (4 bytes).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What are those CHS, LBA things you ask. Well, in þe old times, it was actually needed to refer to a disk sector by its &lt;a href="http://en.wikipedia.org/wiki/Cylinder-head-sector#CHS_to_LBA_mapping" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;CHS coordinates (Cylinder, Head, Sector)&lt;/a&gt; which is hardware-dependant. However, nowadays software cares more about LBA (Logical Block Addressing) because it&amp;rsquo;s easier and the abstraction layers do the hard part.&lt;/p&gt;
&lt;p&gt;Also, as &lt;a href="http://lists.freebsd.org/pipermail/freebsd-questions/2004-January/033376.html" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;Dan Strick&lt;/a&gt; said on the FreeBSD mail list (and I believe it just because my BIOS agrees):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Modern BIOS geometry most frequently uses 255 heads and 63 sectors/track
because that maximizes the addressable part of the disk drive using the
basic int13 function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="real-case-mbr"&gt;Real case MBR&lt;/h2&gt;
&lt;p&gt;Cool, we now know that there are three things we need, and roughly how they are, but it was all too abstract, so, as an instructive exercise, why don&amp;rsquo;t you go to your terminal and execute&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;dd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/sda&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;hd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;less
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that you may get into permission errors, just turn root, use sudo or get privileges to the disk group or whatever helps you get raw access to the disk.
Also note that if you mess up the &lt;code&gt;if=&lt;/code&gt; and write &lt;code&gt;of=&lt;/code&gt; instead, you may be killing your MBR :), read &lt;code&gt;man dd&lt;/code&gt; for more info.&lt;/p&gt;
&lt;p&gt;So, mine looks a bit like this (I skipped a part as it&amp;rsquo;s mostly incomprehensible):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nl"&gt;00000000&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;eb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;63&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;90&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;d0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;bc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;7c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;8e&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;c0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;8e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;d8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;7c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;bf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;.c....|......|..&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;00000010&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;06&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;b9&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;02&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;fc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;f3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;a4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;50&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;68&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;1c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;06&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;cb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;fb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;b9&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;04&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;.......Ph.......&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;00000020&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;bd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;07&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;80&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;7e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;7c&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;0b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;85&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;83&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;c5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;10&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;....~..|........&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;00000170&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;95&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;7d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;e8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;34&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;9a&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;7d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;e8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;2e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;18&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;eb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;fe&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;..}.4...}.......&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;00000180&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;47&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;52&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;55&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;42&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;47&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;65&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;6f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;6d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;48&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;61&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;72&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;GRUB .Geom.Hard &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;00000190&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;44&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;69&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;73&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;6b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;52&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;65&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;61&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;45&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;72&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;72&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;6f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;72&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;Disk.Read. Error&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;000001a0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;0d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;bb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;b4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0e&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;ac&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;3c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;75&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;f4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;c3&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;...........&amp;lt;.u..&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;000001b0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;f7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;a4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;85&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;a3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;2f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;d2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;80&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;.........J..... &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;000001c0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;17&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;fe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;ff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;ff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;08&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;80&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;02&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;fe&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;!...............&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;000001d0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;ff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;ff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;83&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;fe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;ff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;ff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;73&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0a&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;80&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;02&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;92&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;69&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;04&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;fe&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;......s....i....&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;000001e0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;ff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;ff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;17&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;fe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;ff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;ff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;05&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;74&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;84&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;02&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;c1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;3e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;fe&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;.......t...&amp;gt;....&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;000001f0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;ff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;ff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;05&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;fe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;ff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;ff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;fe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;bf&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;84&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;02&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;02&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;c8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;1c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;55&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;aa&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;..............U.&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The interesting part is at offset 0x1b0, which is the row in which the partition table starts, notice the section at 0x1B8, where we see &lt;code&gt;f7 a4 85 a3 2f d2&lt;/code&gt;, that&amp;rsquo;d be this disk&amp;rsquo;s identifier (I must confess I don&amp;rsquo;t know if, or how this is important) and right after that, starting at 0x1BE, we find the start of the partition table.&lt;/p&gt;
&lt;p&gt;If we try the same thing (hexdump the first 512 bytes) on our image (again, some bits have been skipped):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nl"&gt;00000000&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;eb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;52&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;90&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;4e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;54&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;46&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;53&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;02&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;08&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;.R.NTFS    .....&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;00000010&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;f8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;3f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;ff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;08&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;........?.......&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;00000020&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;80&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;80&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;f8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;ff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;7f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;02&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;................&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;00000030&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;8e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;f0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;1b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;................&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;00000180&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;eb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;f2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;c3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;45&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;72&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;72&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;6f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;72&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;65&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;6c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;65&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;.....Error de le&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;00000190&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;63&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;74&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;75&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;72&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;61&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;65&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;69&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;73&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;63&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;6f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0d&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;ctura de disco..&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;000001a0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;0a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;46&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;61&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;6c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;74&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;61&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;4e&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;54&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;4c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;44&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;52&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;4e&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;.Falta NTLDR...N&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;000001b0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;54&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;4c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;44&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;52&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;63&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;6f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;6d&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;70&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;72&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;69&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;6d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;69&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;6f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;TLDR comprimido.&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;000001c0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;0d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;50&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;72&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;65&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;73&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;69&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;6f&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;6e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;65&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;43&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;74&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;72&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;6c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;2b&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;..Presione Ctrl+&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;000001d0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;41&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;6c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;74&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;2b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;53&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;75&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;70&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;72&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;70&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;61&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;72&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;61&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;72&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;65&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;Alt+Supr para re&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;000001e0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;69&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;6e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;69&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;63&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;69&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;61&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;72&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0d&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;0a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;iniciar.........&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;000001f0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;83&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;9f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;ad&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;c0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;55&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;aa&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;..............U.&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which doesn&amp;rsquo;t look like a partition table… Now, that&amp;rsquo;d explain why our virtualisation software refuses to boot it!&lt;/p&gt;
&lt;h2 id="generating-the-mbr"&gt;Generating the MBR&lt;/h2&gt;
&lt;p&gt;As mentioned before, we only have to worry about the partition table, but we do need a valid code area; luckily, there is already some software available to do it for us. That&amp;rsquo;d be &lt;a href="http://ms-sys.sourceforge.net" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;ms-sys&lt;/a&gt;, which, by the way is not packaged by Debian due to license issues (citation needed; read it long ago, can&amp;rsquo;t be arsed to look for it now) but it&amp;rsquo;s just a matter of downloading the source code and compiling.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ms-sys&lt;/code&gt; has several options, the one I&amp;rsquo;m interested in is &lt;code&gt;-m&lt;/code&gt;. Now, turns out, &lt;code&gt;ms-sys&lt;/code&gt; needs a file to write the data, so let&amp;rsquo;s create a zeroed one.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;dd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/zero&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mymbr&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2048&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;./ms-sys&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;mymbr
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice the -f argument, if it weren&amp;rsquo;t there, &lt;code&gt;ms-sys&lt;/code&gt; would complain about the file not being a disk device, but it&amp;rsquo;s ok, we know (or hope we know) what we&amp;rsquo;re doing. Also, we created a 1MB zeroed file (&lt;code&gt;count=2048&lt;/code&gt; in &lt;code&gt;dd&lt;/code&gt;), that&amp;rsquo;s because it&amp;rsquo;ll be the start of our image, and leaving 1MB at the beginning seems to be a sane thing to do (e.g. &lt;code&gt;gparted&lt;/code&gt; does it that way).&lt;/p&gt;
&lt;p&gt;Having done that, we get:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nl"&gt;00000180&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;................&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="p"&gt;*&lt;/span&gt;
&lt;span class="nl"&gt;000001b0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;2c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;44&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;63&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;.....,Dc........&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;000001c0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;................&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="p"&gt;*&lt;/span&gt;
&lt;span class="nl"&gt;000001f0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;55&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;aa&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;..............U.&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;00000200&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;................&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That is, no disk identifier, no partition table, but filled in code area and Boot Record Signature; not bad.&lt;/p&gt;
&lt;p&gt;We just have to fill in the missing data with a hex editor (e.g. &lt;code&gt;ghex&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;As for the disk identifier, I guess we can fill in those bytes (0x1B8&amp;mdash;0x1BD) pseudo-randomly, but have really no clue if that&amp;rsquo;s what dedicated software do :).&lt;/p&gt;
&lt;p&gt;So, as for the Partition Table, we just need to fill in the first entry (0x1BE&amp;mdash;0x1CD), as a single partition is all we need.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Partition State: 0x80 it&amp;rsquo;ll be the boot partition.&lt;/li&gt;
&lt;li&gt;Starting sector: 0x002021 the partition will start at 1MB, see details for CHS encoding &lt;a href="http://thestarman.pcministry.com/asm/mbr/PartTables.htm#Decoding" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Partition Type: 0x07 for NTFS, use &lt;code&gt;fdisk&lt;/code&gt; and then &lt;code&gt;l&lt;/code&gt; for more options.&lt;/li&gt;
&lt;li&gt;Ending sector: odds are, your partition is big enough as to not fit in these three bytes, in those cases 0xFEFFFF is what should be there.&lt;/li&gt;
&lt;li&gt;Starting sector: 0x00080000 that&amp;rsquo;d be 2048 as stored on a little-endian computer, it&amp;rsquo;s 2048 because our partition will start after 1MB (2048 sectors of 512 bytes).&lt;/li&gt;
&lt;li&gt;Partition length: this will vary depending on your partition size, first find out the size of your partition in bytes and then divide it by 512, or better yet, find the size of your partition in sectors! Hint: &lt;code&gt;man fdisk&lt;/code&gt;, &lt;code&gt;man ls&lt;/code&gt;. This field must be encoded as stored on a little-endian computer as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After all that mess, my partition entry looks like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nl"&gt;000001b0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;2c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;44&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;63&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;f2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;aa&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;f3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;83&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;80&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;.....,Dc........&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;000001c0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;07&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;fe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;ff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;ff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;08&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;d8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;54&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;02&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt; !..........T...&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="nl"&gt;000001d0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s"&gt;................&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="joining-the-new-mbr-and-the-image"&gt;Joining the new MBR and the image&lt;/h2&gt;
&lt;p&gt;So, we make a copy of the &lt;code&gt;mymbr&lt;/code&gt; file as &lt;code&gt;myimg.hdd&lt;/code&gt; for example. Remember that we created this file as a zeroed 1MB file and then edited the first 512 bytes corresponding to the MBR.&lt;/p&gt;
&lt;p&gt;And then, we create, extract, convert or whatever the image, and add it to that file using &lt;code&gt;dd&lt;/code&gt;, in my case:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;gunzip&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;Image.gz&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ntfsclone&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;-O&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myimg.hdd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1048576&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;seek&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;gunzip&lt;/code&gt; and &lt;code&gt;ntfsclone&lt;/code&gt; parts are just because that&amp;rsquo;s how I have the image already, in practise you just need to pass the raw data to &lt;code&gt;dd&lt;/code&gt; via standard input or create the image directly by indicating the input file (see &lt;code&gt;man dd&lt;/code&gt;).
Notice the arguments &lt;code&gt;bs=1048576 seek=1&lt;/code&gt; for &lt;code&gt;dd&lt;/code&gt;, here, we&amp;rsquo;re telling &lt;code&gt;dd&lt;/code&gt; to start writing on &lt;code&gt;myimg.hdd&lt;/code&gt; after 1MB, leaving our 512 byte long MBR plus some zero data intact; by the way, by increasing the block size, the whole process is considerably faster.&lt;/p&gt;
&lt;p&gt;Also, do note that we can overwrite the MBR of this newly created virtual hard drive at any time:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;dd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mymbr&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myimg.hdd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;conv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notrunc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that if you leave out the &lt;code&gt;conv=notrunc&lt;/code&gt; parameter, you&amp;rsquo;ll lose all your precious data.&lt;/p&gt;
&lt;h2 id="use-the-image"&gt;Use the image&lt;/h2&gt;
&lt;p&gt;Now just use whatever virtualisation solution you want with this raw image or maybe even convert it to another format (like &lt;code&gt;vdi&lt;/code&gt;), it&amp;rsquo;s a perfectly valid virtual hard drive with a single partition.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;It does look like quite a mess, but creating a virtual hard drive with a bootable partition from an image or a real hard drive just takes around two minutes, that is, not taking into account the time-consuming &lt;code&gt;dd&lt;/code&gt; step, but then again, there we just have to sit back and relax.&lt;/p&gt;
&lt;p&gt;Just as a side note, this does not guarantee by any means a migration from a live installation to a Virtual Machine, it does guarantee however that the virtualisation software will &lt;em&gt;try&lt;/em&gt; to boot that partition.&lt;/p&gt;
&lt;p&gt;As a matter of fact, my migration was &amp;ldquo;unsuccessful&amp;rdquo; at first as I got in VirtualBox the dreadful BSOD, but I could boot using &lt;code&gt;kvm&lt;/code&gt; &amp;mdash; albeit, the guest was pretty slow.&lt;/p&gt;
&lt;p&gt;It was caused by the IDE/ATA drivers. So I booted with &lt;code&gt;kvm&lt;/code&gt; and used the &lt;code&gt;MergeIDE&lt;/code&gt; solution mentioned &lt;a href="https://virtualbox.org/wiki/Migrate_Windows" referrerpolicy="no-referrer" rel="noopener noreferrer" target="_blank"&gt;here&lt;/a&gt;. After doing that, I&amp;rsquo;m able to boot into that image from VirtualBox  and it actually runs fast in &lt;code&gt;kvm&lt;/code&gt;.&lt;/p&gt;</content></entry></feed>