<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Perfectly Sane</title>
    <link rel="self" type="application/atom+xml" href="https://uninsane.org/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://uninsane.org"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2024-11-16T00:00:00+00:00</updated>
    <id>https://uninsane.org/atom.xml</id>
    <entry xml:lang="en">
        <title>Kernel Hacking + Device Bringup on NixOS</title>
        <published>2024-11-16T00:00:00+00:00</published>
        <updated>2024-11-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://uninsane.org/blog/nixos-kernel-hacking/"/>
        <id>https://uninsane.org/blog/nixos-kernel-hacking/</id>
        
        <content type="html" xml:base="https://uninsane.org/blog/nixos-kernel-hacking/">&lt;p&gt;one of my NixOS devices is a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;PinePhone_Pro&quot;&gt;PinePhone Pro&lt;&#x2F;a&gt;.
at time of writing, it boots to a working display&#x2F;touchscreen and even WiFi on stock NixOS,
however other basic features like audio and battery readouts aren&#x27;t available.
actually, these features &lt;em&gt;are&lt;&#x2F;em&gt; available on distros like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;postmarketos.org&#x2F;&quot;&gt;postmarketOS&lt;&#x2F;a&gt; which ship kernel forks
specifically for this. but maintaining a fork is at least a bit of work, and NixOS gives you lots
&lt;em&gt;better&lt;&#x2F;em&gt; ways to tweak your kernel than simply building the whole thing from a different source tree.&lt;&#x2F;p&gt;
&lt;p&gt;so, how best to develop and deploy kernel-level changes on a NixOS system?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;kernel-pain-points&quot;&gt;Kernel Pain Points&lt;&#x2F;h2&gt;
&lt;p&gt;skip ahead if you&#x27;re already familiar with the NixOS kernel options.
the straightforward way to ship custom kernels on NixOS is something like:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;{ pkgs, ... }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  boot&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;kernelPackages&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = pkgs.linuxPackagesFor (pkgs.buildLinux {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    src&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = fetchgit {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;      # ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;    # ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;or if your kernel patches are simple enough, then track the NixOS kernel and patch it instead:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;{ ... }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  boot&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;kernelPatches&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      name&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;my-kernel-patch&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      patch&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&#x2F;my-kernel-patch.patch&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;but the crushing downside to this approach is that tweaking just a single line in &lt;code&gt;my-kernel-patch.patch&lt;&#x2F;code&gt; forces
a complete rebuild of the kernel: your iteration cycle time might well be 30 minutes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;faster-kernel-iterations&quot;&gt;Faster Kernel Iterations&lt;&#x2F;h2&gt;
&lt;p&gt;until we get something like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;rfcs&#x2F;pull&#x2F;92&quot;&gt;dynamic derivations&lt;&#x2F;a&gt;, the path out of costly iteration is to ship whichever tweaks you&#x27;re pursuing via some other mechanism than as part of the top-level kernel derivation.
the kernel is fairly pluggable; consider:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;device tree files can be loaded at runtime.&lt;&#x2F;li&gt;
&lt;li&gt;kernel modules can be loaded at runtime.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;with the right config, either of these things can be freely modified without forcing a kernel build.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shipping-device-tree-patches&quot;&gt;Shipping Device Tree Patches&lt;&#x2F;h2&gt;
&lt;p&gt;Linux uses &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.kernel.org&#x2F;devicetree&#x2F;usage-model.html&quot;&gt;device tree&lt;&#x2F;a&gt; files to determine which peripherals are available on your
device and how to interface with them. it&#x27;s more relevant for embedded devices than for traditional x86 machines.&lt;&#x2F;p&gt;
&lt;p&gt;the typical boot flow for an embedded device is that the bootloader (U-Boot) knows the name of
the device it&#x27;s running on (perhaps because it was built &lt;em&gt;specifically&lt;&#x2F;em&gt; for that device) and communicates
this to the kernel when it hands over control of the device.
the kernel ships with device tree definitions for hundreds of different devices,
each with a name like &lt;code&gt;samsung,exynos7-espresso&lt;&#x2F;code&gt; or &lt;code&gt;pine64,pinephone-pro&lt;&#x2F;code&gt;,
and after learning which device it&#x27;s actually running on it applies the appropriate device tree,
instantiating and configuring device drivers accordingly.&lt;&#x2F;p&gt;
&lt;p&gt;device trees are composable by design; the spec defines the concept of an &quot;overlay&quot;,
such that the actual device tree to apply to a device should be the union of whatever&#x27;s
defined in the kernel source tree plus any overlays that apply to the same device.
NixOS makes this feature available via the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;search.nixos.org&#x2F;options?channel=unstable&amp;amp;show=hardware.deviceTree.overlays&amp;amp;from=0&amp;amp;size=50&amp;amp;sort=relevance&amp;amp;type=packages&amp;amp;query=hardware.deviceTree.overlays&quot;&gt;&lt;code&gt;hardware.deviceTree.overlays&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; option,
and a certain amount of our kernel changes can be expressed via this option instead of costly &lt;code&gt;.patch&lt;&#x2F;code&gt; files.&lt;&#x2F;p&gt;
&lt;p&gt;for example, the stock kernel misidentifies volume-down key presses as volume-up events on the PinePhone Pro.
distros like postmarketOS fix this by patching the device tree file like so:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;--- arch&#x2F;arm64&#x2F;boot&#x2F;dts&#x2F;rockchip&#x2F;rk3399-pinephone-pro.dts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+++ arch&#x2F;arm64&#x2F;boot&#x2F;dts&#x2F;rockchip&#x2F;rk3399-pinephone-pro.dts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@@ -48,11 +48,11 @@ &#x2F; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 	adc-keys {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 		compatible = &amp;quot;adc-keys&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 		io-channels = &amp;lt;&amp;amp;saradc 1&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 		io-channel-names = &amp;quot;buttons&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 		keyup-threshold-microvolt = &amp;lt;1600000&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 		poll-interval = &amp;lt;100&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 		button-down {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 			label = &amp;quot;Volume Down&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 			linux,code = &amp;lt;KEY_VOLUMEDOWN&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-deleted&quot;&gt;-			press-threshold-microvolt = &amp;lt;600000&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-inserted&quot;&gt;+			press-threshold-microvolt = &amp;lt;400000&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 		};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;this patch can alternately be represented as a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.kernel.org&#x2F;devicetree&#x2F;overlay-notes.html&quot;&gt;Device Tree Overlay&lt;&#x2F;a&gt; (DTO):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; file: rk3399-pinephone-pro-lradc-fix.dtso&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; boilerplate required by the device tree compiler.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;dts&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;v1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;plugin&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; instruct the system to only apply this overlay&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; to PinePhone Pro, and not any other devices.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	compatible &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;pine64,pinephone-pro&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; address the parent node we want to patch,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; and then inject a new property value.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; this overrides `press-threshold-microvolt`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; while leaving all other properties unchanged.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;adc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;keys&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;button&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;down} {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	press&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;threshold&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;microvolt &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;400000&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and then we can ship it in NixOS like so:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;{ ... }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  hardware&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;deviceTree&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;overlays&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      name&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;rk3399-pinephone-pro-lradc-fix&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      dtsFile&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&#x2F;rk3399-pinephone-pro-lradc-fix.dtso&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;after adding the above to a stock NixOS config, and deploying
to a PinePhone Pro, the volume down button should now be fixed!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shipping-custom-kernel-modules&quot;&gt;Shipping Custom Kernel Modules&lt;&#x2F;h2&gt;
&lt;p&gt;one of the less trivial patches yet to be mainlined is support for
battery monitoring on the PinePhone Pro (i.e. viewing the charge level
and the charge&#x2F;discharge rate). this feature is supported
in downstream kernels by shipping two new kernel modules:
&lt;code&gt;rk818_battery&lt;&#x2F;code&gt; and &lt;code&gt;rk818_charger&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;these modules are device drivers: they detect a &lt;code&gt;rk818-battery&lt;&#x2F;code&gt;
device somewhere on the system, and then run code in response
to that (namely, enable some voltage regulators, and create sysfs
nodes to let userspace interact with the device).&lt;&#x2F;p&gt;
&lt;p&gt;code wise, the typical driver is a single .c file with few if any
direct dependencies on other drivers. most driver modules are
standalone in the same sense that each nix package is standalone.&lt;&#x2F;p&gt;
&lt;p&gt;then, we can ship new kernel modules by:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;defining a nix derivation that compiles the kernel module.&lt;&#x2F;li&gt;
&lt;li&gt;telling nix to deploy that on the kernel&#x27;s module search path.&lt;&#x2F;li&gt;
&lt;li&gt;instructing the kernel to load our module.&lt;&#x2F;li&gt;
&lt;li&gt;shipping a device tree overlay to associate the relevant devices
with the driver our module provides.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;building-a-kernel-module&quot;&gt;Building a Kernel Module&lt;&#x2F;h3&gt;
&lt;p&gt;my kernel module has 3 files, which live in my nix repo inline:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.uninsane.org&#x2F;colin&#x2F;nix-files&#x2F;src&#x2F;commit&#x2F;4e008c34208143a489d824b288437201f7137ace&#x2F;pkgs&#x2F;linux-packages&#x2F;rk818-charger&#x2F;rk818_battery.c&quot;&gt;rk818_battery.c&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.uninsane.org&#x2F;colin&#x2F;nix-files&#x2F;src&#x2F;commit&#x2F;4e008c34208143a489d824b288437201f7137ace&#x2F;pkgs&#x2F;linux-packages&#x2F;rk818-charger&#x2F;rk818_battery.h&quot;&gt;rk818_battery.h&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.uninsane.org&#x2F;colin&#x2F;nix-files&#x2F;src&#x2F;commit&#x2F;4e008c34208143a489d824b288437201f7137ace&#x2F;pkgs&#x2F;linux-packages&#x2F;rk818-charger&#x2F;rk818_charger.c&quot;&gt;rk818_charger.c&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;these are compiled into &lt;code&gt;rk818_battery.ko&lt;&#x2F;code&gt; and &lt;code&gt;rk818_charger.ko&lt;&#x2F;code&gt; by a Makefile:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;make&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;obj-m&lt;&#x2F;span&gt;&lt;span&gt; := rk818_battery.o rk818_charger.o&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;	$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;MAKE&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt; -C &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;KERNEL_DIR&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; M=&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;PWD&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; modules&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;install&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	install -Dm444 rk818_battery.ko &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt;\&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;		$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;INSTALL_MOD_PATH&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;drivers&#x2F;power&#x2F;supply&#x2F;rk818_battery.ko&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	install -Dm444 rk818_charger.ko &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt;\&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;		$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;INSTALL_MOD_PATH&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;drivers&#x2F;power&#x2F;supply&#x2F;rk818_charger.ko&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;this can be packaged for nix as so:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;# file: pkgs&#x2F;linux-packages&#x2F;rk818-charger&#x2F;default.nix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;  buildPackages,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;  kernel,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;  lib,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;  stdenv,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;}: stdenv.mkDerivation {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  pname&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;rk818-charger&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  version&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;0-unstable-2024-10-01&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  src&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&#x2F;.&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  hardeningDisable&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;pic&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  nativeBuildInputs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = kernel.moduleBuildDependencies;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  makeFlags&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;    &amp;quot;KERNEL_DIR=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;kernel.dev&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;lib&#x2F;modules&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;kernel.modDirVersion&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation&quot;&gt;&#x2F;build&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;    &amp;quot;INSTALL_MOD_PATH=$(out)&#x2F;lib&#x2F;modules&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;kernel.modDirVersion&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation&quot;&gt;&#x2F;kernel&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;    # from pkgs&#x2F;os-specific&#x2F;linux&#x2F;kernel&#x2F;manual-config.nix:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;    &amp;quot;O=$(buildRoot)&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;    &amp;quot;CC=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;stdenv.cc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;bin&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;stdenv.cc.targetPrefix&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation&quot;&gt;cc&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;    &amp;quot;HOSTCC=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;buildPackages.stdenv.cc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;bin&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;buildPackages.stdenv.cc.targetPrefix&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation&quot;&gt;cc&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;    &amp;quot;HOSTLD=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;buildPackages.stdenv.cc.bintools&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;bin&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;buildPackages.stdenv.cc.targetPrefix&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation&quot;&gt;ld&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;    &amp;quot;ARCH=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;stdenv.hostPlatform.linuxArch&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt;  ] ++ lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform) [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;    &amp;quot;CROSS_COMPILE=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;stdenv.cc.targetPrefix&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;  # NixOS kernel expects compressed kernel modules, so do that here.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  postInstall&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    find $out -name &amp;#39;*.ko&amp;#39; -exec xz {} \;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;then add this package to nixpkgs&#x27; &lt;code&gt;linuxKernel&lt;&#x2F;code&gt; package set:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;{ ... }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  nixpkgs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;overlays&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = [(self: super: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    linuxKernel&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = super.linuxKernel &#x2F;&#x2F; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      packagesFor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = kernel:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;          super.linuxKernel.packagesFor kernel&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt;        ).extend (kFinal: kPrev:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; with&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt; kFinal; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;          rk818-charger&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = callPackage&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&#x2F;pkgs&#x2F;linux-packages&#x2F;rk818-charger&lt;&#x2F;span&gt;&lt;span&gt; { };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  })];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;kernel modules must be built against the same kernel version
that they&#x27;ll be loaded by: this &lt;code&gt;linuxKernel&lt;&#x2F;code&gt; package set looks
funny at first, but it exists to make that easier.&lt;&#x2F;p&gt;
&lt;p&gt;your kernel module can be built like this:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;nix-build -A linuxPackages_6_11.rk818-charger&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;nix-build -A linuxPackages_6_10.rk818-charger&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;and so on, depending on which kernel you&#x27;re running.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;deploying-a-kernel-module&quot;&gt;Deploying a Kernel Module&lt;&#x2F;h3&gt;
&lt;p&gt;add the following to your NixOS config:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;{ config, ... }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;  # this builds our modules against the specific kernel in use by the host&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;  # and makes them available under &#x2F;run&#x2F;current-system&#x2F;kernel-modules&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;  # where they can be found by tools like `modprobe`.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  boot&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;extraModulePackages&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;    config.boot.kernelPackages.rk818-charger&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;  # explicitly load the modules during stage-2 boot.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;  # if they&amp;#39;re needed earlier in the boot process,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;  # then use `boot.initrd.kernelModules` instead.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  boot&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;kernelModules&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;    &amp;quot;rk818_battery&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;    &amp;quot;rk818_charger&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;now we&#x27;ve recreated the same behavior as if the kernel module had been shipped in-tree,
but we still have to associate the driver with a specific device if we want it to do anything.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;device-tree-module-integration&quot;&gt;Device Tree + Module Integration&lt;&#x2F;h3&gt;
&lt;p&gt;add the following Device Tree Overlay to &lt;code&gt;hardware.deviceTree.overlays&lt;&#x2F;code&gt;,
as we did earlier with the battery DTO:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;dts&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;v1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;plugin&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; apply the overlay only to PinePhone Pro; no other devices.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	compatible &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;pine64,pinephone-pro&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;} {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	bat: battery {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		compatible &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;simple-battery&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		voltage&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;min&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;design&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;microvolt &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;3400000&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		voltage&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;max&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;design&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;microvolt &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4350000&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;rk818 {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	battery {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;		&#x2F;&#x2F; this line associates this device with the rk818-battery module&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;		&#x2F;&#x2F; we just shipped&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		compatible &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;rockchip,rk818-battery&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;		&#x2F;&#x2F; constants were taken from the pine64-org kernel tree:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		ocv_table &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;3400 3675 3689 3716 3740 3756 3768 3780&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;			3793 3807 3827 3853 3896 3937 3974 4007 4066&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;			4110 4161 4217 4308&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		design_capacity &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;2916&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		design_qmax &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;2708&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		bat_res &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;150&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		max_input_current &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;3000&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		max_chrg_current &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		max_chrg_voltage &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4350&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		sleep_enter_current &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;300&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		sleep_exit_current &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;300&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		power_off_thresd &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;3400&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		zero_algorithm_vol &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;3950&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		fb_temperature &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;105&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		sample_res &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;10&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		max_soc_offset &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;60&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		energy_mode &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		monitor_sec &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;5&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		virtual_power &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		power_dc2otg &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		otg5v_suspend_enable &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	charger {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;		&#x2F;&#x2F; this line associates this device with the rk818-charger module&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;		&#x2F;&#x2F; we just shipped&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		compatible &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;rockchip,rk818-charger&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		monitored&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;battery &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;= &amp;lt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;bat&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;patching-an-upstream-kernel-module&quot;&gt;Patching an Upstream Kernel Module&lt;&#x2F;h2&gt;
&lt;p&gt;this all works, however the PinePhone Pro battery integration
requires not just a new kernel module, but also to patch an existing
kernel module (&lt;code&gt;rk8xx-i2c&lt;&#x2F;code&gt;). since the kernel module is defined in-tree, the natural
way to do that would force a kernel rebuild every time we adjust the patches.&lt;&#x2F;p&gt;
&lt;p&gt;we can build our patched module out-of-tree using the same method
we used to build a wholly new module out-of-tree (write the derivation
as some &lt;code&gt;patches&lt;&#x2F;code&gt; atop &lt;code&gt;src = linux-latest&lt;&#x2F;code&gt;, or forget about tracking
patches and just copy the entire module source into our repo).
then we just configure the kernel to prefer our module over its in-tree module.&lt;&#x2F;p&gt;
&lt;p&gt;a first approach might be to configure the kernel with &lt;code&gt;CONFIG_MFD_RK8XX_I2C=n&lt;&#x2F;code&gt;,
then ship our module as above. this works:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;{ lib, ... }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  boot&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;kernelPatches&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      name&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;rk8xx-i2c-out-of-tree&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      patch&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; null&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      extraStructuredConfig&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; with&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt; lib.kernel; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;        MFD_RK8XX_I2C&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = no;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;this will result in &lt;em&gt;one&lt;&#x2F;em&gt; kernel rebuild, and then you can freely
edit your out-of-tree &lt;code&gt;rk8xx-i2c&lt;&#x2F;code&gt; module without costly rebuilds.
if you find yourself patching a lot of modules, then it may be
preferable to simply build &lt;em&gt;all&lt;&#x2F;em&gt; in-tree modules as dynamic modules,
and configure out-of-tree modules to take precedence over the in-tree ones.
remove the &lt;code&gt;MFD_RK8XX_I2C = no&lt;&#x2F;code&gt; patch and replace it with this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;{ ... }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  nixpkgs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;hostPlatform&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;linux-kernel&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;    # build every module known to the mainline kernel&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    autoModules&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;    # and build them as dynamically loaded modules (`=m`), not builtins (`=y`)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    preferBuiltin&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;  # default nixos behavior is to error if a kernel module is provided by more&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;  # than one package. but we&amp;#39;re doing that intentionally, so patch the logic&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;  # from &amp;lt;nixos&#x2F;modules&#x2F;system&#x2F;boot&#x2F;kernel.nix&amp;gt; (AKA pkgs.aggregateModules)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;  # to not complain.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  system&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;modulesTree&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = lib.mkForce [(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;    (pkgs.aggregateModules&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;      (config.boot.extraModulePackages ++ [ config.boot.kernelPackages.kernel ])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt;    ).overrideAttrs {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;      # when ignoring collisions, order becomes important:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;      # earlier items (config.boot.extraModulePackages) override later items&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;      # (config.boot.kernelPackages.kernel).&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      ignoreCollisions&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;more examples for this method can be found on the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.nixos.org&#x2F;wiki&#x2F;Linux_kernel#Patching_a_single_In-tree_kernel_module&quot;&gt;nixos wiki&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;and there you have it! several techniques for working with device trees and kernel
modules without suffering lengthy builds.
if you found anything here useful, then my request to you
is to upstream your kernel work so that the next reader of this blog doesn&#x27;t have
to go through the same pain 😛&lt;&#x2F;p&gt;
&lt;p&gt;complete nix expressions for my PinePhone Pro system can be found &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.uninsane.org&#x2F;colin&#x2F;nix-files&#x2F;src&#x2F;commit&#x2F;4e008c34208143a489d824b288437201f7137ace&#x2F;hosts&#x2F;modules&#x2F;hal&#x2F;pine64-pinephone-pro&#x2F;default.nix#L59&quot;&gt;here&lt;&#x2F;a&gt;
in case i missed some crucial detail in this writeup.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Wake-on-LAN and Push Notifications in Mobile Linux</title>
        <published>2023-12-09T00:00:00+00:00</published>
        <updated>2023-12-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://uninsane.org/blog/mobile-linux-push-notifications/"/>
        <id>https://uninsane.org/blog/mobile-linux-push-notifications/</id>
        
        <content type="html" xml:base="https://uninsane.org/blog/mobile-linux-push-notifications/">&lt;p&gt;the more i carry my Pinephone for long stretches, the more i feel limited by battery life. there&#x27;s a lot one can do to deal with that, and among those options is to reduce idle power draw.&lt;&#x2F;p&gt;
&lt;p&gt;if you use a desktop environment like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sxmo.org&quot;&gt;SXMO&lt;&#x2F;a&gt;, it&#x27;ll keep a charge for the whole day if you were to keep it screen-off in your pocket. it does that by suspending the CPU to RAM, but keeping the modem powered -- great if all you care about is being notified on incoming calls&#x2F;texts, not so great if you want to be notified by apps running on the CPU.&lt;&#x2F;p&gt;
&lt;p&gt;a typical solution to this is to wake the CPU whenever the system gets an IP packet, yield that to userspace &amp;amp; give your applications a chance to sound a ringer, vibrate, etc, and then suspend again when they&#x27;re done.&lt;&#x2F;p&gt;
&lt;p&gt;the more you can decide &quot;this packet isn&#x27;t urgent&quot; without CPU intervention, the more you can avoid waking the CPU, the more you can extend battery life. and so most systems provide tooling to help you tune IP-based wakeups if you know where to poke.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wake-on-lan&quot;&gt;Wake on LAN&lt;&#x2F;h2&gt;
&lt;p&gt;a.k.a. &quot;Wake-On-Wireless LAN&quot; or &quot;wowlan&quot;. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Wake-on-LAN&quot;&gt;Wake on LAN&lt;&#x2F;a&gt; is an actual established standard for remotely waking one ethernet device from another device on the same LAN. the user or OS enables this feature in the BIOS, the PC suspends, some other device sends a specially crafted packet (a &quot;magic packet&quot;), &lt;strong&gt;magic happens&lt;&#x2F;strong&gt;, and the BIOS wakes the system.&lt;&#x2F;p&gt;
&lt;p&gt;this can work on WiFi, too, but on a mobile system you enable this by speaking directly to the WiFi chip, rather than BIOS&#x2F;UEFI. if you&#x27;re lucky, &lt;code&gt;sudo iw phy0 wowlan enable any&lt;&#x2F;code&gt; will do just that.&lt;&#x2F;p&gt;
&lt;p&gt;then enter sleep (&lt;code&gt;rtcwake -m mem -s 300&lt;&#x2F;code&gt; to suspend to RAM for 300 seconds), and from a different device on the same LAN, wake your phone with &lt;code&gt;wol &amp;lt;your_phones_mac_address&amp;gt;&lt;&#x2F;code&gt; e.g. &lt;code&gt;wol 02:ba:7c:9c:cc:78&lt;&#x2F;code&gt; (find the MAC address with &lt;code&gt;iw dev&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;for the Pinephone, whether or not this works depends on how your kernel is configured. firstly, your kernel needs to know to keep the WiFi chip powered during suspend. second, there&#x27;s a GPIO routed from the WiFi chip to the main SoC: the WiFi chip toggles this GPIO when it sees the magic packet, and the SoC needs to be told to recognize that as an interrupt source that the kernel can respond to. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;xnux.eu&#x2F;devices&#x2F;pine64-pinephone.html&quot;&gt;megi&#x27;s&lt;&#x2F;a&gt; kernel does these things, i can&#x27;t say about other kernels like Mobian&#x27;s.&lt;&#x2F;p&gt;
&lt;p&gt;so if you&#x27;re on megi&#x27;s kernel, this should work... at least once if you repeat the process enough. WiFi and Bluetooth share a lot of the same resources and don&#x27;t always play nicely together. a quick to get WoL to work reliably is to just disable bluetooth: &lt;code&gt;rm &#x2F;lib&#x2F;firmware&#x2F;rtl_bt&lt;&#x2F;code&gt; (back it up first). there&#x27;s also a &lt;code&gt;CONFIG_BT_COEXIST&lt;&#x2F;code&gt; kernel option you could mess with if you&#x27;re pursuing a longer-term fix (i don&#x27;t really use bluetooth, but would appreciate to hear better fixes if anyone pursues them).&lt;&#x2F;p&gt;
&lt;p&gt;and now, &lt;code&gt;wol&lt;&#x2F;code&gt; should pretty reliably wake the phone. there&#x27;s a race condition if you try that &lt;em&gt;during&lt;&#x2F;em&gt; the &lt;code&gt;rtcwake&lt;&#x2F;code&gt; call instead of waiting a second for it to complete -- i&#x27;ll address that further down.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;wowlan-for-userspace&quot;&gt;Wowlan for Userspace&lt;&#x2F;h3&gt;
&lt;p&gt;you&#x27;re probably not interested in manually calling &lt;code&gt;wol&lt;&#x2F;code&gt; from some other computer on the same network to wake your phone.
you&#x27;d probably prefer it to wake anytime you get a Matrix or XMPP message, or something.
for that, things diverge quickly from any sort of standard.
but &lt;code&gt;wol&lt;&#x2F;code&gt; actually just sends out an ordinary UDP packet with a specific payload, so getting from there to &quot;wake on TCP traffic sent to port 22&quot;, or &quot;wake on TCP traffic sent from IP foo&quot; shouldn&#x27;t be &lt;em&gt;technically&lt;&#x2F;em&gt; difficult.&lt;&#x2F;p&gt;
&lt;p&gt;the Pinephone&#x27;s Realtek WiFi chip exposes a programmable pattern-matching facility. just give it a byte string and a mask, and it&#x27;ll wake on any packet whose bytes within the masked regions match those within the byte string.
for example, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Internet_Protocol_version_4#Header&quot;&gt;IPv4&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Transmission_Control_Protocol#TCP_segment_structure&quot;&gt;TCP&lt;&#x2F;a&gt; packets specify their destination port at bytes 37-38. it&#x27;s easy to tell the WiFi chip to &quot;wake on any TCP packet sent to port 22&quot;. run this command before the &lt;code&gt;rtcwake&lt;&#x2F;code&gt; call, then try &lt;code&gt;ssh&lt;&#x2F;code&gt;ing into your phone while it&#x27;s asleep:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; iwpriv wlan0 wow_set_pattern pattern=-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:00:16&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;with any luck, it should wake!&lt;&#x2F;p&gt;
&lt;p&gt;the fields there are hexadecimal, &lt;code&gt;:&lt;&#x2F;code&gt; indicates byte boundaries, &lt;code&gt;-&lt;&#x2F;code&gt; indicates &quot;i don&#x27;t care what value this byte is set to&quot;. but figuring out the byte pattern which achieves what you want is painful, so i&#x27;ve got a script for that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.uninsane.org&#x2F;colin&#x2F;nix-files&#x2F;src&#x2F;commit&#x2F;01de6f84cfc2291092a6809e61339e992f9b97b4&#x2F;pkgs&#x2F;additional&#x2F;rtl8723cs-wowlan&#x2F;rtl8723cs-wowlan&quot;&gt;here&lt;&#x2F;a&gt; (&lt;a href=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;mobile-linux-push-notifications&#x2F;rtl8723cs-wowlan.py&quot;&gt;mirrored&lt;&#x2F;a&gt;). equivalent to the above is &lt;code&gt;rtl8723cs-wowlan tcp --dest-port 22&lt;&#x2F;code&gt;. if you have a chat application which is talking over https, you might try &lt;code&gt;rtl8723cs-wowlan tcp --source-port 443&lt;&#x2F;code&gt; and see that the phone wakes every time you get a message. but then you&#x27;ll get spurious wakes if you leave a web browser open, etc. you could use &lt;code&gt;lsof -i4&lt;&#x2F;code&gt; to locate the local port(s) your IM app is using and wake more specifically on those. but you&#x27;ll still hit limits with this approach, especially if you&#x27;re using a chatty protocol like Matrix, or if you idle in a bunch of channels configured to notify you only on @mention.&lt;&#x2F;p&gt;
&lt;p&gt;enter... notification servers!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;notification-servers&quot;&gt;Notification Servers&lt;&#x2F;h2&gt;
&lt;p&gt;when an application wants to sound a notification on iOS or Android when it&#x27;s not in focus, it&#x27;s not the application running on the phone which does that, but actually some other server (operated by or for the developer) which tells &lt;em&gt;Apple&#x27;s&lt;&#x2F;em&gt; server (or Google&#x27;s) about the notification, and then they relay it to your phone. one of the justification for that is precisely to allow the type of power-saving we&#x27;re aiming for here, but because that approach poses privacy concerns, passionate people have created open source alternatives to the official Apple&#x2F;Google notification delivery servers.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;unifiedpush.org&#x2F;&quot;&gt;UnifiedPush&lt;&#x2F;a&gt; defines the standards for each confusingly-named portion of this data flow,
and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ntfy.sh&quot;&gt;ntfy&lt;&#x2F;a&gt; provides hosted and self-hosted implementations for all the major components. most mature applications provide a way for you to integrate all this: i&#x27;ll show how to do this for Matrix-synapse and Prosody (XMPP). both can be configured server-side if you have access to that, or client-side &lt;em&gt;if your client exposes an option for it&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;but first, let&#x27;s prove this push notification system in a simpler CLI workflow.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;minimal-ntfy-workflow&quot;&gt;Minimal ntfy Workflow&lt;&#x2F;h3&gt;
&lt;p&gt;ntfy operates a free-to-use server for the push gateway component (i.e. the part that&#x27;s normally routed through Apple&#x2F;Google), so install the CLI package for the publish&#x2F;subscribe portions and we can test a wake-on-notification setup trivially.&lt;&#x2F;p&gt;
&lt;p&gt;on your phone:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ntfy sub TEST_WOWLAN_TOPIC&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;then determine the local port that ntfy process connected from with &lt;code&gt;lsof -i4 -P&lt;&#x2F;code&gt;, and create a wake condition for it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# clear out previous rules&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; rtl8723cs-wowlan enable-clean&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; rtl8723cs-wowlan tcp&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-numeric&quot;&gt; --dest-port 1234&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;#^ replace 1234 with the port from `lsof`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;then sleep the phone with &lt;code&gt;rtcwake -m mem&lt;&#x2F;code&gt;, and from another computer (or the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ntfy.sh&#x2F;app&quot;&gt;ntfy web interface&lt;&#x2F;a&gt;) send a notification:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation&quot;&gt; ntfy pub TEST_WOWLAN_TOPIC &amp;quot;hello phone&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;if all is well, your phone should awake and the &lt;code&gt;ntfy sub&lt;&#x2F;code&gt; command from earlier should have printed &quot;hello phone&quot;!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hosting-a-ntfy-instance&quot;&gt;Hosting a ntfy instance&lt;&#x2F;h3&gt;
&lt;p&gt;this part is &lt;strong&gt;optional&lt;&#x2F;strong&gt;. most applications which speak UnifiedPush provide an option to only send a summary (like &quot;new Matrix message&quot;) instead of the actual contents, so you only really need to concern yourself with this if you want the control of self-hosting or are worried more specifically about metadata leaks.&lt;&#x2F;p&gt;
&lt;p&gt;NixOS ships &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;search.nixos.org&#x2F;options?show=services.ntfy-sh&quot;&gt;options&lt;&#x2F;a&gt; for hosting your own ntfy. use it like:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;services.ntfy-sh.enable&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;ntfy listens on port 2586 by default, so you can subscribe like &lt;code&gt;ntfy sub MY_INSTANCE_ADDRESS:2586&#x2F;TEST_WOWLAN_TOPIC&lt;&#x2F;code&gt;, and use a wowlan rule that matches the source port instead of checking &lt;code&gt;lsof&lt;&#x2F;code&gt; manually: &lt;code&gt;rtl8723cs-wowlan tcp --source-port 2586&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;however, running it this way allows anyone to use your server. the easy way to overcome this is to treat the topic as a shared secret, and forbid use of any topic except your secret topic. naturally, that only works if you secure the connection so do all this behind a TLS-capable reverse proxy like nginx:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;services.ntfy-sh.enable&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;services.nginx.virtualHosts.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;&amp;quot;MY.NTFY.HOST&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  forceSSL&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  listen&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; addr&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;0.0.0.0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; port&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2587&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; ssl&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; addr&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;0.0.0.0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; port&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 443&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  ssl&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  locations&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;&amp;quot;&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    proxyPass&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;http:&#x2F;&#x2F;127.0.0.1:2586&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    proxyWebsockets&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;  #&amp;lt; support websocket upgrades. without that, `ntfy sub` hangs silently&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    recommendedProxySettings&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; #&amp;lt; adds headers so ntfy logs include the real IP&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    extraConfig&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;      # absurdly long timeout (86400s=24h) so that we never hang up on clients.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;      proxy_read_timeout 86400s;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;services.ntfy-sh.settings&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  base-url&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;https:&#x2F;&#x2F;MY.NTFY.HOST&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  behind-proxy&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  auth-default-access&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;deny-all&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;systemd.services.ntfy-sh.preStart&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  # note that this will fail upon first run, i.e. before ntfy has created its db.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  # just restart the service.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;  ${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;pkgs.ntfy-sh&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;bin&#x2F;ntfy access everyone TEST_WOWLAN_TOPIC read-write&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;deploy that and subscribe to the https url this time. note the port change to 2587: you still want a unique port against which you can write a wowlan rule, and it&#x27;s easier to put nginx on a new port than ntfy&#x27;s default 2586.&lt;&#x2F;p&gt;
&lt;p&gt;if you&#x27;re thorough, you might notice some spurious wakeups with this setup. ntfy sends keep-alive packets every 45 seconds, but that&#x27;s no good for us! best is to decrease&#x2F;disable those keep-alives. i&#x27;ll revisit the topic of keep-alives &amp;amp; sleep durations near the end of this article.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;services.ntfy-sh.settings.keepalive-interval&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;30m&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;synapse-matrix-ntfy-integration&quot;&gt;Synapse (Matrix) ntfy integration&lt;&#x2F;h3&gt;
&lt;p&gt;the Synapse Matrix server exposes an API for controlling its push behavior &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;spec.matrix.org&#x2F;unstable&#x2F;push-gateway-api&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;.
some of the larger clients, like Fluffychat, can be seen to host their own push gateways, which they point Synapse to via this API.
presumably one could configure these clients to request a different push gateway (i.e. ntfy.sh, or your own instance from just above),
either in the UI or with an edit to their source code. but if you have CLI admin access to Synapse, it&#x27;s easier to do this generically from the CLI.&lt;&#x2F;p&gt;
&lt;p&gt;first, grab an auth token for your Matrix account. it looks like &lt;code&gt;6cC_a03Ty3sTfqvo3FS_x8vEjdvNsxyL5W4mm73&lt;&#x2F;code&gt; and can be found in Element&#x27;s &quot;Help &amp;amp; About&quot; settings page.&lt;&#x2F;p&gt;
&lt;p&gt;then, with this token, open a CLI to wherever you host your synapse server and issue this command to see where it&#x27;s currently sending notifications (if anywhere):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;  curl&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; --header&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;Authorization: Bearer ACCESS_TOKEN&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  localhost:8008&#x2F;_matrix&#x2F;client&#x2F;v3&#x2F;pushers&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; jq&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;  &amp;quot;pushers&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;      &amp;quot;app_display_name&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;Element (iOS)&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;      &amp;quot;app_id&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;im.vector.app.ios.prod&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;      &amp;quot;data&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        &amp;quot;url&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;https:&#x2F;&#x2F;matrix.org&#x2F;_matrix&#x2F;push&#x2F;v1&#x2F;notify&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        &amp;quot;format&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;event_id_only&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        &amp;quot;default_payload&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;          &amp;quot;aps&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;            &amp;quot;mutable-content&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; 1,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;            &amp;quot;alert&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;              &amp;quot;loc-key&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;Notification&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;              &amp;quot;loc-args&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; []&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;      &amp;quot;device_display_name&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;iPhone&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;      &amp;quot;kind&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;http&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;      &amp;quot;lang&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;en-US&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;      &amp;quot;profile_tag&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;1Akvq5DDr159ANj9&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;      &amp;quot;pushkey&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;Bap1n7kqGzF9TuPkiDNDy9wW+cuvfDnxy7Cab7AMsIX=&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;now, add a new pusher for your ntfy service. this won&#x27;t replace the existing settings, instead it&#x27;ll cause push notifications to be sent to two locations simultaneously:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; curl&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; --header&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;Authorization: Bearer ACCESS_TOKEN&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;  --data&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;{ \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;quot;app_display_name&amp;quot;: &amp;quot;ntfy-adapter&amp;quot;, \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;quot;app_id&amp;quot;: &amp;quot;ntfy.uninsane.org&amp;quot;, \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;quot;data&amp;quot;: { \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;      &amp;quot;url&amp;quot;: &amp;quot;https:&#x2F;&#x2F;MY.NTFY.HOST&#x2F;_matrix&#x2F;push&#x2F;v1&#x2F;notify&amp;quot;, \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;      &amp;quot;format&amp;quot;: &amp;quot;event_id_only&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    }, \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;quot;device_display_name&amp;quot;: &amp;quot;ntfy-adapter&amp;quot;, \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;quot;kind&amp;quot;: &amp;quot;http&amp;quot;, \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;quot;lang&amp;quot;: &amp;quot;en-US&amp;quot;, \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;quot;profile_tag&amp;quot;: &amp;quot;&amp;quot;, \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;quot;pushkey&amp;quot;: &amp;quot;TEST_WOWLAN_TOPIC&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  }&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  localhost:8008&#x2F;_matrix&#x2F;client&#x2F;v3&#x2F;pushers&#x2F;set&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;repeat the first query and you&#x27;ll see both of these listed. to delete the new pusher, repeat the above &lt;code&gt;curl&lt;&#x2F;code&gt; command with &lt;code&gt;kind&lt;&#x2F;code&gt; set to &lt;code&gt;null&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;anyway, put your phone to sleep, have someone send you a message that Synapse would alert you on, and now your phone should awake!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;prosody-xmpp-ntfy-integration&quot;&gt;Prosody (XMPP) ntfy integration&lt;&#x2F;h3&gt;
&lt;p&gt;Prosody has &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;modules.prosody.im&#x2F;mod_cloud_notify&quot;&gt;mod_cloud_notify&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;xmpp.org&#x2F;extensions&#x2F;xep-0357.html&quot;&gt;XEP-0357&lt;&#x2F;a&gt;, but at the time of writing, client support is wanting. easier is to hack it in server-side.&lt;&#x2F;p&gt;
&lt;p&gt;Prosody has a Lua-based module system, so we can just author our own &lt;code&gt;mod_ntfy_push&lt;&#x2F;code&gt; (available &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.uninsane.org&#x2F;colin&#x2F;nix-files&#x2F;src&#x2F;commit&#x2F;01de6f84cfc2291092a6809e61339e992f9b97b4&#x2F;hosts&#x2F;by-name&#x2F;servo&#x2F;services&#x2F;prosody&#x2F;modules&#x2F;mod_sane_ntfy.lua&quot;&gt;here&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;mobile-linux-push-notifications&#x2F;mod_sane_notify.lua&quot;&gt;here&lt;&#x2F;a&gt;), drop it in the modules directory, and then import it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;services.prosody.extraPluginsPath&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&#x2F;folder&#x2F;containing&#x2F;mod_ntfy_push&lt;&#x2F;span&gt;&lt;span&gt; ]&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;services.prosody.extraModules&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;ntfy_push&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; ]&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;services.prosody.extraConfig&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  ntfy_endpoint = &amp;quot;https:&#x2F;&#x2F;MY.NTFY.HOST&#x2F;TEST_WOWLAN_TOPIC&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;i&#x27;ve only authored this module to alert me on jingle calls. one could presumably alert on DMs, MUC messages, or anything more particular by finding the right Prosody hooks (mod_cloud_notify may be an ok guide for that).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;sxmo-integration&quot;&gt;SXMO Integration&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sxmo.org&#x2F;&quot;&gt;SXMO&lt;&#x2F;a&gt; is a mobile-friendly Linux desktop notable for being extremely hackable (it&#x27;s really just a collection of shell scripts layered over &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;swaywm.org&#x2F;&quot;&gt;sway&lt;&#x2F;a&gt;), and as such we can integrate all the client-side work we did above into something the desktop environment handles for us transparently.&lt;&#x2F;p&gt;
&lt;p&gt;SXMO includes an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~mil&#x2F;sxmo-utils&#x2F;tree&#x2F;master&#x2F;item&#x2F;scripts&#x2F;core&#x2F;sxmo_autosuspend.sh&quot;&gt;&lt;code&gt;autosuspend&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; service. it&#x27;s just a bash &lt;code&gt;while&lt;&#x2F;code&gt; loop that periodically checks if conditions are suitable to sleep the phone (is the screen off, is the user not in a call, are no media players active, and so on) and if so, calls out to &lt;code&gt;sxmo_suspend.sh&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~mil&#x2F;sxmo-utils&#x2F;tree&#x2F;master&#x2F;item&#x2F;scripts&#x2F;core&#x2F;sxmo_suspend.sh&quot;&gt;&lt;code&gt;sxmo_suspend.sh&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; looks like this (simplified -- it does a little more to make cronjobs reliable)&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;suspend_time&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;99999999&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt; # far away&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;sxmo_log&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;calling suspend with suspend_time &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;$suspend_time&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation&quot;&gt;&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;doas&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; rtcwake&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -m&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; mem&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -s&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;$suspend_time&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ||&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; exit&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;sxmo_hook_postwake.sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;since the different components of SXMO communicate by shelling out to each other, we can patch it just by putting our own &lt;code&gt;sxmo_suspend.sh&lt;&#x2F;code&gt; script somewhere on the PATH. that&#x27;s intentional behavior: SXMO goes out of its way to add &lt;code&gt;~&#x2F;.config&#x2F;sxmo&#x2F;hooks&lt;&#x2F;code&gt; as a PATH entry to its services, so we can just drop a file like this into &lt;code&gt;~&#x2F;.config&#x2F;sxmo&#x2F;hooks&#x2F;sxmo_suspend.sh&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;ntfy&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sub TEST_WOWLAN_TOPIC&lt;&#x2F;span&gt;&lt;span&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;NTFY_PID&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;$!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;port&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;while&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; [ -z&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;$port&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; ];&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;  # netstat output will look like:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;  #   tcp 0 0 127.0.0.1:12345 10.11.12.13:443 ESTABLISHED 2798004&#x2F;ntfy&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;  # pipe through sed&#x2F;cut to extract the `12345` local port component&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;  # do this in a loop to allow time for ntfy to establish the connection&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;  port&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;netstat&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; --tcp --program --numeric-ports&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; grep&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ntfy&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; sed&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;#39;s&#x2F;  *&#x2F; &#x2F;g&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; cut&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-numeric&quot;&gt; -f 4&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; cut&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;&amp;#39;:&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -f2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;done&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# configure to wake on any traffic to that ntfy connection&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;rtl8723cs-wowlan&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; enable-clean&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;rtl8723cs-wowlan&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; tcp&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; --dest-port&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;$port&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;sxmo_log&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;calling suspend with suspend_time 600&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;doas&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; rtcwake&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -m&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; mem&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-numeric&quot;&gt; -s 600&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ||&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; exit&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function&quot;&gt;kill&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; $NTFY_PID&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;sxmo_hook_postwake.sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function&quot;&gt;wait&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;  # for ntfy to exit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and that&#x27;s it. use your phone as you normally would, and everything should be the same except that now it&#x27;ll wake up whenever you get a Matrix&#x2F;XMPP&#x2F;ntfy notification.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pinephone-wowlan-race-condition&quot;&gt;Pinephone Wowlan Race Condition&lt;&#x2F;h2&gt;
&lt;p&gt;as alluded, sending a wake packet within about a second of sending the SoC to sleep will fail to wake the system. worse, the SoC will be &lt;em&gt;stuck&lt;&#x2F;em&gt; in the sleep state, not waking on any &lt;em&gt;future&lt;&#x2F;em&gt; wake packets (you can still wake it via the power button, and it should behave correctly during the next sleep cycle). one can imagine lots of ways an edge-triggered interrupt could be misimplemented to cause this type of race, but i wasn&#x27;t able to diagnose it any more than this.&lt;&#x2F;p&gt;
&lt;p&gt;in light of that type of thing, i&#x27;m being cautious and never sleeping the phone for more than 10 minutes at a time (&lt;code&gt;rtcwake -s 600&lt;&#x2F;code&gt;).
so if you get a VoIP call, there&#x27;s something like a 0.2% chance you&#x27;ll miss it and see a notification for it 10 minutes later.
there&#x27;s a higher chance than that i miss a call just by forgetting my phone in the other room, so i&#x27;m not especially concerned.
but if it bothers you, a workaround exists by coordinating your notification server and your phone such that it knows when your phone is about to enter sleep and delays notifications during that time by a couple seconds to avoid triggering the race. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.uninsane.org&#x2F;colin&#x2F;nix-files&#x2F;src&#x2F;commit&#x2F;01de6f84cfc2291092a6809e61339e992f9b97b4&#x2F;hosts&#x2F;by-name&#x2F;servo&#x2F;services&#x2F;ntfy&#x2F;ntfy-waiter&quot;&gt;here&#x27;s&lt;&#x2F;a&gt; how i do that.&lt;&#x2F;p&gt;
&lt;p&gt;there are other reasons you may want to not sleep for extended durations: NATs. in theory, a TCP connection with no traffic &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;datatracker.ietf.org&#x2F;doc&#x2F;html&#x2F;rfc5382#section-5&quot;&gt;should remain routable for at least 2 hours&lt;&#x2F;a&gt; -- meaning you could safely sleep for 2 hours and the notification server will still be able to reach you at any point. in practice, i don&#x27;t know how that holds across all networks. UDP has far lower guarantees (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc4787#section-4.3&quot;&gt;30s to 120s&lt;&#x2F;a&gt; based on who you ask): consider that if you&#x27;re using a UDP-based VPN like Wireguard. there&#x27;s also lots of gotchas around WiFi connection details (moving from one network to another, GTK&#x2F;encryption rekeying, DHCP lease renewals, etc).&lt;&#x2F;p&gt;
&lt;p&gt;in any case, longer sleep durations give diminishing returns. if you sleep for 600s, and then wake for 15s before going back to sleep, you&#x27;ve already captured 95% of the gains you could get from this sleep method. sleeping for an hour isn&#x27;t going to get you that much more battery life.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;&#x2F;h2&gt;
&lt;p&gt;i only covered the WiFi usecase. the modem also has a line to the CPU and could be made to wake it under similar scenarios. i believe it already &lt;em&gt;does&lt;&#x2F;em&gt; for calls&#x2F;SMS, but i haven&#x27;t gotten around to understanding or tuning its behavior for IP traffic (information from anyone who has looked into this would be appreciated!)&lt;&#x2F;p&gt;
&lt;p&gt;i&#x27;m not convinced this push notification system will have a place in the long-term future of mobile Linux.
in my setup, i&#x27;m not actually surfacing any of the content from the push gateway onto the phone&#x27;s display. the push gateway could just as well be sending the phone 0-byte packets. i&#x27;m just using it to wake the phone and then let the applications learn about new messages&#x2F;calls and tell the DE about them in their ordinary manner. there are simpler ways to do that.&lt;&#x2F;p&gt;
&lt;p&gt;maybe some XDG spec or Wayland protocol will be developed to let applications tell the OS &quot;incoming data on this TCP connection isn&#x27;t urgent. but data on this other TCP connection &lt;em&gt;is&lt;&#x2F;em&gt; urgent&quot; and then the OS can program the wowlan rules accordingly, and notification servers have no special place. on the other hand, a lot of messaging systems are having to implement Apple&#x2F;Google&#x27;s notification system for their users anyway, so am i describing an actual reduction in complexity there or would it more likely be &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;xkcd.com&#x2F;927&#x2F;&quot;&gt;yet another standard&lt;&#x2F;a&gt; everyone would need to support?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;maybe you want to help determine that future&lt;&#x2F;strong&gt;? if so: Librem&#x27;s working on their Chatty client, and while i have no special affiliation with them, i&#x27;ll point out that they&#x27;re &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.gnome.org&#x2F;World&#x2F;Chatty&#x2F;-&#x2F;issues&#x2F;846&quot;&gt;actively considering&lt;&#x2F;a&gt; how to integrate sleep&#x2F;push notifications into their client recently.&lt;&#x2F;p&gt;
&lt;p&gt;besides that, KDE is also &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.volkerkrause.eu&#x2F;2022&#x2F;11&#x2F;12&#x2F;kde-unifiedpush-push-notifications.html&quot;&gt;looking into&lt;&#x2F;a&gt; similar &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;plasma-mobile.org&#x2F;2023&#x2F;05&#x2F;05&#x2F;this-month-plasma-mobile&#x2F;#push-notifications&quot;&gt;things&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;where-to-learn-more&quot;&gt;Where to Learn More&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;the Arch Linux wiki has a page on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;Wake-on-LAN&quot;&gt;Wake-on-LAN&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;official SoC&#x2F;peripheral documentation can sometimes be found in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;linux-sunxi.org&#x2F;Main_Page&quot;&gt;linux-sunxi wiki&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;binwiederhier&#x2F;ntfy&#x2F;issues&#x2F;319&quot;&gt;this&lt;&#x2F;a&gt; ntfy ticket illuminated the Matrix interactions for me&lt;&#x2F;li&gt;
&lt;li&gt;i found out about the Pinephone&#x27;s wowlan second-hand from Peetz0r in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;irclog.whitequark.org&#x2F;linux-sunxi&#x2F;2021-02-18&quot;&gt;#linux-sunxi&lt;&#x2F;a&gt; (IRC)
&lt;ul&gt;
&lt;li&gt;and summarized in megi&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;xnux.eu&#x2F;log&#x2F;031.html&quot;&gt;dev log&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;i found out about BT_COEXIST issues from Aren in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sxmo.org&#x2F;support&quot;&gt;#sxmo&lt;&#x2F;a&gt; (IRC&#x2F;Matrix)&lt;&#x2F;li&gt;
&lt;li&gt;Guido hinted me onto Phosh&#x27;s plans for wake-on-lan&#x2F;push notifications in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.to&#x2F;#&#x2F;#phosh:talk.puri.sm&quot;&gt;#phosh&lt;&#x2F;a&gt; (Matrix)&lt;&#x2F;li&gt;
&lt;li&gt;i got some hints about XMPP push notification support from xavi in &lt;a href=&quot;xmpp:chat@dino.im&quot;&gt;chat@dino.im&lt;&#x2F;a&gt; (XMPP)&lt;&#x2F;li&gt;
&lt;li&gt;more generally, i keep tabs on app-level and ecosystem developments through:
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;linmob.net&quot;&gt;LINMob&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;thisweek.gnome.org&#x2F;&quot;&gt;This Week in Gnome&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;linuxphoneapps.org&#x2F;&quot;&gt;LinuxPhoneApps&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cast.postmarketos.org&#x2F;&quot;&gt;postmarketOS podcast&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;as always, feel free to message me via any method on my &lt;a href=&quot;&#x2F;about&quot;&gt;about page&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>How To Casually Contribute to NixOS&#x2F;Nixpkgs</title>
        <published>2022-10-13T00:00:00+00:00</published>
        <updated>2022-10-13T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://uninsane.org/blog/nixos-upstreaming/"/>
        <id>https://uninsane.org/blog/nixos-upstreaming/</id>
        
        <content type="html" xml:base="https://uninsane.org/blog/nixos-upstreaming/">&lt;p&gt;as a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nixos.org&quot;&gt;nix&#x2F;NixOS&lt;&#x2F;a&gt; user, you&#x27;ll inherently find yourself writing code, and some of this code might be valuable to others: be it new packages, improvements upon existing packages, or new services&#x2F;options. part of what makes nix successful (IMO) is the ease of sharing work and upstreaming, so i&#x27;m setting out here to show some easy workflows for doing so.&lt;&#x2F;p&gt;
&lt;p&gt;the number one thing you can do -- if you&#x27;re not already -- is host your nix config publicly. for example, mine is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.uninsane.org&#x2F;colin&#x2F;nix-files&quot;&gt;here&lt;&#x2F;a&gt;, and even without advertising it i occasionally hear from people in my orbit that they&#x27;ve found it useful. as a beginner i had some public configs i found on google or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiby.me&quot;&gt;wiby&lt;&#x2F;a&gt; which i routinely consulted, even though many of those authors probably never heard a word from the people like me who found use in their work.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;authoring-new-packages&quot;&gt;Authoring New Packages&lt;&#x2F;h2&gt;
&lt;p&gt;let&#x27;s say you find some software you want that isn&#x27;t yet packaged. first, confirm it doesn&#x27;t exist by searching the package name (and probable variants) on github. remove all filters, so that you&#x27;re searching both PRs and issues: maybe somebody opened a packaging request and there&#x27;s already something to work off of. for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;nixpkgs&#x2F;issues?q=zecwallet-lite&quot;&gt;example&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;having confirmed that, let&#x27;s make a new package. anywhere in your config, add an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nixos.wiki&#x2F;wiki&#x2F;Overlays&quot;&gt;overlay&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;{ ... }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  nixpkgs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;overlays&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;    (final: prev: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      zecwallet-lite&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = prev.callPackage&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&#x2F;zecwallet-lite&lt;&#x2F;span&gt;&lt;span&gt; { };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;i&#x27;m just going to show the process i followed when upstreaming that zecwallet-lite package. create the package directory so that &lt;code&gt;callPackage&lt;&#x2F;code&gt; knows what to reference:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; mkdir zecwallet-lite&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; touch zecwallet-lite&#x2F;default.nix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;when you reference a path in nix like &lt;code&gt;.&#x2F;zecwallet-lite&lt;&#x2F;code&gt;, it&#x27;ll resolve to &lt;code&gt;.&#x2F;zecwallet-lite&#x2F;default.nix&lt;&#x2F;code&gt; if that&#x27;s valid. if you&#x27;re using a flake, the path won&#x27;t resolve unless it&#x27;s in git:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; git add zecwallet-lite&#x2F;default.nix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;now i locate the upstream package distribution. zecwallet-lite is distributed as an appimage, so i search nixpkgs for other appimages to reference:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; cd ~&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; git clone https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;nixpkgs.git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; cd nixpkgs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; rg appimage pkgs&#x2F;applications&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -l&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; xargs&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; wc&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -l&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; sort&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -h&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;   19&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; pkgs&#x2F;applications&#x2F;misc&#x2F;remnote&#x2F;default.nix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;   24&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; pkgs&#x2F;applications&#x2F;video&#x2F;losslesscut-bin&#x2F;default.nix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;   25&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; pkgs&#x2F;applications&#x2F;networking&#x2F;instant-messengers&#x2F;caprine-bin&#x2F;default.nix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;   26&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; pkgs&#x2F;applications&#x2F;misc&#x2F;fspy&#x2F;default.nix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;   27&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; pkgs&#x2F;applications&#x2F;audio&#x2F;sonixd&#x2F;default.nix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function&quot;&gt;   ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;remnote&lt;&#x2F;code&gt; is a simple derivation, only 19 LOC:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;{ lib, fetchurl, appimageTools }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;appimageTools.wrapType2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; rec&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  pname&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;remnote&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  version&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;1.7.6&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  src&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = fetchurl {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    url&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;https:&#x2F;&#x2F;download.remnote.io&#x2F;RemNote-&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;version&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation&quot;&gt;.AppImage&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    sha256&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;sha256-yRUpLev&#x2F;Fr3mOamkFgevArv2UoXgV4e6zlyv7FaQ4RM=&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  meta&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; with&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt; lib; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    description&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;A note-taking application focused on learning and productivity&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    homepage&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;https:&#x2F;&#x2F;remnote.com&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    maintainers&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; with&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt; maintainers; [ max-niederman ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    license&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = licenses.unfree;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    platforms&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = platforms.linux;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;so copy that to &lt;code&gt;zecwallet-lite&#x2F;default.nix&lt;&#x2F;code&gt; and edit the values to match your appimage package. set &lt;code&gt;sha256 = lib.fakeHash&lt;&#x2F;code&gt;, and nix will tell you the actual hash.&lt;&#x2F;p&gt;
&lt;p&gt;then build the package, test it, and tweak it until you&#x27;re happy. the simplest way to do that is add it to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;search.nixos.org&#x2F;options?channel=unstable&amp;amp;show=environment.systemPackages&amp;amp;from=0&amp;amp;size=50&amp;amp;sort=relevance&amp;amp;type=packages&amp;amp;query=packages&quot;&gt;&lt;code&gt;environment.systemPackages&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and &lt;code&gt;nixos-rebuild switch&lt;&#x2F;code&gt;. normally, if it builds, it&#x27;ll run (and you might just need to patch up some icon&#x2F;.desktop files, etc).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;upstreaming-new-packages&quot;&gt;Upstreaming New Packages&lt;&#x2F;h2&gt;
&lt;p&gt;so the package builds, you&#x27;ve used it long enough to be confident in it: time to upstream it from your silo into the main nixpkgs. even if you&#x27;re strictly self-interested, upstreaming means less custom code for you to maintain, less burden keeping the version up-to-date since others may help with that, and less time spent rebuilding it when a dependency updates since it&#x27;ll be present in the official nixos caches. it often takes all of ten minutes once you&#x27;re familiar.&lt;&#x2F;p&gt;
&lt;p&gt;go back to your &lt;code&gt;nixpkgs&lt;&#x2F;code&gt; repo and check out the &lt;code&gt;master&lt;&#x2F;code&gt; branch. choose a reasonable folder for the package, like &lt;code&gt;pkgs&#x2F;applications&#x2F;misc&#x2F;&lt;&#x2F;code&gt;, and copy your package there:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; cp&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -R&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;etc&#x2F;nixos&#x2F;zecwallet-lite ~&#x2F;nixpkgs&#x2F;pkgs&#x2F;applications&#x2F;misc&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;then take the &lt;code&gt;callPackage&lt;&#x2F;code&gt; invocation from your overlay and place it in &lt;code&gt;pkgs&#x2F;top-level&#x2F;all-packages.nix&lt;&#x2F;code&gt; instead. &lt;code&gt;git add&lt;&#x2F;code&gt; the file, and make sure the package builds:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;~&#x2F;nixpkgs$ nix build &lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;.#zecwallet-lite&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;~&#x2F;nixpkgs$ .&#x2F;result&#x2F;bin&#x2F;zecwallet-lite&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;  # make sure it runs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;commit the changes, push to a github account, and open a PR. github should pre-populate the PR description with a checklist for you to complete. near as i can tell, you don&#x27;t have to check &lt;em&gt;every&lt;&#x2F;em&gt; item: it&#x27;s just a way for reviewers to know where to focus.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;cleanup-and-dry&quot;&gt;Cleanup and DRY&lt;&#x2F;h2&gt;
&lt;p&gt;you could leave the duplicate package definition both in your &lt;code&gt;&#x2F;etc&#x2F;nixos&lt;&#x2F;code&gt; repo &lt;em&gt;and&lt;&#x2F;em&gt; in your &lt;code&gt;nixpkgs&lt;&#x2F;code&gt; repo and wait until the next release of nixos&#x2F;nixpkgs before removing the former; or you can remove the package from your nixos repo now and import it by reference to avoid repetition.&lt;&#x2F;p&gt;
&lt;p&gt;if you&#x27;re using a flake for your system config, it likely looks something like this right now:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  inputs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    nixpkgs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;url&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;nixpkgs&#x2F;nixos-22.05&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  outputs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = { self, nixpkgs }: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    nixosConfigurations&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;mySystem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = nixpkgs.lib.nixosSystem {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      system&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;x86_64-linux&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      modules&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        .&#x2F;mySystem.nix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;we want to somehow patch that &lt;code&gt;inputs.nixpkgs&lt;&#x2F;code&gt; so that it includes whatever changes we&#x27;re trying to upstream (our new package, a package update, etc). the simplest option is to point &lt;code&gt;inputs.nixpkgs.url&lt;&#x2F;code&gt; directly at our open PR (add &lt;code&gt;&amp;amp;rev=&amp;lt;git-commit&amp;gt;&lt;&#x2F;code&gt; to do this), but that doesn&#x27;t work if we have multiple PRs.&lt;&#x2F;p&gt;
&lt;p&gt;instead, we use a trick shown &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;nix&#x2F;issues&#x2F;3920#issuecomment-1168041777&quot;&gt;here&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  inputs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    nixpkgs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;url&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;nixpkgs&#x2F;nixos-22.05&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  outputs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = { self, nixpkgs }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  let&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    patchedPkgs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = nixpkgs.legacyPackages.x86_64-linux.applyPatches {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      name&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;nixpkgs-patched&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      src&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = nixpkgs;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      patches&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = [&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&#x2F;patch1.patch .&#x2F;patch2.patch&lt;&#x2F;span&gt;&lt;span&gt; ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    nixosSystem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; import&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt; (patchedPkgs +&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;&#x2F;nixos&#x2F;lib&#x2F;eval-config.nix&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  in&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    nixosConfigurations&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;mySystem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = nixosSystem {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      system&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;x86_64-linux&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      modules&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        .&#x2F;mySystem.nix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;that is, we take the base nixpkgs repo&#x2F;derivation, create a new derivation which patches the original nixpkgs, and then import the result of that derivation so that the rest of our flake refers to this patched nixpkgs.&lt;&#x2F;p&gt;
&lt;p&gt;the version above expects your patches to live in the same repo as your config flake, but if the goal is to avoid duplication we&#x27;d rather point these at the PRs hosted on github. you can use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nixos.org&#x2F;manual&#x2F;nixpkgs&#x2F;stable&#x2F;#fetchurl&quot;&gt;&lt;code&gt;fetchpatch&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; for this. if your PR is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;nixpkgs&#x2F;pull&#x2F;180960&quot;&gt;180960&lt;&#x2F;a&gt;, append &lt;code&gt;.diff&lt;&#x2F;code&gt; to the URL to get a patch. the &lt;code&gt;let&lt;&#x2F;code&gt; block above becomes this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;let&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  fetchpatch&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = nixpkgs.legacyPackages.x86_64-linux.fetchpatch;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  patchedPkgs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = nixpkgs.legacyPackages.x86_64-linux.applyPatches {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    name&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;nixpkgs-patched&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    src&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = nixpkgs;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    patches&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;      (fetchpatch {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;        url&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;nixpkgs&#x2F;pull&#x2F;180960.diff&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;        sha256&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;sha256-HVVj&#x2F;T3yQtjYBoxXpoPiG9Zar&#x2F;eik9IoDVDhTOehBdY=&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  nixosSystem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; import&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt; (patchedPkgs +&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;&#x2F;nixos&#x2F;lib&#x2F;eval-config.nix&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and we&#x27;re set! as before, if you don&#x27;t know the hash just set it to &lt;code&gt;nixpkgs.lib.fakeHash&lt;&#x2F;code&gt;. you can freely push to your open PR: to have the changes reflect on your system just reset&#x2F;update the hash and &lt;code&gt;nixos-rebuild switch&lt;&#x2F;code&gt;. once your PR is merged, the next &lt;code&gt;nix flake update&lt;&#x2F;code&gt; might include your patch already, in which case nix will error when building &lt;code&gt;nixpkgs-patched&lt;&#x2F;code&gt;. at that point you can safely remove this entry from &lt;code&gt;patches&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;upstreaming-other-changes&quot;&gt;Upstreaming Other Changes&lt;&#x2F;h2&gt;
&lt;p&gt;since this approach is ultimately just applying patches onto &lt;code&gt;nixpkgs&lt;&#x2F;code&gt;, it&#x27;s not limited to only new packages. to update a package version, add a new config option, etc, you can follow effectively the same process: clone &lt;code&gt;nixpkgs&lt;&#x2F;code&gt;, checkout the master branch, make a change and &lt;code&gt;nix build&lt;&#x2F;code&gt; the result, commit &amp;amp; open a PR, and &lt;code&gt;fetchpatch&lt;&#x2F;code&gt; the PR into your flake.&lt;&#x2F;p&gt;
&lt;p&gt;if you&#x27;re making changes in a hot area of the codebase, the diff from your PR (which targets master) might not apply cleanly to your flake (which pulls from a release or nixos-unstable). in that case you can &lt;code&gt;git log&lt;&#x2F;code&gt; the files you&#x27;re changing and use the same &lt;code&gt;fetchpatch&lt;&#x2F;code&gt; trick to cherry-pick whatever prerequisite commits you need so that your patch applies.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;closing-thoughts&quot;&gt;Closing Thoughts&lt;&#x2F;h2&gt;
&lt;p&gt;i described here the workflow i found easiest to get started with. with something as configurable as nix, many people use many different workflows. some prefer maintaining their own long-running fork of nixpkgs wherein they cherry-pick all these PRs and periodically rebase against nixos-unstable (or a release branch), and point their flake directly at their fork of nixpkgs. your workflow will surely evolve as you settle in, but hopefully this gives you enough inspiration to get started :-)&lt;&#x2F;p&gt;
&lt;p&gt;finally, don&#x27;t be overly intimidated by the &lt;code&gt;nixos-unstable&lt;&#x2F;code&gt; branch. it might not be quite as unstable as the name suggests: i&#x27;ve yet to encounter any show-stopping issues, 90% of the instability is just that packages don&#x27;t build and need to be pinned or fixed (or you skip the update that day). there&#x27;s a point where the inconvenience of nixos-unstable is less than the inconvenience of maintaining patches that have already landed upstream. if you&#x27;re upstreaming enough that you feel that pain point, you&#x27;ll do fine on the unstable branch.&lt;&#x2F;p&gt;
&lt;p&gt;as always, don&#x27;t hesitate to &lt;a href=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;about&#x2F;&quot;&gt;contact me&lt;&#x2F;a&gt; or reach out on the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nixos.org&#x2F;community&#x2F;&quot;&gt;forums&#x2F;chat&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Bootstrapping a NixOS Pinephone Installation</title>
        <published>2022-06-15T00:00:00+00:00</published>
        <updated>2022-06-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://uninsane.org/blog/mobile-nixos-pinephone/"/>
        <id>https://uninsane.org/blog/mobile-nixos-pinephone/</id>
        
        <content type="html" xml:base="https://uninsane.org/blog/mobile-nixos-pinephone/">&lt;p&gt;there is no official, easy-to-grab NixOS image to download and flash to devices like the pinephone today. although there is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=30010178&quot;&gt;a way&lt;&#x2F;a&gt; to do that via the hydra build cache, it&#x27;s a bit tortured and the images built in automation don&#x27;t have a user. the bootstrapping process would require manually editing &lt;code&gt;&#x2F;etc&#x2F;fstab&lt;&#x2F;code&gt; (from a different machine) to add a user and then logging in with a USB-C keyboard to setup ssh or a desktop -- neither user-friendly nor easily reproducible.&lt;&#x2F;p&gt;
&lt;p&gt;so let&#x27;s bootstrap from an existing Nix install. first, provision a machine (either a NixOS installation or something with the &lt;code&gt;nix&lt;&#x2F;code&gt; binary installed). the architecture doesn&#x27;t matter -- i&#x27;ll assume it&#x27;s x86_64 and show you how to cross-compile. create a new directory for the work&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;mkdir&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; nixos-pinephone-getting-started&lt;&#x2F;span&gt;&lt;span&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; cd&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; nixos-pinephone-getting-started&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;each section in this article corresponds to one commit in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.uninsane.org&#x2F;colin&#x2F;nixos-pinephone-getting-started&quot;&gt;this&lt;&#x2F;a&gt; repository in case you want to skip the commentary.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bare-minimal-build&quot;&gt;Bare-minimal Build&lt;&#x2F;h2&gt;
&lt;p&gt;for our Pinephone machine, we&#x27;ll let &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;mobile-nixos&#x2F;&quot;&gt;mobile-nixos&lt;&#x2F;a&gt; do the heavy lifting. create a minimal &lt;code&gt;flake.nix&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  inputs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;    # nixpkgs.url = &amp;quot;nixpkgs&#x2F;nixos-22.05&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    nixpkgs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;url&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;nixpkgs&#x2F;dfd82985c273aac6eced03625f454b334daae2e8&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    mobile-nixos&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;      # url = &amp;quot;github:nixos&#x2F;mobile-nixos&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      url&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;github:nixos&#x2F;mobile-nixos&#x2F;efbe2c3c5409c868309ae0770852638e623690b5&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      flake&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  outputs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = { self, nixpkgs, mobile-nixos }: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    pinephone-img&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt; = (nixpkgs.lib.nixosSystem {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      system&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;aarch64-linux&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      modules&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;import&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;mobile-nixos&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-embedded&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation&quot;&gt;&#x2F;lib&#x2F;configuration.nix&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;          device&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;pine64-pinephone&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;        ({ ... }: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;          nixpkgs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;allowUnfree&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-variable z-parameter&quot;&gt;    }).config.mobile.outputs.u-boot.disk-image;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;i&#x27;ve frozen the mobile-nixos commit here because they&#x27;re in the process of switching from U-Boot to towboot -- the pinned commit is among the last which support U-Boot, which this guide assumes. pinning &lt;code&gt;nixpkgs&lt;&#x2F;code&gt; may be paranoia: try switching to &lt;code&gt;nixpkgs&#x2F;nixos-&amp;lt;release&amp;gt;&lt;&#x2F;code&gt; after you&#x27;re bootstrapped.&lt;&#x2F;p&gt;
&lt;p&gt;if your host machine isn&#x27;t aarch64, you&#x27;ll have to enable cross compiling. the trivial way is emulation (i.e. qemu), though it could make the build take a full afternoon depending on how much of your system is available through nixcache.&lt;&#x2F;p&gt;
&lt;p&gt;on the host box, add this somewhere in your config (e.g. &lt;code&gt;&#x2F;etc&#x2F;nixos&#x2F;configuration.nix&lt;&#x2F;code&gt;):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;boot.binfmt.emulatedSystems&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;aarch64-linux&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; ]&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;if you added this emulation, rebuild your host machine to enable it (&lt;code&gt;nixos-rebuild --flake &#x2F;etc&#x2F;nixos switch&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;then build the image with:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;nixos-pinephone-getting-started$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; nix build &amp;#39;.&#x2F;#pinephone-img&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;the resulting image is effectively the same as what Hydra spits out in the automation: no users, and no way to login. if you&#x27;re brand new to the Pinephone, i recommend flashing the image and booting as a sanity check. otherwise, skip to &lt;a href=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;mobile-nixos-pinephone&#x2F;#making-the-image-more-usable&quot;&gt;&lt;em&gt;Making the Image More Usable&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; where we&#x27;ll build a more usable image. at any time, consult the &lt;a href=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;mobile-nixos-pinephone&#x2F;#troubleshooting&quot;&gt;&lt;em&gt;Troubleshooting&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; section if you hit something unexpected.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;flashing-the-image&quot;&gt;Flashing the Image&lt;&#x2F;h3&gt;
&lt;p&gt;you might be tempted to flash this to the eMMC instead of an SD card, thinking that the former will be more reliable storage. it&#x27;s not: my eMMC began to fail within 20 write cycles. i highly recommend you use an SD card for this.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; dd if=&lt;&#x2F;span&gt;&lt;span&gt;$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;readlink&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&#x2F;result&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; of=&#x2F;dev&#x2F;sdb bs=4M oflag=direct conv=sync status=progress&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;oflag=direct&lt;&#x2F;code&gt; makes the progress bar actually usable -- otherwise it&#x27;ll just show how much data has been passed to the kernel -- and &lt;code&gt;conv=sync&lt;&#x2F;code&gt; seems to deal better with low quality SD cards (it feels like paranoia, but if i &lt;code&gt;fsck -f&lt;&#x2F;code&gt; the result it sometimes shows corruption without this flag).&lt;&#x2F;p&gt;
&lt;p&gt;insert the card to the Pinephone and boot it. the SD slot takes precedent over the eMMC in its boot sequence, so nothing more is needed. you should see the power indicator turn red (indicating that U-Boot is active), then yellow (u-boot has run the mobile-nixos boot script), then green (NixOS stage 1). you&#x27;ll see a mobile-NixOS splash screen, it&#x27;ll take a minute to validate the fs, and then boot into stage 2 where you&#x27;ll be stuck at a login prompt.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;making-the-image-more-usable&quot;&gt;Making the Image More Usable&lt;&#x2F;h2&gt;
&lt;p&gt;we&#x27;ll want to rebuild the image and include a user, desktop environment, and some basic applications. i tried Phosh, Plasma Mobile, and Gnome: of these, Phosh works the best OOTB &lt;em&gt;by far&lt;&#x2F;em&gt; (Plasma Mobile is &lt;em&gt;slow&lt;&#x2F;em&gt; and crash-prone, Gnome lacks an on-screen keyboard outside the core apps and its navigation is less tailored to phones).&lt;&#x2F;p&gt;
&lt;p&gt;we&#x27;ll use home-manager to make user setup a bit nicer. add that to &lt;code&gt;flake.nix&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; inputs = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   nixpkgs.url = &amp;quot;nixpkgs&#x2F;dfd82985c273aac6eced03625f454b334daae2e8&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   mobile-nixos = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     url = &amp;quot;github:nixos&#x2F;mobile-nixos&#x2F;efbe2c3c5409c868309ae0770852638e623690b5&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     flake = false;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-inserted&quot;&gt;+  home-manager.url = &amp;quot;github:nix-community&#x2F;home-manager&#x2F;release-22.05&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;add a module for this pinephone build:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-deleted&quot;&gt;-  outputs = { self, nixpkgs, mobile-nixos }: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-inserted&quot;&gt;+  outputs = { self, nixpkgs, mobile-nixos, home-manager }: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     pinephone-img = (pkgs-mobile.lib.nixosSystem {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       system = &amp;quot;aarch64-linux&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-inserted&quot;&gt;+      specialArgs = { inherit home-manager; };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       modules = [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;         (import &amp;quot;${mobile-nixos}&#x2F;lib&#x2F;configuration.nix&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;           device = &amp;quot;pine64-pinephone&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;         })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-deleted&quot;&gt;-        ({ ... }: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-deleted&quot;&gt;-          nixpkgs.config.allowUnfree = true;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-deleted&quot;&gt;-        })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-inserted&quot;&gt;+        .&#x2F;modules&#x2F;default.nix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }).config.mobile.outputs.u-boot.disk-image;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and then populate &lt;code&gt;modules&#x2F;default.nix&lt;&#x2F;code&gt;. you could just throw everything in here, but it&#x27;s more readable (and reusable -- in case you want to share this config across machines) if you split it up:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;{ ... }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  imports&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    .&#x2F;hardware.nix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    .&#x2F;home-manager.nix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    .&#x2F;phosh.nix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    .&#x2F;users.nix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  system&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;stateVersion&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;22.05&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  nixpkgs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;allowUnfree&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;modules&#x2F;hardware.nix&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;{ ... }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;  ## enable the hardware rotation sensor&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  hardware&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;sensor&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;iio&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;enable&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  hardware&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;opengl&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;enable&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  hardware&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;opengl&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;driSupport&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;modules&#x2F;home-manager.nix&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;{ pkgs, home-manager, ... }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  imports&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;    home-manager.nixosModule&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  home-manager&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;useGlobalPkgs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  home-manager&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;useUserPackages&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  home-manager&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;users&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;colin&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    home&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;stateVersion&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;21.11&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    home&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;username&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;colin&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    home&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;homeDirectory&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;&#x2F;home&#x2F;colin&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    programs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      home-manager&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;enable&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      firefox&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;enable&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;      git&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;enable&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;    # a few useful packages to start with&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    home&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;packages&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; with&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt; pkgs; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;      # useful CLI&#x2F;admin tools to have during setup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;      fatresize&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;      gptfdisk&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;      networkmanager&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;      sudo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;      vim&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;      wget&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;      # it&amp;#39;s good to have a variety of terminals (x11, Qt, GTK) to handle more failures&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;      xterm&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;      plasma5Packages.konsole&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-keyword z-operator&quot;&gt;      gnome.gnome-terminal&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;modules&#x2F;phosh.nix&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;{ ... }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  services&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;xserver&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;desktopManager&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;phosh&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    enable&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    user&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;colin&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    group&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;users&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  environment&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;variables&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;    # Qt apps won&amp;#39;t always start unless this env var is set&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    QT_QPA_PLATFORM&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;wayland&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;modules&#x2F;users.nix&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;{ ... }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  users&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;mutableUsers&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  users&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;users&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;colin&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    isNormalUser&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    home&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;&#x2F;home&#x2F;colin&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    uid&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1000&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;    # make this numeric so that you can enter it in the phosh lockscreen.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;    # DON&amp;#39;T leave this empty: not all greeters support passwordless users.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    initialPassword&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;147147&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    extraGroups&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;wheel&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  security&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    enable&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    wheelNeedsPassword&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;  services&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;openssh&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    enable&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    permitRootLogin&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;no&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;    passwordAuthentication&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;build and flash the image as before. this is the final image we&#x27;ll flash, so before you eject the card resize the rootfs to make use of the full device. do NOT use &lt;code&gt;fdisk&lt;&#x2F;code&gt; or &lt;code&gt;parted&lt;&#x2F;code&gt; for this. it disrupts some on-disk structures required by the bootloader (this won&#x27;t be a worry after mobile-nixos officially switches to tow-boot). instead, use the ncurses frontend to fdisk: &lt;code&gt;cfdisk&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sudo cfdisk &#x2F;dev&#x2F;sdb&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;scroll&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; to &#x2F;dev&#x2F;sdb4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt; Resize&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;  leave&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; at default&lt;&#x2F;span&gt;&lt;span&gt; (28.8G)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt; Write&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function&quot;&gt;  type&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;yes&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt; Quit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;eject the card and boot the phone, login, and toy around with it. open the display settings and mess with the scale (it defaults to 200%). i find 150% is best, particularly so that Firefox doesn&#x27;t overflow.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;building-generations-on-the-device&quot;&gt;Building Generations on the Device&lt;&#x2F;h2&gt;
&lt;p&gt;we don&#x27;t want to re-flash the device &lt;em&gt;every&lt;&#x2F;em&gt; time we change something. let&#x27;s update the flake to allow on-device updates.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-deleted&quot;&gt;- outputs = { self, nixpkgs, mobile-nixos, home-manager }: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-deleted&quot;&gt;-   pinephone-img = (nixpkgs.lib.nixosSystem {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-inserted&quot;&gt;+ outputs = { self, nixpkgs, mobile-nixos, home-manager }: rec {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-inserted&quot;&gt;+   nixosConfigurations.pinephone = (nixpkgs.lib.nixosSystem {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      system = &amp;quot;aarch64-linux&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      modules = [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (import &amp;quot;${mobile-nixos}&#x2F;lib&#x2F;configuration.nix&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          device = &amp;quot;pine64-pinephone&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       .&#x2F;pinephone.nix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-deleted&quot;&gt;-   }).config.mobile.outputs.u-boot.disk-image;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-inserted&quot;&gt;+   });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-inserted&quot;&gt;+   pinephone-img = nixosConfigurations.pinephone.config.mobile.outputs.u-boot.disk-image;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    nixosConfigurations.host = nixpkgs.lib.nixosSystem {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      system = &amp;quot;x86_64-linux&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;you may need network access for this. unless you connect a USB-C keyboard, the graphical network manager might not be usable. in that case, open a terminal and run:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; nmcli device wifi connect&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;wifinam&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;e&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; password&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;passwor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;d&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;copy your flake over to the phone at &lt;code&gt;&#x2F;etc&#x2F;nixos&#x2F;flake.nix&lt;&#x2F;code&gt;. i use git for this. on the phone (or over ssh -- try &lt;code&gt;ssh nixos&lt;&#x2F;code&gt; from the same LAN or use &lt;code&gt;ip addr&lt;&#x2F;code&gt; to find its IP):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;~$ git clone https:&#x2F;&#x2F;git.uninsane.org&#x2F;colin&#x2F;nixos-pinephone-getting-started.git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;~$ sudo rmdir &#x2F;etc&#x2F;nixos &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; sudo&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; mv nixos-pinephone-getting-started &#x2F;etc&#x2F;nixos&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;~$ cd &#x2F;etc&#x2F;nixos&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;&#x2F;etc&#x2F;nixos$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sudo git config&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; --global --add&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; safe.directory &#x2F;etc&#x2F;nixos&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;&#x2F;etc&#x2F;nixos$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sudo nixos-rebuild&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; --flake&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt; &amp;quot;.&#x2F;#pinephone&amp;quot; switch&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;validate this with a reboot, and you should be golden! i recommend mirroring your &lt;code&gt;&#x2F;etc&#x2F;nixos&lt;&#x2F;code&gt; folder to at least one other place: a github repo, &lt;code&gt;rsync&lt;&#x2F;code&gt; to a desktop, &lt;code&gt;duplicity&lt;&#x2F;code&gt; to cloud storage, etc. that way if you ever brick your phone you can restore from your latest config using the &lt;code&gt;dd&lt;&#x2F;code&gt; approach. managing generations and rolling back to a good one doesn&#x27;t seem quite as easy to do on a phone.&lt;&#x2F;p&gt;
&lt;p&gt;happy nixing :-)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;mobile-nixos-pinephone&#x2F;booted-running-firefox.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;troubleshooting&quot;&gt;Troubleshooting&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;device is unbootable or unflashable (eMMC isn&#x27;t exposed as a &#x2F;dev node over USB):&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;these issues tend to be power-related. flash a minimal, known-good image (like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;images.postmarketos.org&#x2F;pinephone&#x2F;&quot;&gt;postmarketOS&lt;&#x2F;a&gt;) to the SD card and boot with the battery and USB charger plugged in. wait until the battery gets to a good charge and resume.&lt;&#x2F;p&gt;
&lt;p&gt;if it&#x27;s still not working, try holding the reset button underneat the back cover of the phone for 10s.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;fs appears to be corrupted:&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;validate the built, but unflashed, image:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# create a loopback device from our .img file:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# .&#x2F;result should be a symlink to &#x2F;nix&#x2F;store&#x2F;&amp;lt;hash&amp;gt;-pine64-pinephone_full-disk-image.img&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sudo losetup&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -Pf&lt;&#x2F;span&gt;&lt;span&gt; $(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;readlink&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&#x2F;result&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# check the rootfs partition (partition #4):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# you might see a single, inconsequential error about &amp;quot;Padding at end of inode bitmap is not set.&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sudo fsck&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;dev&#x2F;loop0p4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# close the loopback device:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sudo losetup&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -d&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;dev&#x2F;loop0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;then flash the image using the dd flags i show in the article. while the SD card is still attached to the host, validate it with  &lt;code&gt;sudo fsck -f &#x2F;dev&#x2F;sdb4&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;finally, the eMMC has a dozen or so write cycles: prefer an SD card.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;the nix config won&#x27;t build:&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;nixos moves fast, but does occasionally break. you might need to freeze the nixpkgs to a specific commit. try cloning the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.uninsane.org&#x2F;colin&#x2F;nixos-pinephone-getting-started&quot;&gt;repo&lt;&#x2F;a&gt; for this post and building it directly -- the repo includes &lt;code&gt;flake.lock&lt;&#x2F;code&gt; so that it should be more reproducible.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;additional-resources&quot;&gt;Additional Resources:&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Tom Fitzhenry &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~tomf&#x2F;notes&#x2F;tree&#x2F;master&#x2F;item&#x2F;pinephone-nixos-getting-started.md&quot;&gt;building a minimal Pinephone image&lt;&#x2F;a&gt; by dropping his config straight into a mobile-nixos checkout, no &lt;code&gt;flake.nix&lt;&#x2F;code&gt; required.&lt;&#x2F;li&gt;
&lt;li&gt;Ana, Hoverbear on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hoverbear.org&#x2F;blog&#x2F;nix-flake-live-media&#x2F;&quot;&gt;making an install image&lt;&#x2F;a&gt; from an existing NixOS configuration.&lt;&#x2F;li&gt;
&lt;li&gt;Cole Mickens&#x27; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;colemickens&#x2F;nixcfg&quot;&gt;public nix config&lt;&#x2F;a&gt; shows how to maintain a shared repo for multiple machines with more advanced things like custom packages, modules, and secrets.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.uninsane.org&#x2F;colin&#x2F;nix-files&#x2F;&quot;&gt;my own nix config&lt;&#x2F;a&gt; which targets multiple machines with one of these operating a nix cache to speed up pinephone builds.&lt;&#x2F;li&gt;
&lt;li&gt;Pavel Makhov showing a better way to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;eno.space&#x2F;blog&#x2F;&#x2F;2021&#x2F;08&#x2F;nixos-on-underpowered-devices&quot;&gt;distribute builds across machines&lt;&#x2F;a&gt; which i haven&#x27;t got around to trying.&lt;&#x2F;li&gt;
&lt;li&gt;the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.to&#x2F;#&#x2F;#community:nixos.org&quot;&gt;NixOS Matrix space&lt;&#x2F;a&gt; which contains chat rooms like &lt;code&gt;#nixos-on-arm:nixos.org&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;or contact me directly via the links in my &lt;a href=&quot;&#x2F;about&quot;&gt;about&lt;&#x2F;a&gt; page.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The Jackson Pad: DIY Vibration Damping for Drumsets</title>
        <published>2022-05-25T00:00:00+00:00</published>
        <updated>2022-05-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://uninsane.org/blog/vibration-damping-for-drumsets/"/>
        <id>https://uninsane.org/blog/vibration-damping-for-drumsets/</id>
        
        <content type="html" xml:base="https://uninsane.org/blog/vibration-damping-for-drumsets/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;jackson-pad-assembled-profile.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;a few years ago i moved out of student apartments and into the second story
of a commuter-centric apartment. i wanted to stay on my neighbors&#x27; good side,
so i ditched my acoustic set and bought an electronic one. shortly after,
i received my first ever noise complaint!&lt;&#x2F;p&gt;
&lt;p&gt;although electronic sets are audibly quieter, they still transmit a lot of energy
into the floor. when struck, vibrations carry from the drum into
the harness and from there into the floor. moreover, the kick and high-hat
pedal action mirrors that of a person stomping, so of
&lt;em&gt;course&lt;&#x2F;em&gt; that&#x27;s going to annoy the downstairs neighbor.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-to-decrease-vibrations&quot;&gt;How to decrease vibrations&lt;&#x2F;h2&gt;
&lt;p&gt;the most straightforward way to decrease floor vibrations is to decrease
the energy associated with each hit.
for example, use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.com&#x2F;Yamaha-KU100-Beaterless-Silent-Pedal&#x2F;dp&#x2F;B00FI2PE1U&quot;&gt;beaterless pedals&lt;&#x2F;a&gt;.
if there&#x27;s no beater imparting energy into the kick plate then
that&#x27;s less energy transmitted into the floor.
similarly, slamming the high-hat close imparts momentum from the upper cymbal
into the stand and from there to the floor.
beaterless pedals solve both these issues.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;yamaha-ku100-beaterless-pedal.jpg&quot; alt=&quot;Yamaha KU100 beaterless pedal&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;but even with beaterless pedals (if you&#x27;re willing to go that route),
you still have the issue of snare&#x2F;tom hits vibrating through the harness.
if you want to stop all these, you&#x27;ll need an isolation platfrom
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.thomann.de&#x2F;gb&#x2F;thomann_drum_noise_elimination_podium.htm&quot;&gt;like this&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;thomann-drum-noise-elimination-podium.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;or you can place the set atop a riser:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;drum-riser.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;i was very tempted by that Thomann podium, but it&#x27;s priced a bit too aggressively for me.&lt;&#x2F;p&gt;
&lt;p&gt;cheaper options include placing the set on a large rubber or foam pad, similar
to what you might put under a washing machine or an exercise bike (&quot;anti vibration mats&quot;).&lt;&#x2F;p&gt;
&lt;p&gt;but after browsing drum forums i couldn&#x27;t find many success stories from these approaches.
maybe that&#x27;s just a bias from the people who frequent these forums and anti-vibration mats
work equally well, but i was intrigued by the other approaches i saw there.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;diy-solutions&quot;&gt;DIY solutions&lt;&#x2F;h2&gt;
&lt;p&gt;the most infamous DIY approach within these drum communities is the &lt;em&gt;tennis ball riser&lt;&#x2F;em&gt;:
sandwich tennis balls between two large panels (or a panel and the floor), put the drumset
on top, and you&#x27;re done. it&#x27;s a simple suspension system and actually resembles that
commercial Thomann podium quite a bit.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;tennis-ball-riser.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;a more thoroughly engineered solution i came across was &lt;em&gt;The Jackson Pad&lt;&#x2F;em&gt;. the
creator (Brian Jackson) provides detailed build instructions, some convincing
noise measurements, a believable theory of operation, and i couldn&#x27;t find anyone
who tried this &lt;em&gt;without&lt;&#x2F;em&gt; success.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-jackson-pad&quot;&gt;The Jackson Pad&lt;&#x2F;h2&gt;
&lt;p&gt;initially proposed &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.vdrums.com&#x2F;forum&#x2F;advanced&#x2F;diy&#x2F;1095500-new-design-impact-isolating-platform-plans-and-guide&quot;&gt;on vdrums.com&lt;&#x2F;a&gt;,
i&#x27;ve mirrored the original &lt;a href=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;Jackson%20Pad%20-%20Design%20Drawings%20-%202-10-2015.pdf&quot;&gt;design document&lt;&#x2F;a&gt;
and &lt;a href=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;The%20Jackson%20Pad%20-%20R1%20-%20Illustrated%20Builders%20Guide.pdf&quot;&gt;build instructions&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;the first idea is that the drumset should sit atop a platform that has a lot of mass.
because of the high mass, the momentum you impart onto it when stomping your feet
causes less physical displacement than a platform with low mass.&lt;&#x2F;p&gt;
&lt;p&gt;the second idea is to set this platform atop four air-filled inner tubes.
the displacement of the platform will compress the air within the tubes,
redirecting some of the motion outward instead of downward and supposedly
transforming mechanical energy into heat energy (compressing a gas causes it to heat):
the heat will be transformed back into mechnical energy as the pressures balance,
but the effect is that sharp impulses of mechanical energy are imparted into the
floor over a longer period of time.
it&#x27;s like applying a low-pass filter to the original foot stomp or drum hit.&lt;&#x2F;p&gt;
&lt;p&gt;i&#x27;m a little skeptical of the heat argument, but overall i&#x27;m satisfied enough
with the theory of operation to give it a try.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;construction&quot;&gt;Construction&lt;&#x2F;h2&gt;
&lt;p&gt;the pad materials are super practical to source. it&#x27;s all off-the-shelf lumber
that you can buy at a hardware store with a minimal number of cuts.
the stock 4&#x27;x4.7&#x27; design is just large enough
for a typical 4-drum + highhat + kick + ride + crash configuration and a stool
with modest footprint.&lt;&#x2F;p&gt;
&lt;p&gt;unfortunately, it&#x27;s just large enough to not be practical to transport.
i move homes every year or two, so i wanted to find a way to make it fit in
my stationwagon.&lt;&#x2F;p&gt;
&lt;p&gt;the stock plan makes use of four 55.5&quot; trusses which support fourteen 2x4s running
perpindicular to them:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;plans-clean.png&quot; alt=&quot;Original Jackson Pad&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;i decided to make a vertical cut down the center of this entire design such that
i would have two separate 48&quot;x27.25&quot; pads.
each of the four trusses would be cut in half, and a brace would be added to each one
which would secure the halves when bolted. with the bolts in place, you&#x27;re left with
a solid 48&quot;x55.5&quot; pad; with the bolts removed, you have two smaller pads that can
be easily transported.&lt;&#x2F;p&gt;
&lt;p&gt;the original design leaves four 40&quot; boards of scrap from cutting the trusses.
you can trim these down to 2&#x27; and use those as the braces. the only addition to
Jackson&#x27;s BOM is sixteen 4&quot; bolts, washers, and wingnuts.&lt;&#x2F;p&gt;
&lt;p&gt;here&#x27;s what the truss + brace system looks like when bolted together:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;trusses-bolted-together.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;from here on, i just follow Jackson&#x27;s original plans.
when constructing the platform, keep the bolts in place and secure the 2x4s atop
each truss as normal (make sure to secure the slats onto the trusses, and &lt;em&gt;not&lt;&#x2F;em&gt; the braces -- otherwise you won&#x27;t be able to detach the braces!)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;first-2x4-secured.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;completed-frame-top.jpg&quot; alt=&quot;all 2x4s secured&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;here&#x27;s what it looks like from the underside:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;completed-frame-underside.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;you&#x27;ll want to label the braces now so that you know which brace belongs
to which truss when you reassemble it later (even though they should have
the same cuts, in practice you won&#x27;t have lined up the holes identically in
each one). i just marked them such that they spell a word when correctly paired:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;completed-frame-labeled.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;if you want carpeting, do that while the platform is assembled.
you can cut one piece of carpet to the full 48&quot;x55.5&quot;, staple it, and let it fold inward
during disassembly. it&#x27;ll take two people to carry it this way, but it probably looks
nicer than if you carpeted each half separately.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;disassembly-reassembly&quot;&gt;Disassembly &amp;amp; Reassembly&lt;&#x2F;h2&gt;
&lt;p&gt;when you&#x27;re ready, just unbolt the braces, slide them off, and fold the platform:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;folded-1.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;folded-2.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;folded-in-car.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;to setup the pad somewhere more permanent, first situate the isolation pedestals, then reattach the braces and lower the pad onto the pedestals.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;reattach-braces-1.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;reattach-braces-2.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;completed-pad.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;and you&#x27;re good to go :)&lt;&#x2F;p&gt;
&lt;p&gt;this pad has served me for four years and at three different homes with zero noise complaints.
i refill the tubes yearly, and very imprecisely. you really just need to fill them to capacity
and the 200-300 pounds sitting atop them should pressurize it all decently.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;vibration-damping-for-drumsets&#x2F;assembled-front-view.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Rescuing a Broken EXT4 System with Ext4Magic and dd</title>
        <published>2022-04-14T00:00:00+00:00</published>
        <updated>2022-04-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://uninsane.org/blog/fsck-dd-rescue/"/>
        <id>https://uninsane.org/blog/fsck-dd-rescue/</id>
        
        <content type="html" xml:base="https://uninsane.org/blog/fsck-dd-rescue/">&lt;p&gt;while i was setting up this machine, i made some mistakes along the way and observed the dreaded fsck messages on boot:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[kernel boot output]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&#x2F;dev&#x2F;sda1 contains a file system with errors, fsck required.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dropping into emergency shell&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;so i ran &lt;code&gt;fsck&lt;&#x2F;code&gt; on the disk like the computer told me to:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# fsck &#x2F;dev&#x2F;sda1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[...]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Inode 5202 has an invalid extent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (logical block 0, invalid physical block 2250752, len 2048)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Clear (&amp;#39;a&amp;#39; enables &amp;#39;yes&amp;#39; to all) &amp;lt;y&amp;gt;? yes to all&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and then i typed &lt;code&gt;a&lt;&#x2F;code&gt; (&quot;yes to all&quot;) like a noob, expecting that any cornerstone tool of Linux in 2022 would act sanely.
i mean, it didn&#x27;t give me any other apparent option: i had to complete &lt;code&gt;fsck&lt;&#x2F;code&gt; before it would let me boot,
and the only option &lt;code&gt;fsck&lt;&#x2F;code&gt; gives me on each error is &lt;code&gt;yes&lt;&#x2F;code&gt; or &lt;code&gt;yes to all&lt;&#x2F;code&gt;. so, obviously i&#x27;m supposed to
let it fix everything itself.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fsck-fsck&quot;&gt;Fsck fsck&lt;&#x2F;h2&gt;
&lt;p&gt;so i let it whir. and it trashed everything. if i had properly read the output, i maybe would have
understood that it was simply deleting (&quot;clearing&quot;) everything on the disk that it didn&#x27;t understand.
but i&#x27;m an imperfect user, and i was in a hurry. so bye-bye &lt;code&gt;&#x2F;opt&#x2F;pleroma&lt;&#x2F;code&gt;. bye-bye &lt;code&gt;&#x2F;home&lt;&#x2F;code&gt;. bye-bye
arbitrary chunks of &lt;code&gt;&#x2F;var&#x2F;lib&#x2F;postgres&#x2F;data&#x2F;base&#x2F;50616&lt;&#x2F;code&gt;. i didn&#x27;t have backups in place:
i had naively decided to tackle that &lt;em&gt;after&lt;&#x2F;em&gt; i finished installation and had a clear sense
of how this system would be structured long-term.&lt;&#x2F;p&gt;
&lt;p&gt;so do i just live with this, and redo two days of work?&lt;&#x2F;p&gt;
&lt;p&gt;hell no. i&#x27;d rather spend three days diving into EXT4 internals than redo anything. so i saved the
fsck output, attached (but didn&#x27;t mount) the drive to a clean system, and got busy.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-did-fsck-actually-do&quot;&gt;What Did fsck Actually &lt;em&gt;Do&lt;&#x2F;em&gt;?&lt;&#x2F;h2&gt;
&lt;p&gt;at the start of the fsck run was this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Resize inode not valid.  Recreate&amp;lt;y&amp;gt;? yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;the remainder of the messages were mostly of two forms:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Entry &amp;#39;admin&amp;#39; in &#x2F;home (8196) has invalid inode #: 2234378.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Clear? yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Inode 4574 has an invalid extent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (logical block 0, invalid physical block 4031998, len 1)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Clear&amp;lt;y&amp;gt;? yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Inode 4574, i_blocks is 8, should be 0.  Fix&amp;lt;y&amp;gt;? yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;in fact, i had done an earlier &lt;code&gt;resize2fs&lt;&#x2F;code&gt; to expand the 8 GiB FS to fit its 2 TiB partition.
the docs say you can do this on a live filesystem, but... caveat emptor?&lt;&#x2F;p&gt;
&lt;p&gt;EXT4 defaults to a block size of 4096B (i.e., a traditional page of RAM). physical blocks
are a direct reference to some offset into the underlying device. so &quot;physical block 4031998&quot;
corresponds to the byte range on the device of 16515063808 - 16515067904.
inodes are indexed by their physical block address as well, so &lt;code&gt;inode # 2234378&lt;&#x2F;code&gt; corresponds
to the block starting at byte index of 9152012288.
notably, these indexes are both beyond the original 8 GiB fs size.
this holds true of &lt;em&gt;every&lt;&#x2F;em&gt; inode and data block fsck complained about.&lt;&#x2F;p&gt;
&lt;p&gt;there&#x27;s a good chance that all&#x2F;most of the actual data&#x2F;inode blocks still hold valid data
on-disk, and the EXT4 drivers simply didn&#x27;t understand their address.&lt;&#x2F;p&gt;
&lt;p&gt;we have two tasks here:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;recover the unlinked inodes (i.e. directory entries).&lt;&#x2F;li&gt;
&lt;li&gt;recover the cleared extents (i.e. data blocks within a file).&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;there&#x27;s a purpose-built tool for #1, and we can script our own thing for #2.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;recovering-inodes-with-ext4magic&quot;&gt;Recovering Inodes with Ext4Magic&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gktrk&#x2F;ext4magic&quot;&gt;ext4magic&lt;&#x2F;a&gt; is a tool to manage data loss like this.
one of its modes is &lt;code&gt;ext4magic -I &amp;lt;inode&amp;gt; -R &amp;lt;device&amp;gt;&lt;&#x2F;code&gt;, wherein you pass
it an inode #, it parses the inode data structure off the disk, and then makes a best-effort
attempt to recover everything in the fs tree at, or referenced by, that inode.&lt;&#x2F;p&gt;
&lt;p&gt;so for the missing &lt;code&gt;&#x2F;home&#x2F;admin&lt;&#x2F;code&gt; directory, i need only run:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# extmagic -I 2234378 -R -d recovered-inodes &#x2F;dev&#x2F;sda1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and out pops the &lt;em&gt;entire&lt;&#x2F;em&gt; directory tree for the admin directory. e.g.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ tree recovered-inodes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;└── &amp;lt;2234378&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    └── gitea&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ├── assets&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        │   ├── emoji.json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        │   └── logo.svg&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ├── BSDmakefile&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ├── build&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        │   ├── code-batch-process.go&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        │   ├── codeformat&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        │   │   ├── formatimports.go&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        │   │   └── formatimports_test.go&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        │   ├── generate-bindata.go&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        │   ├── generate-emoji.go&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;everything under that &lt;code&gt;&amp;lt;2234378&amp;gt;&lt;&#x2F;code&gt; even has the correct group&#x2F;owner&#x2F;permission bits.
you just need to rename &lt;code&gt;&amp;lt;2234378&amp;gt;&lt;&#x2F;code&gt; to &lt;code&gt;admin&lt;&#x2F;code&gt;, &lt;code&gt;chown&lt;&#x2F;code&gt; it to what it was before,
and then link it back into &lt;code&gt;&#x2F;home&lt;&#x2F;code&gt; in your fs (but don&#x27;t do that yet: put this in some
staging directory and link everything back in only after all the data has been recovered).&lt;&#x2F;p&gt;
&lt;p&gt;repeat this for all the &quot;invalid inodes&quot; referenced in the fsck output. then we&#x27;ll recover
the data blocks.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;recovering-data-blocks-ext4-data-structures&quot;&gt;Recovering Data Blocks: EXT4 Data Structures&lt;&#x2F;h2&gt;
&lt;p&gt;the 2nd class of message was:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Inode 4574 has an invalid extent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (logical block 0, invalid physical block 4031998, len 1)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Clear&amp;lt;y&amp;gt;? yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Inode 4574, i_blocks is 8, should be 0.  Fix&amp;lt;y&amp;gt;? yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;to understand that this even &lt;em&gt;is&lt;&#x2F;em&gt; recoverable, it helps to understand the ext4 inode structure.
inodes are on-disk data structures, one for every directory entry on the system.
an inode might represent a file or a directory. they look similar in both cases, but we only care about
inodes which represent files here.&lt;&#x2F;p&gt;
&lt;p&gt;each inode is a fixed-size structure holding metadata, like file type&#x2F;mtime and -- notably --
file &lt;em&gt;size&lt;&#x2F;em&gt;, and then they link to a dynamically-sized sequence of &quot;extents&quot;; roughly, pointers to where the file data
lives on disk. the English translation is like &quot;data bytes 0-32768 occupy the physical blocks starting
at block 4156555; bytes 32768-36864 occupy the physical blocks starting at block 6285112&quot;. this is
all represented in terms of blocks, so based on the file length the last block may only be partially
filled with data.&lt;&#x2F;p&gt;
&lt;p&gt;EXT4 (and many file systems) largely keeps the file data entirely outside of the inode structure. fsck tells
us that it cleared the extent &lt;em&gt;entries&lt;&#x2F;em&gt;, but not the actual data blocks. &lt;code&gt;i_blocks&lt;&#x2F;code&gt; here refers to the
blocks allocated to the inode for storing its variably-sized data, i.e. the list of extents (for
what seems to be legacy reasons, this is denoted in 512B disk sectors instead of FS blocks).&lt;&#x2F;p&gt;
&lt;p&gt;so, all the inode metadata is still here; the data blocks exist but are unlinked, and only the extents
were lost. if you try reading the file, it&#x27;ll still present its original length of data, but will show
a block&#x27;s worth of zeros for every logical block whose extent was cleared.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;recovering-data-blocks&quot;&gt;Recovering Data Blocks&lt;&#x2F;h2&gt;
&lt;p&gt;so we just need to link the data blocks back into the extents structure.
we could dive deeper into EXT4 data structures and twiddle those bits, but that would lead us into
having to understand the inode and block allocators. instead, we can just dump the block-level data, and use
fs-level APIs to put it back.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;Ext4Magic -B &amp;lt;block&amp;gt;&lt;&#x2F;code&gt; will dump the full 4096 bytes of some physical block. but because the physical
block is a direct index into the device, we can also just use &lt;code&gt;dd&lt;&#x2F;code&gt;. for example, let&#x27;s recover
this cleared extent:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Inode 4574 has an invalid extent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (logical block 0, invalid physical block 4031998, len 1)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Clear&amp;lt;y&amp;gt;? yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Inode 4574, i_blocks is 8, should be 0.  Fix&amp;lt;y&amp;gt;? yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;first, we&#x27;ll want to know which file this comes from:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; mkdir preserved&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# mount the drive READ-ONLY:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sudo mount&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -o&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ro &#x2F;dev&#x2F;sda1 preserved&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; find preserved&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-numeric&quot;&gt; -inum 4574&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;preserved&#x2F;etc&#x2F;passwd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;that&#x27;s, uh, an important file. does the data block still hold proper data?&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; dd if=&#x2F;dev&#x2F;sda1 of=&#x2F;dev&#x2F;stdout bs=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4096&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; skip=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4031998&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; count=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;root:x:0:0::&#x2F;root:&#x2F;bin&#x2F;bash&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;bin:x:1:1::&#x2F;:&#x2F;usr&#x2F;bin&#x2F;nologin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;daemon:x:2:2::&#x2F;:&#x2F;usr&#x2F;bin&#x2F;nologin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;mail:x:8:12::&#x2F;var&#x2F;spool&#x2F;mail:&#x2F;usr&#x2F;bin&#x2F;nologin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;ftp:x:14:11::&#x2F;srv&#x2F;ftp:&#x2F;usr&#x2F;bin&#x2F;nologin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;http:x:33:33::&#x2F;srv&#x2F;http:&#x2F;usr&#x2F;bin&#x2F;nologin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;nobody:x:65534:65534:Nobody:&#x2F;:&#x2F;usr&#x2F;bin&#x2F;nologin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[...]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;lt;NUL&amp;gt;&amp;lt;NUL&amp;gt;&amp;lt;NUL&amp;gt;[...]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;yes!&lt;&#x2F;p&gt;
&lt;p&gt;let&#x27;s set up a scratch space. we can construct an overlay of our rootfs where we
place all the recovered and patched entries, and then apply that to the original device
once we&#x27;re done recovering.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; mkdir recovered&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;go ahead and manually link all the entries we recovered with &lt;code&gt;ext4magic -I&lt;&#x2F;code&gt; earlier into this &lt;code&gt;recovered&lt;&#x2F;code&gt; directory and fix up their group&#x2F;owner&#x2F;permissions.&lt;&#x2F;p&gt;
&lt;p&gt;now we can patch individual files by copying them from &lt;code&gt;preserved&#x2F;&amp;lt;path&amp;gt;&lt;&#x2F;code&gt; to &lt;code&gt;recovered&#x2F;&amp;lt;path&amp;gt;&lt;&#x2F;code&gt; and then &lt;code&gt;dd&lt;&#x2F;code&gt;ing specific
byte ranges from &lt;code&gt;&#x2F;dev&#x2F;sda1&lt;&#x2F;code&gt; into &lt;code&gt;recovered&#x2F;&amp;lt;path&amp;gt;&lt;&#x2F;code&gt;. for example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; mkdir&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -p&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; recovered&#x2F;ext&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sudo cp preserved&#x2F;ext&#x2F;passwd recovered&#x2F;ext&#x2F;passwd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sudo dd if=&#x2F;dev&#x2F;sda1 of=recovered&#x2F;ext&#x2F;passwd bs=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4096&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; skip=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4031998&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; count=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ls&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -l&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; preserved&#x2F;ext&#x2F;passwd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;-rw-r--r--&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; root root&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3528&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;etc&#x2F;passwd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sudo truncate&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; --size=3528&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; recovered&#x2F;ext&#x2F;passwd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;because &lt;code&gt;dd&lt;&#x2F;code&gt; copies the whole block, we have that additional step of truncating the file to its original size.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bringing-it-together&quot;&gt;Bringing It Together&lt;&#x2F;h2&gt;
&lt;p&gt;we&#x27;ve successfully recovered (into the &lt;code&gt;recovered&lt;&#x2F;code&gt; directory):&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;all unlinked directory entries.&lt;&#x2F;li&gt;
&lt;li&gt;the cleared extent in &lt;code&gt;&#x2F;etc&#x2F;passwd&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;we still need to:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;recover all &lt;em&gt;other&lt;&#x2F;em&gt; cleared extents.&lt;&#x2F;li&gt;
&lt;li&gt;link the recovered data back into the real file system.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;step 2 is a simple &lt;code&gt;rsync&lt;&#x2F;code&gt;. step 1 is some nasty &lt;code&gt;dd&lt;&#x2F;code&gt; work. i demoed it for a file with only one cleared extent, but some files have &lt;em&gt;many&lt;&#x2F;em&gt; cleared extents, often non-contiguous.&lt;&#x2F;p&gt;
&lt;p&gt;assume the presence of a script &lt;code&gt;patch_file.py&lt;&#x2F;code&gt; (see &lt;a href=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;blog&#x2F;fsck-dd-rescue&#x2F;#appendix&quot;&gt;Appendix&lt;&#x2F;a&gt;) which takes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;an inode number (&lt;code&gt;4574&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;a file path (&lt;code&gt;etc&#x2F;passwd&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;the first logical block of the extent which was cleared (&lt;code&gt;0&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;the length in blocks of the extent which was cleared (&lt;code&gt;1&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;the first physical block containing the extent&#x27;s data (&lt;code&gt;4031998&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;then we can parse the fsck output and script the rest of step 1.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# fsck &#x2F;dev&#x2F;sda1 &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[...]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Inode 34215 has an invalid extent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (logical block 14, invalid physical block 2207227, len 1)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Clear? yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Inode 58213 has an invalid extent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (logical block 0, invalid physical block 3456000, len 1024)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Clear? yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Inode 58213 has an invalid extent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (logical block 1024, invalid physical block 3463168, len 623)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Clear? yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Inode 58213, i_blocks is 13176, should be 0.  Fix? yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Inode 58222 has an invalid extent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (logical block 0, invalid physical block 2207151, len 1)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Clear? yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Inode 58222, i_blocks is 8, should be 0.  Fix? yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[...]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;run &lt;code&gt;find -i &amp;lt;inode&amp;gt; preserved&#x2F;&lt;&#x2F;code&gt; on each of these inodes to find the file they correspond to, and then you can create this script from that snippet of fsck output:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;.&#x2F;patch_file.py&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-numeric&quot;&gt; -i 34215 -f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; var&#x2F;log&#x2F;pacman.log 14,1,2207227&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;.&#x2F;patch_file.py&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-numeric&quot;&gt; -i 58213 -f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; usr&#x2F;bin&#x2F;yay 0,1024,3456000 1024,623,3463168&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;.&#x2F;patch_file.py&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-numeric&quot;&gt; -i 58222 -f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; etc&#x2F;fstab 0,1,2207151&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;sometimes &lt;code&gt;find&lt;&#x2F;code&gt; won&#x27;t find the inode that fsck updated. for example, if you booted the system after running &lt;code&gt;fsck&lt;&#x2F;code&gt;, Linux will notice that certain files have been corrupted
and will update them with placeholders, destroying the original inode. these are usually the more important files, so you can dump the data block with that &lt;code&gt;dd&lt;&#x2F;code&gt; command
and compare it to notable entries on a good file system to &quot;guess&quot; what it was originally.
since we don&#x27;t have the original inode, we lost the metadata like its length, so use the &lt;code&gt;--auto-len&lt;&#x2F;code&gt; flag to guess the length by trimming zero&#x27;s off the original
data block.&lt;&#x2F;p&gt;
&lt;p&gt;take this snippet of fsck output:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Inode 4997 has an invalid extent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (logical block 0, invalid physical block 3831801, len 1)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Clear&amp;lt;y&amp;gt;? yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Inode 4997, i_blocks is 8, should be 0.  Fix&amp;lt;y&amp;gt;? yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;try to find the file:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; find&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-numeric&quot;&gt; -i 4997&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; preserved&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# (no output)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;but we dump physical block 3831801 and notice that it looks a lot like &lt;code&gt;&#x2F;etc&#x2F;shadow&lt;&#x2F;code&gt;. so:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;.&#x2F;patch_file.py&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-numeric&quot;&gt; -i 4997 --auto-len -f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; etc&#x2F;shadow 0,1,3831801&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;once you&#x27;ve patched all the files, then bring the file system back online, writeable, and copy over your changes.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sudo umount preserved&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; mkdir sda1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sudo mount &#x2F;dev&#x2F;sda1 sda1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; rsync&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -av --checksum&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; recovered&#x2F; sda1&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sync&lt;&#x2F;span&gt;&lt;span&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; sudo&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; umount sda1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;if all went well, you can boot the disk now. cheers 🍻&lt;&#x2F;p&gt;
&lt;h2 id=&quot;appendix&quot;&gt;Appendix&lt;&#x2F;h2&gt;
&lt;p&gt;the &lt;code&gt;patch_file.py&lt;&#x2F;code&gt; script:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;#!&#x2F;usr&#x2F;bin&#x2F;env python3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;replaces zero-pages, or partial zero-pages within a single file&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; os&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; subprocess&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; sys&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant z-other z-keyword z-operator&quot;&gt;PAGE_LEN =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4096&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant z-other z-keyword z-operator&quot;&gt;IN_DIR =&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;preserved&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant z-other z-keyword z-operator&quot;&gt;OUT_DIR =&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;recovered&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; patch_range&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;file_&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-annotation z-python&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type&quot;&gt; str&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-parameters z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt; logical_block&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-annotation z-python&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-parameters z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt; n_blocks&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-annotation z-python&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-parameters z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt; physical_block&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-annotation z-python&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;#39;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    patch a whole range of blocks within the file&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;#39;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    subprocess.check_output([&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        &amp;#39;dd&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        &amp;#39;if=&#x2F;dev&#x2F;sda1&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;        f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;of=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;file_&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        &amp;#39;bs=4096&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;        f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;seek=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;logical_block&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;        f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;skip=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;physical_block&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;        f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;count=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;n_blocks&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; copy_for_patch&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;path&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-annotation z-python&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type&quot;&gt; str&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;) -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-constant z-other&quot;&gt;    in_path = os.path.join(IN_DIR,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;.&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, path)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-constant z-other&quot;&gt;    out_path = os.path.join(OUT_DIR, path)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    subprocess.check_output([&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;rsync&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;-a&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;--relative&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-other z-keyword z-operator&quot;&gt;, in_path, OUT_DIR +&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;&#x2F;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; out_path&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; estimate_length&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;path&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-annotation z-python&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type&quot;&gt; str&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;) -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;#39;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    return the length of the file were there to be no trailing bytes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;#39;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;    contents =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; open&lt;&#x2F;span&gt;&lt;span&gt;(path,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;rb&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).read()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;    l =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(contents)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    while&lt;&#x2F;span&gt;&lt;span&gt; l&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-logical z-python&quot;&gt; and&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; contents[l-&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;] ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;        l -=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; l&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; main&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;path&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-annotation z-python&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type&quot;&gt; str&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-parameters z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt; auto_len&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-annotation z-python&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type&quot;&gt; bool&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-parameters z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt; patches&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-annotation z-python&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type&quot;&gt; list&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;    path = copy_for_patch(path)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;    old_size = os.stat(path).st_size&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span&gt; patch&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; patches:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;        logical_block, n_blocks, physical_block = patch&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        patch_range(path, logical_block, n_blocks, physical_block)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span&gt; auto_len:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        os.truncate(path, estimate_length(path))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    else&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        os.truncate(path, old_size)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; parse_args&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;args&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-annotation z-python&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type&quot;&gt; list&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;#39;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    return:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;      str: the relative file being operated on,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;      bool: auto-estimate len,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;      list: the ranges to patch&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;#39;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;    i =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;    inode =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; None&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;    file_ =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; None&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;    auto_len =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; False&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;    ranges = []&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    while&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; i &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(args):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;        arg = args[i]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; arg ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;-i&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;            inode =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;(args[i+&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;            i +=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;        elif&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; arg ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;-f&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;            file_ = args[i+&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;            i +=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;        elif&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; arg ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;--auto-len&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;            auto_len =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; True&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;            i +=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;        else&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;            logical_block, n_blocks, physical_block =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;, arg.split(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;,&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;            #vvv not actually required, but indicative of an error&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;            assert&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; logical_block &amp;lt; physical_block&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ranges.append((logical_block, n_blocks, physical_block))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;            i +=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    # inode doesn&amp;#39;t actually get used&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    # it&amp;#39;s useful just to keep the script invocations organized&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; file_, auto_len, ranges&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;if&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-magic z-python&quot;&gt; __name__&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;__main__&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;    main(*parse_args(sys.argv[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;:]))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;</content>
        
    </entry>
    <entry xml:lang="en">
        <title>A Reasonably Secure Mailserver Installation</title>
        <published>2022-04-06T00:00:00+00:00</published>
        <updated>2022-04-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://uninsane.org/blog/systemd-nspawn-postfix/"/>
        <id>https://uninsane.org/blog/systemd-nspawn-postfix/</id>
        
        <content type="html" xml:base="https://uninsane.org/blog/systemd-nspawn-postfix/">&lt;p&gt;i need software to receive emails, and possibly to send them too. i.e., a mailserver.
the mature mailserver implementations were all written in a time where security was
even worse than today. Postfix is among the better ones, but even it has a fair number of
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cve.mitre.org&#x2F;cgi-bin&#x2F;cvekey.cgi?keyword=postfix&quot;&gt;CVEs&lt;&#x2F;a&gt;.
its intended operation -- where it writes to mailboxes owned by different users --
relies on elevated access control. although the risks are mitigated by its design
around separation of concerns --
where only select portions of code get elevated permissions --
and the linux capabilities system, i still would not feel comfortable running
this without isolating it from other applications on the same machine.&lt;&#x2F;p&gt;
&lt;p&gt;enter systemd-nspawn. nspawn is an extremely lightweight container. it&#x27;s more of a transparent chroot:
package up the userspace of some linux distribution, place it in a directory, and then
nspawn uses the host kernel and performs the whole PID 1 boot sequence of that
chroot, virtualizing all the fs access and isolating the processes&#x2F;etc. we&#x27;ll use this
to create a container dedicated to postfix.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;&#x2F;h2&gt;
&lt;p&gt;start by creating the rootfs and launching it as a container. this assumes the host
is running Arch:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@host &#x2F;]# pacman -S arch-install-scripts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@host &#x2F;]# mkdir &#x2F;opt&#x2F;postfix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@host &#x2F;]# pacstrap -c &#x2F;opt&#x2F;postfix base postfix openbsd-netcat opendkim perl&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@host &#x2F;]# systemd-nspawn -D &#x2F;opt&#x2F;postfix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;gt;# passwd&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;  # choose some password you can remember for the rest of setup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;gt;# exit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;if you&#x27;re ssh&#x27;d into the host, you need to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;Systemd-nspawn#Root_login_fails&quot;&gt;relax some security settings&lt;&#x2F;a&gt;
in the container before it&#x27;ll let you login:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@host &#x2F;opt&#x2F;postfix]$ mv etc&#x2F;securetty etc&#x2F;securetty.OLD&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@host &#x2F;opt&#x2F;postfix]$ mv usr&#x2F;share&#x2F;factory&#x2F;etc&#x2F;securetty &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt;\&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;                             usr&#x2F;share&#x2F;factory&#x2F;etc&#x2F;securetty.OLD&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# then comment out the line containing securetty in usr&#x2F;lib&#x2F;tmpfiles.d&#x2F;arch.conf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;configure &lt;code&gt;myhostname&lt;&#x2F;code&gt; and &lt;code&gt;mydomain&lt;&#x2F;code&gt; in &lt;code&gt;&#x2F;opt&#x2F;postfix&#x2F;etc&#x2F;postfix&#x2F;main.cf&lt;&#x2F;code&gt;  (it&#x27;s fine for these to be the same: i use &lt;code&gt;uninsane.org&lt;&#x2F;code&gt; for both)&lt;&#x2F;p&gt;
&lt;p&gt;using the &lt;code&gt;--network-veth&lt;&#x2F;code&gt; flag, systemd will create a NAT&#x27;d network and expose the downstream to the container.
we can then forward ports across the NAT just like you would forward ports from your router to your PC&#x2F;server (port 25 here is the SMTP port):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@host &#x2F;]# systemd-nspawn -b --network-veth -p 25:25 -D &#x2F;opt&#x2F;postfix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;postfix&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; login: root&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Password:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;enter i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;t&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;[root@postfix ~]# systemctl enable --now systemd-resolved&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;[root@postfix ~]# systemctl enable --now postfix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# then create the db which maps email address to linux user accounts:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;[root@postfix ~]# newaliases&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;for the record, &lt;code&gt;&#x2F;etc&#x2F;postfix&#x2F;aliases&lt;&#x2F;code&gt; contains the mappings consumed by &lt;code&gt;newaliases&lt;&#x2F;code&gt;. the defaults work for us now but you&#x27;ll
want to tweak them later.&lt;&#x2F;p&gt;
&lt;p&gt;like HTTP, the SMTP grammar is human friendly. we can &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;Virtual_user_mail_system_with_Postfix,_Dovecot_and_Roundcube#Testing&quot;&gt;verify our setup with netcat&lt;&#x2F;a&gt;.
you can do this from within the container (substitute &lt;code&gt;localhost&lt;&#x2F;code&gt; for &lt;code&gt;&amp;lt;container&amp;gt;&lt;&#x2F;code&gt;), or from another box on your LAN (substitute the host&#x27;s IP&#x2F;name for &lt;code&gt;&amp;lt;container&amp;gt;&lt;&#x2F;code&gt;).
you probably don&#x27;t want to expose this to the WAN yet:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; nc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;containe&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;r&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 25&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;helo&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; uninsane.org&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;mail&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; from:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;test@uninsane.or&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;g&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;rcpt&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; to:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;root@uninsane.or&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;g&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;this&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; is a test.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function&quot;&gt;.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;quit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;mail should show up in the container at &lt;code&gt;var&#x2F;spool&#x2F;mail&#x2F;root&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;if this is intended as a single-user mailserver, you might want a catch-all mail rule:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@postfix &#x2F;]# echo &lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;@uninsane.org root&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;gt;&amp;gt; &#x2F;etc&#x2F;postfix&#x2F;virtual&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@postfix &#x2F;]# echo &lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;virtual_alias_maps = hash:&#x2F;etc&#x2F;postfix&#x2F;virtual&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;                        &#x2F;etc&#x2F;postfix&#x2F;main.cf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@postfix &#x2F;]# postmap &#x2F;etc&#x2F;postfix&#x2F;virtual&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@postfix &#x2F;]# systemctl restart postfix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;try the &lt;code&gt;nc&lt;&#x2F;code&gt; command from above again, but use &lt;code&gt;rcpt to:&amp;lt;anything@uninsane.org&lt;&#x2F;code&gt; and
the mail should be appended to that same &lt;code&gt;&#x2F;var&#x2F;spool&#x2F;mail&#x2F;root&lt;&#x2F;code&gt; file.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;non-root-user&quot;&gt;Non-Root User&lt;&#x2F;h2&gt;
&lt;p&gt;we&#x27;d prefer to be able to read mail &lt;em&gt;without&lt;&#x2F;em&gt; being root. so create a user dedicated to holding the mailboxes:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@postfix &#x2F;]# useradd --create-home --user-group vmail&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;edit &lt;code&gt;etc&#x2F;postfix&#x2F;main.cf&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-deleted&quot;&gt;- mail_owner = postfix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-inserted&quot;&gt;+ mail_owner = vmail&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;edit &lt;code&gt;etc&#x2F;postfix&#x2F;aliases&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-deleted&quot;&gt;- root: you&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-inserted&quot;&gt;+ root: vmail&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;edit &lt;code&gt;etc&#x2F;postfix&#x2F;virtual&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-deleted&quot;&gt;- @uninsane.org root&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-inserted&quot;&gt;+ @uninsane.org vmail&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;update the database mappings and then restart the services:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@postfix &#x2F;]# newaliases&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@postfix &#x2F;]# postmap &#x2F;etc&#x2F;postfix&#x2F;virtual&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@postfix &#x2F;]# postfix set-permissions&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@postfix &#x2F;]# systemctl restart postfix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;the &lt;code&gt;postfix&lt;&#x2F;code&gt; Arch package includes the &lt;code&gt;&#x2F;var&#x2F;spool&lt;&#x2F;code&gt; files which are now owned by &lt;code&gt;vmail&lt;&#x2F;code&gt;, and AFAICT Arch fixes package permissions on each boot.
so for these changes to take permanent effect, you&#x27;ll need to edit &lt;code&gt;lib&#x2F;systemd&#x2F;system&#x2F;postfix.service&lt;&#x2F;code&gt; to apply &lt;code&gt;set-permissions&lt;&#x2F;code&gt; on each boot:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-deleted&quot;&gt;- ExecStart=&#x2F;usr&#x2F;bin&#x2F;postfix start&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-inserted&quot;&gt;+ ExecStart=&#x2F;usr&#x2F;bin&#x2F;bash -c &amp;#39;&#x2F;usr&#x2F;bin&#x2F;postfix set-permissions \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-markup z-inserted&quot;&gt;+                             &amp;amp;&amp;amp; &#x2F;usr&#x2F;bin&#x2F;postfix start&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;because systemd limits postfix&#x27;s ability to write outside of &lt;code&gt;&#x2F;var&#x2F;spool&lt;&#x2F;code&gt;, you&#x27;ll need to change which files postfix tries to enforce permissions on if you want this to succeed.
in &lt;code&gt;etc&#x2F;postfix&#x2F;postfix-files&lt;&#x2F;code&gt;, comment out every line which starts with one of:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$config_directory&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;$daemon_directoy&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;$sample_directory&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;$readme_directory&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;$html_directory&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;$shlib_directory&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;$manpage_directory&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;since Arch manages these (correctly), you&#x27;re not really losing anything.&lt;&#x2F;p&gt;
&lt;p&gt;run that &lt;code&gt;nc&lt;&#x2F;code&gt; command again: this time mail should show up in &lt;code&gt;&#x2F;var&#x2F;spool&#x2F;mail&#x2F;vmail&lt;&#x2F;code&gt;,
and that file should be owned by the &lt;code&gt;vmail&lt;&#x2F;code&gt; user instead of &lt;code&gt;root&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;now we can work on the WAN side of things. to prevent spoofing &amp;amp; improve the likelihood that your
messages will be accepted by other servers, you&#x27;ll want to add some DNS records to your zone file:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a SPF DNS record (instructs recipients to enforce that your message originates from a specific IP)&lt;&#x2F;li&gt;
&lt;li&gt;a DKIM DNS record (signs the message content with a key owned by your mailserver)&lt;&#x2F;li&gt;
&lt;li&gt;a DMARC DNS record (allows you to receive reports from recipient mailservers)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;and of course you&#x27;ll need a MX record so others know where to send mail.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dkim-and-dns&quot;&gt;DKIM and DNS&lt;&#x2F;h2&gt;
&lt;p&gt;we installed opendkim during the earlier &lt;code&gt;pacstrap&lt;&#x2F;code&gt; invocation: now we&#x27;ll configure it to sign
outgoing messages:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@host &#x2F;opt&#x2F;postfix]$ cp usr&#x2F;share&#x2F;doc&#x2F;opendkim&#x2F;opendkim.conf.sample &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt;\&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;                             etc&#x2F;opendkim&#x2F;opendkim.conf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;open &lt;code&gt;etc&#x2F;opendkim&#x2F;opendkim.conf&lt;&#x2F;code&gt; in an editor and:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;update the &lt;code&gt;Domain&lt;&#x2F;code&gt; field&lt;&#x2F;li&gt;
&lt;li&gt;point the &lt;code&gt;KeyFile&lt;&#x2F;code&gt; to &lt;code&gt;&#x2F;home&#x2F;vmail&#x2F;dkim&#x2F;mx1.private&lt;&#x2F;code&gt; (created later)&lt;&#x2F;li&gt;
&lt;li&gt;set &lt;code&gt;UserID&lt;&#x2F;code&gt; to &lt;code&gt;vmail&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;make sure &lt;code&gt;Socket&lt;&#x2F;code&gt; points to &lt;code&gt;inet:8891@localhost&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;and consider changing Canonicalization from &lt;code&gt;simple&#x2F;simple&lt;&#x2F;code&gt; to &lt;code&gt;relaxed&#x2F;simple&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;then append this to &lt;code&gt;etc&#x2F;postfix&#x2F;main.cf&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# For use by dkim milter&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;smtpd_milters&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; = inet:localhost:8891&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;non_smtpd_milters&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; $smtpd_milters&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;milter_default_action&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; = accept&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;generate the keys (run this as the &lt;code&gt;vmail&lt;&#x2F;code&gt; user):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[vmail@postfix &#x2F;home&#x2F;vmail]$ mkdir dkim &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; cd&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; dkim&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[vmail@postfix &#x2F;home&#x2F;vmail&#x2F;dkim]$ opendkim-genkey -r -s mx1 -d uninsane.org&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;start the service:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@postfix &#x2F;]# systemctl enable --now opendkim&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;add the &lt;code&gt;mx1._domainkey&lt;&#x2F;code&gt; TXT record (documented in &lt;code&gt;&#x2F;home&#x2F;vmail&#x2F;dkim&#x2F;mx1.txt&lt;&#x2F;code&gt;) into your zone file.&lt;&#x2F;p&gt;
&lt;p&gt;then run the &lt;code&gt;nc&lt;&#x2F;code&gt; example again. you should get mail that has an &lt;code&gt;Authentication-Results&lt;&#x2F;code&gt; header -- which fails,
since we didn&#x27;t sign our message.&lt;&#x2F;p&gt;
&lt;p&gt;using the postfix &lt;code&gt;sendmail&lt;&#x2F;code&gt; command we should be able to send something with a valid signature:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@postfix &#x2F;]# sendmail test@uninsane.org&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;this&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; message should be signed&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function&quot;&gt;.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@postfix &#x2F;]# cat &#x2F;var&#x2F;mail&#x2F;vmail&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[...]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;DKIM-Signature:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; v=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;rsa-sha256&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; c&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;relaxed&#x2F;simple&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; d&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;uninsane.org&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; s&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;mx1&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        t&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;[...&lt;&#x2F;span&gt;&lt;span&gt;];&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; bh&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;[...&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        h&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Date:From&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        b&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;[...&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Message-Id:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;YYYYMMDDTTTTTT.NNNNNNNNNNN@uninsane.or&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;g&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Date:&lt;&#x2F;span&gt;&lt;span&gt; [...]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;From:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; root@uninsane.org&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;this&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; message should be signed&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;then add a SPF DNS record and a DMARC record to receive delivery reports.
if you&#x27;re running a large mail server it would be good to install &lt;code&gt;opendmarc&lt;&#x2F;code&gt; to send delivery reports to &lt;em&gt;other&lt;&#x2F;em&gt; servers (like mine!), but i&#x27;ll skip that here.
throw in the MX record, and your zone file should look like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;asm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;; mailserver shares an IP with the rest of uninsane.org.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@                     MX &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;    uninsane.org.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;; Sender Policy Framework:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;;   +mx     =&amp;gt; mail passes if it originated from the MX&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;;   +a      =&amp;gt; mail passes if it originated from the A address of this domain&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;;   +ip4:.. =&amp;gt; mail passes if it originated from this IP&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;;   -all    =&amp;gt; mail fails if none of these conditions were met&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@                     TXT      &amp;quot;v=spf1 &lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;ip4:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;203.0&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;113.1&lt;&#x2F;span&gt;&lt;span&gt; a mx -all&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;; DKIM public key:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;mx1._domainkey        TXT      &amp;quot;v=DKIM1&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;; k=rsa; s=email; p=&amp;lt;big long string&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;; DMARC fields &amp;lt;https:&#x2F;&#x2F;datatracker.ietf.org&#x2F;doc&#x2F;html&#x2F;rfc7489&amp;gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;;   p=none|quarantine|reject: what to do with failures&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;;   sp = p but for subdomains&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;;   rua = where to send aggregrate reports&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;;   ruf = where to send individual failure reports&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;;   fo=0|1|d|s  controls WHEN to send failure reports&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;;     (1=on bad alignment; d=on DKIM failure; s=on SPF failure);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;; Additionally:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;;   adkim=r|s  (is DKIM relaxed [default] or strict)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;;   aspf=r|s   (is SPF relaxed [default] or strict)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;;   pct = sampling ratio for punishing failures (default 100 for 100%)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;;   rf = report format&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;;   ri = report interval&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;_dmarc                TXT      (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;v=DMARC1&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;;p=quarantine;sp=reject;fo=1:d:s;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;rua=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;mailto:&lt;&#x2F;span&gt;&lt;span&gt;admin+mail@uninsane.org&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;;ruf=mailto:admin+mail@uninsane.org&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;validation&quot;&gt;Validation&lt;&#x2F;h2&gt;
&lt;p&gt;validate your DMARC record (and DKIM, SPF if you want): &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;dmarcian.com&#x2F;dmarc-inspector&#x2F;&quot;&gt;https:&#x2F;&#x2F;dmarcian.com&#x2F;dmarc-inspector&#x2F;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;confirm that your domain&#x2F;IP isn&#x27;t blacklisted: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;multirbl.valli.org&#x2F;&quot;&gt;https:&#x2F;&#x2F;multirbl.valli.org&#x2F;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;try sending&#x2F;receiving mail: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.appmaildev.com&#x2F;en&#x2F;dkim&quot;&gt;https:&#x2F;&#x2F;www.appmaildev.com&#x2F;en&#x2F;dkim&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;if these fail, check &lt;code&gt;journalctl -u postfix&lt;&#x2F;code&gt;. if there&#x27;s no indication of traffic, it may be that your ISP blocks outbound port 25.
you can check for that with &lt;code&gt;nc -vz gmail.com 25&lt;&#x2F;code&gt; (will exit 0 if the port is open, hang if the port is blocked).&lt;&#x2F;p&gt;
&lt;p&gt;less probably, your ISP might block &lt;em&gt;inbound&lt;&#x2F;em&gt; port 25. check for that here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;canyouseeme.org&#x2F;&quot;&gt;https:&#x2F;&#x2F;canyouseeme.org&#x2F;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;in my case, Centurylink blocks both directions, so i can&#x27;t even use this setup to &lt;em&gt;receive&lt;&#x2F;em&gt; mail.
for this case, i&#x27;ll explore running postfix on a non-standard port and using a mail forwarder or transparent proxy in a subsequent blog post.&lt;&#x2F;p&gt;
&lt;p&gt;but if your mail server is working, then instruct systemd to launch the container when the host boots. while the container&#x27;s active, run:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@host &#x2F;]# ln -s &#x2F;opt&#x2F;postfix &#x2F;var&#x2F;lib&#x2F;machines&#x2F;postfix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@host &#x2F;]# machinectl enable postfix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@host &#x2F;]# systemctl enable machines.target&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;alternatively, you could move the whole machine into &lt;code&gt;&#x2F;var&#x2F;lib&#x2F;machines&#x2F;postfix&lt;&#x2F;code&gt; instead of symlinking it.&lt;&#x2F;p&gt;
&lt;p&gt;populate &lt;code&gt;&#x2F;etc&#x2F;systemd&#x2F;nspawn&#x2F;postfix.nspawn&lt;&#x2F;code&gt; (you may need to create the directory) with the settings we used earlier:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-section&quot;&gt;Network&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;VirtualEthernet&lt;&#x2F;span&gt;&lt;span&gt;=o&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;n&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;Port&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;25&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;:25&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;then you can stop the machine, restart it, and administer it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@host &#x2F;]# machinectl stop postfix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@host &#x2F;]# machinectl start postfix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[root@host &#x2F;]# machinectl login postfix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;you now have a postfix instance which starts on boot and can send&#x2F;receive mail as long as port 25 is accessible. later on you may want to provide client access in a friendlier way than directly reading the spool or invoking &lt;code&gt;sendmail&lt;&#x2F;code&gt;. that could be done by installing something like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Dovecot_(software)&quot;&gt;dovecot&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Self Hosting in More Detail Than You Asked For</title>
        <published>2022-03-29T00:00:00+00:00</published>
        <updated>2022-03-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://uninsane.org/blog/self-hosting/"/>
        <id>https://uninsane.org/blog/self-hosting/</id>
        
        <content type="html" xml:base="https://uninsane.org/blog/self-hosting/">&lt;p&gt;well i fell down the rabbit hole. this domain hosts 6 services and counting:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;nginx (serving you this page)&lt;&#x2F;li&gt;
&lt;li&gt;gitea (for git hosting&#x2F;collaboration)&lt;&#x2F;li&gt;
&lt;li&gt;Pleroma (for federated &lt;del&gt;shitposting&lt;&#x2F;del&gt; social networking)&lt;&#x2F;li&gt;
&lt;li&gt;Matrix (for chat&#x2F;instant messaging)&lt;&#x2F;li&gt;
&lt;li&gt;Jellyfin (for A&#x2F;V streaming)&lt;&#x2F;li&gt;
&lt;li&gt;Trust DNS (for serving the DNS records of all the above)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;how-it-started-a-brief-history-of-bitcoin&quot;&gt;How It Started: A Brief History of Bitcoin&lt;&#x2F;h2&gt;
&lt;p&gt;i have a caffeine problem.
first thing in the morning, i brew myself 3 double-shots of espresso.
it&#x27;s not even &quot;make one, drink it, make the second, drink it, make the third&quot;: i brew three cups at once and &lt;em&gt;then&lt;&#x2F;em&gt; sip them on the couch while catching up on &quot;The News&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;i wanted to break the habit. rather, i enjoy the &lt;em&gt;benefits&lt;&#x2F;em&gt; of caffeine, but i dislike the &lt;em&gt;dependency&lt;&#x2F;em&gt;.
caffeine&#x27;s not the only drug in its class, but you have to jump through hoops to obtain any of its cousins.
pretty soon i got &lt;em&gt;very&lt;&#x2F;em&gt; accustomed to using Tor, PGP, etc.
pretty soon i found myself caring much more about legal systems than before. and about social norms. and about the whole area of political philosophy.
and pretty soon i noticed the weird chilling effects in my everyday life.
the legal angle, obviously, but also just social inhibition:
i don&#x27;t want to be &quot;that guy&quot; who drags the whole room into a topic only i care about.
and so when it comes to the bits of myself which are the most unusual, the most deviant, the most personal,
i don&#x27;t really have a space to explore those things openly.
and that disappoints me.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-a-fediverse&quot;&gt;What&#x27;s a &quot;Fediverse&quot;?&lt;&#x2F;h2&gt;
&lt;p&gt;there&#x27;s this thing called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Mastodon_(software)&quot;&gt;Mastodon&lt;&#x2F;a&gt;, oft categorized as a &quot;decentralized Twitter&quot;.
i tried it a few years ago and had a positive experience, but didn&#x27;t really have that strong a desire for &quot;social media&quot; at the time.
after pandemic madness, or maybe just after experiencing that shrinking social circle that my older friends like to complain about, i&#x27;m a little more curious about the social internet than before.&lt;&#x2F;p&gt;
&lt;p&gt;the novel thing about Mastodon is that it&#x27;s &quot;federated&quot;.
anyone can host their own server and bridge it to the rest of the network.
the main protocol it speaks is ActivityPub (AP), and there&#x27;s a lot of software beyond Mastodon which speaks AP.&lt;&#x2F;p&gt;
&lt;p&gt;i discovered &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pleroma.social&#x2F;&quot;&gt;Pleroma&lt;&#x2F;a&gt;, which claims support for hosting behind Tor, and i even found a few Tor-bridged instances out there.
so i thought i&#x27;d set up my own and dive in.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-do-i-host-this-shit&quot;&gt;How Do I Host This Shit&lt;&#x2F;h2&gt;
&lt;p&gt;using Tor as a client is easy: just install the Tor browser and go.
running a service behind Tor is slightly more complex, but still fairly easy to understand:
run the Tor daemon. it exposes a SOCKS5 proxy service on port 9050.
launch Pleroma and tell it to proxy all TCP traffic through that port.
now you can make outbound requests to the Fediverse from behind Tor.
but you have no public address yet, so you can&#x27;t get incoming messages.
configure Pleroma to listen on some local port.
then configure Tor to run some Onion Service that&#x27;s serviced by this local port. Tor will generate some &amp;lt;hash&amp;gt;.onion address which is now your publicly routable address.&lt;&#x2F;p&gt;
&lt;p&gt;to cap: external actors send HTTP&#x2F;TCP requests to &amp;lt;hash&amp;gt;.onion, these are serviced by Pleroma and the response is sent back through this tunnel.
when Pleroma is the &lt;em&gt;initiator&lt;&#x2F;em&gt; of a request, it proxies that to the recipient by tunneling it through a separate Tor SOCKS5 proxy.
with this setup you can &lt;em&gt;send&lt;&#x2F;em&gt; messages to anyone on the Fediverse (Tor or clearnet), but you can only &lt;em&gt;receive&lt;&#x2F;em&gt; messages from those who understand .onion addresses.&lt;&#x2F;p&gt;
&lt;p&gt;this whole process is helpfully documented in the Pleroma &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.pleroma.social&#x2F;backend&#x2F;configuration&#x2F;onion_federation&#x2F;&quot;&gt;docs&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;sounds-pretty-fragile&quot;&gt;Sounds Pretty Fragile&lt;&#x2F;h2&gt;
&lt;p&gt;too much complexity? AHAHA. let&#x27;s add more.&lt;&#x2F;p&gt;
&lt;p&gt;(there are millions of fridges out there running Linux as i write. i just saw somebody post a photo of their oven after its OS crashed. ponder that.)&lt;&#x2F;p&gt;
&lt;p&gt;so the worry here is that Pleroma might be tricked or bugged into ignoring the proxy and communicating over the clearnet.
we can take inspiration from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Whonix&quot;&gt;Whonix&lt;&#x2F;a&gt; for this.
set up two machines:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;the first machine (U) has two NICs. one NIC is connected to the WAN and the other NIC is connected directly to the second machine (D). U runs nothing but a Tor proxy, exposing only the proxy endpoint to D (and relaying traffic from its onion service to D).&lt;&#x2F;li&gt;
&lt;li&gt;the second machine (D) has only the one NIC, connected directly to U. there is no way for any traffic to escape the machine except by passing through the Tor proxy.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;in actuality, we&#x27;ll want to restrict D even further: it probably has hardware WiFi or Bluetooth, which is just another vector. so we package up all the application software and throw it inside a VM on D, exposing no IO except that relevant NIC to the VM.&lt;&#x2F;p&gt;
&lt;p&gt;congrats, you&#x27;ve got a decently secure, anonymized computing setup. now you have to deal with the fact that even though Pleroma and Mastodon &lt;em&gt;support&lt;&#x2F;em&gt; federation over Tor, it&#x27;s an optional configuration that pretty much nobody out there enables. plus, the Pleroma frontend requires Javascript, which just means you&#x27;ve shifted the security burden from the server onto the client.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;you-re-telling-me-it-was-all-for-naught&quot;&gt;You&#x27;re Telling Me It Was All for Naught?&lt;&#x2F;h2&gt;
&lt;p&gt;as if you didn&#x27;t see it coming.
but hey, i&#x27;m sure you&#x27;ll find some way to use all that infrastructure for your... &lt;em&gt;Bitcoin&lt;&#x2F;em&gt; activities.&lt;&#x2F;p&gt;
&lt;p&gt;so anyway, give up on your dream of perfect anonymity. you know first-hand now how difficult and restricting that actually is. meditate on &lt;em&gt;why&lt;&#x2F;em&gt; you&#x27;re spending so much time fiddling with these logic gates and bits and reorient.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;self-hosting-is-fetch&quot;&gt;Self-Hosting Is Fetch&lt;&#x2F;h2&gt;
&lt;p&gt;i think this whole Internet thing is maybe just a social playfield?
something to do with exploration, connections, creativity, and self-discovery?
an open environment wherein &lt;em&gt;anyone&lt;&#x2F;em&gt; with time&#x2F;dedication can do these things?&lt;&#x2F;p&gt;
&lt;p&gt;wait, is &lt;em&gt;that&lt;&#x2F;em&gt; where the Web went?&lt;&#x2F;p&gt;
&lt;p&gt;i don&#x27;t want to oversimplify or aggrandize it (i will anyway), but when i recount my favorite eras of the internet, they&#x27;re like this:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;middle school: i built super amateur videogames with my buddies, hosted the downloads + discussion for these on a site we &lt;em&gt;built by hand&lt;&#x2F;em&gt;, and then distributed the binaries + web link by &lt;em&gt;handing out CDs in the school hallway&lt;&#x2F;em&gt;. it was &lt;em&gt;stupidly&lt;&#x2F;em&gt; successful (surely a function of the era).&lt;&#x2F;li&gt;
&lt;li&gt;high school: i encountered my first fandom. i wrote amateur music, internet friends made the song art, these things were shared on blogs and Skype and message boards. i attended cons and had the repeat experience of somebody discovering &quot;oh, you&#x27;re the guy who made &lt;em&gt;that&lt;&#x2F;em&gt;&quot; 10 minutes into one of those late-night hotel-room conversations.&lt;&#x2F;li&gt;
&lt;li&gt;college: i maintained some open source projects and blogged about technical&#x2F;academic topics. people from across the world emailed me private responses that must have taken &lt;em&gt;hours&lt;&#x2F;em&gt; to write. i&#x27;d video-chat with people to help them port&#x2F;extend my software to larger purposes. a professor even assigned my work as reading material for their students.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;and i never really &lt;em&gt;got&lt;&#x2F;em&gt; it. but i think it was just simple, social, creativity. and i want more of that in my life.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;stripping-it-down&quot;&gt;Stripping It Down&lt;&#x2F;h2&gt;
&lt;p&gt;i don&#x27;t really need anonymity for this project, in fact strict anonymity would &lt;em&gt;detract&lt;&#x2F;em&gt; from it. i just need whatever level of pseudonymity helps me to let my guard down (and to not worry about e.g. identity theft).&lt;&#x2F;p&gt;
&lt;p&gt;that host machine (D) already has all the stuff we need for a secure-enough system if we strip out the anonymizing function of U.
so do that, and use your Pleroma instance to explore the Fediverse.
respectably insert yourself into conversations with everyday people and &lt;em&gt;make connections&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;find some little bug, or missing feature, and &lt;em&gt;create&lt;&#x2F;em&gt; a fix for it.
set up a Matrix (or xmpp) instance and reach out to the devs to coordinate.
set up a gitea instance in which to host your improved version of the project and from which to initiate a merge request.
give yourself your own &lt;em&gt;personal&lt;&#x2F;em&gt; homepage on the Web with a static site builder like Zola.
throw all this behind nginx so that you can host these services on different subdomains on the same physical host.
use &lt;code&gt;certbot&lt;&#x2F;code&gt;&#x2F;LetsEncrypt to secure the http traffic in all of 10 minutes.
spin up different systemd-nspawn&#x2F;LXC&#x2F;Qemu instances to isolate each service, or ditch proper containerization and just embrace seperate, privilege-limited user accounts for each service.
you make the call.
just remember to take backups seriously, because things &lt;em&gt;will&lt;&#x2F;em&gt; go wrong as you&#x27;re fiddling with all this stuff.&lt;&#x2F;p&gt;
&lt;p&gt;once you&#x27;re tired of updating DNS subdomain records through your registrar&#x27;s portal, host your own nameserver.
point your toplevel domain to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;freedns.afraid.org&#x2F;&quot;&gt;afraid.org&lt;&#x2F;a&gt;&#x27;s free &amp;amp; friendly dynamic DNS service if you have an unstable residential IP.&lt;&#x2F;p&gt;
&lt;p&gt;at some point, you&#x27;ll have to deal with email.
the state of email on the web is... pretty broken, so i&#x27;ll forgive you if you settle on gmail&#x2F;hosted Zoho&#x2F;etc.
really you can -- and maybe should -- skip as many of these components as you want if they don&#x27;t align with your mission.
but just remember that it&#x27;s &lt;em&gt;you&lt;&#x2F;em&gt; who create the web.
this was and can be a &lt;em&gt;person to person&lt;&#x2F;em&gt; network.
and there are persons out there who &lt;em&gt;want you in it&lt;&#x2F;em&gt;.
if you read this far and want a hand in any of it, reach out to another person.
message me through one of the contacts listed on my &lt;a href=&quot;https:&#x2F;&#x2F;uninsane.org&#x2F;about&#x2F;&quot;&gt;about page&lt;&#x2F;a&gt;.
i promise i&#x27;ll respond, in all likelihood i&#x27;ll be &lt;em&gt;happy&lt;&#x2F;em&gt; to share this space with you.&lt;&#x2F;p&gt;
&lt;p&gt;Colin&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
