<?xml version="1.0" encoding="UTF-8"?>
<!-- generator="FeedCreator 1.8" -->
<?xml-stylesheet href="https://wiki.qlyoung.net/lib/exe/css.php?s=feed" type="text/css"?>
<rdf:RDF
    xmlns="http://purl.org/rss/1.0/"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
    xmlns:dc="http://purl.org/dc/elements/1.1/">
    <channel rdf:about="https://wiki.qlyoung.net/feed.php">
        <title>qlyoung&#039;s wiki</title>
        <description></description>
        <link>https://wiki.qlyoung.net/</link>
        <image rdf:resource="https://wiki.qlyoung.net/_media/wiki/dokuwiki.svg" />
       <dc:date>2026-04-29T16:56:12+00:00</dc:date>
        <items>
            <rdf:Seq>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/adaptive_cruise"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/autoarchaeology"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/av1"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/bird_bar"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/blog"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/books"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/car_scuba_rack"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/cocktail_bars"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/consistency"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/degoogling"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/digital_mapping"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/disable_bracketed_paste_in_gnu_readline"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/dive_light_burn_times"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/diy_oxygen_analyzer"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/documentation"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/fall"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/file_transfer"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/gambling"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/insurance"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/iphone"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/just"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/location_history"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/music_management"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/my_web"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/network_simulation_with_k8s-topo_on_raspi_cluster"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/note_taking_programs"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/oxygen_rebreather"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/password_management"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/peak_sets"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/peak_tributes"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/personal_infrastructure"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/photo_management"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/possession_minimalism"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/power_juice"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/quotes"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/restaurants"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/sleep"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/start"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/syncing"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/task_tracking"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/tiered_storage"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/too_many_projects_too_little_time"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/underwater_videography_with_gopro"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/vehicle_leases"/>
                <rdf:li rdf:resource="https://wiki.qlyoung.net/whole_foods_pre_seasoned_protein_tier_list"/>
            </rdf:Seq>
        </items>
    </channel>
    <image rdf:about="https://wiki.qlyoung.net/_media/wiki/dokuwiki.svg">
        <title>qlyoung's wiki</title>
        <link>https://wiki.qlyoung.net/</link>
        <url>https://wiki.qlyoung.net/_media/wiki/dokuwiki.svg</url>
    </image>
    <item rdf:about="https://wiki.qlyoung.net/adaptive_cruise">
        <dc:format>text/html</dc:format>
        <dc:date>2024-01-06T05:27:46+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>adaptive_cruise</title>
        <link>https://wiki.qlyoung.net/adaptive_cruise</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;adaptive_cruise&quot;&gt;adaptive cruise&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
My car has an adaptive cruise control system. Adaptive cruise is a variation on traditional fixed-speed cruise control that adds the ability to maintain a follow distance from a leading car. For example, the adaptive cruise system can be set to 80mph and will keep the car going 80mph as long as there&amp;#039;s no vehicle in front of it. If it comes up behind a lead vehicle that is going slower, say 65mph, then it will slow to 65mph and maintain some reasonably safe following distance, say 25m. This is quite useful because it eliminates the need to adjust cruise control settings to accommodate slower traffic.
&lt;/p&gt;

&lt;p&gt;
I&amp;#039;ve illustrated this scenario with a diagram. The actual speed of vehicles is denoted with &lt;code&gt;(parentheses)&lt;/code&gt; and the adaptive cruise control setting with &lt;code&gt;[brackets]&lt;/code&gt;. &lt;code&gt;L&lt;/code&gt; is the lead car and &lt;code&gt;C&lt;/code&gt; is our car. Let&amp;#039;s say each “.” character is about 5 meters.
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;L (65)
.
.
.
.
.
C (65) [80]&lt;/pre&gt;

&lt;p&gt;
Everything is fine here. Now let&amp;#039;s get on the highway, introducing another lane (&lt;code&gt;|&lt;/code&gt;) and two more vehicles. One of the vehicles &lt;code&gt;X&lt;/code&gt; is adjacent to our lead car and the other &lt;code&gt;F&lt;/code&gt; a is a driver following us.
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;L (65)      | X (65)
.           | .
.           | .
.           | .
.           | .
.           | .
C (65) [80] | .
.           | .
.           | .
F (65)      |&lt;/pre&gt;

&lt;p&gt;
Our car &lt;code&gt;C&lt;/code&gt; is maintaining a safe follow distance of about 25 meters from the lead car, courtesy of its ACC system. But the speed limit is 80mph and &lt;code&gt;F&lt;/code&gt; wants to go 80mph. Looking at the diagram it&amp;#039;s clear that there&amp;#039;s no scenario here that is going to allow &lt;code&gt;F&lt;/code&gt; to go 80mph unless &lt;code&gt;L&lt;/code&gt; or &lt;code&gt;X&lt;/code&gt; decides to change their speeds.
&lt;/p&gt;

&lt;p&gt;
Virtually every time I find myself in this scenario (driving &lt;code&gt;C&lt;/code&gt;), &lt;code&gt;F&lt;/code&gt; decides to overtake and merge in between &lt;code&gt;C&lt;/code&gt; and &lt;code&gt;L&lt;/code&gt;, ending up offset by about 25 meters from their original position but still traveling at their original speed of 65mph.
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;L (65)      | X (65)
.           | .
.           | .
F (65)      | .
.           | .
.           | .
C (65) [80] | .&lt;/pre&gt;

&lt;p&gt;
Now we no longer have the desired 25m follow distance from the car in front of us, so the ACC system briefly slows the vehicle to create the gap then resumes cruising at 65mph.
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;L (65)      | X (65)
.           | .
.           | .
.           | .
F (65)      | .
.           | .
.           | .
.           | .
.           | .
.           | .
C (65) [80] | .
.           | .
.           | .
G (65)      | .&lt;/pre&gt;

&lt;p&gt;
Oh look, we have a new follower &lt;code&gt;G&lt;/code&gt;…I wonder what will happen next…
&lt;/p&gt;

&lt;p&gt;
And so it goes. I have had this happen countless times, I even get the finger or see the driver yelling at me as they pass by.
&lt;/p&gt;

&lt;p&gt;
Hence the dilemma: Do you use the system that automatically maintains a safe follow distance resulting in getting verbally abused and which causes others around you to drive in an unsafe manner? Or do you keep an unsafe follow distance to prevent that behavior?
&lt;/p&gt;

&lt;p&gt;
In the specific situation here, the best action is to get into the right hand lane and wait until the situation in the left hand lane improves. However, in practice this frequently occurs when &lt;code&gt;L&lt;/code&gt; is going marginally faster than &lt;code&gt;X&lt;/code&gt; (e.g. 65mph), so you will pass them eventually and it makes sense to stay in the left hand lane, in which case you are subject to the behavior.
&lt;/p&gt;

&lt;p&gt;
The general phenomenon isn&amp;#039;t limited to adaptive cruise. In general, the safer you drive the more upset other people will be at you.
&lt;/p&gt;

&lt;p&gt;
I have yet to draw any worthwhile conclusions from this phenomenon.
&lt;/p&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/from_blog?do=showtag&amp;amp;tag=from_blog&quot; class=&quot;wikilink1&quot; title=&quot;tag:from_blog&quot; rel=&quot;tag&quot;&gt;from blog&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/technology?do=showtag&amp;amp;tag=technology&quot; class=&quot;wikilink1&quot; title=&quot;tag:technology&quot; rel=&quot;tag&quot;&gt;technology&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/humanity?do=showtag&amp;amp;tag=humanity&quot; class=&quot;wikilink1&quot; title=&quot;tag:humanity&quot; rel=&quot;tag&quot;&gt;humanity&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/autoarchaeology">
        <dc:format>text/html</dc:format>
        <dc:date>2025-10-06T02:45:51+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>autoarchaeology</title>
        <link>https://wiki.qlyoung.net/autoarchaeology</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;autoarchaeology&quot;&gt;autoarchaeology&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
def. discovering information about oneself via “&lt;a href=&quot;https://en.wikipedia.org/wiki/material culture&quot; class=&quot;interwiki iw_wp&quot; title=&quot;https://en.wikipedia.org/wiki/material culture&quot;&gt;material culture&lt;/a&gt;”.
&lt;/p&gt;

&lt;p&gt;
As I get older I remember less about myself. Where was I in a given year? Where was I living, who was I hanging out with? What were my interests? What am I not remembering?
&lt;/p&gt;

&lt;p&gt;
Lately I&amp;#039;ve been doing a sort of archaeological dig on myself, investigating my own records, belongings, and digital traces in order to remember.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;autoarchaeology&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;autoarchaeology&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-425&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit2&quot; id=&quot;location&quot;&gt;location&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Where was I?
&lt;/p&gt;

&lt;p&gt;
Materials:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Bills - these often have addresses and can tell you where you lived in a particular time period.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Receipts - if digital (online shopping), almost always have shipping addresses&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Email is a good source, as in every case&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Photographs - if you took them or are in them, they localize you to a place - metadata or memory can supply the date, and often have GPS coordinates. Geotagging photos is super useful. Even if you don&amp;#039;t have geotagged photos, someone else might&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Google Timeline, or anything that you use or used to use that logged your location&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Flight receipts - email, credit card portals, airline websites, travel portals you use&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Transaction history - credit card transaction logs always list merchant names can localize you to a place on a date&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Think outside the box. Lots of things log your location. Online account login history can show you if you logged in on a particular device; that can localize you to a place.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;location&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;location&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;426-1422&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit3&quot; id=&quot;friends&quot;&gt;friends&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Who was I with?
&lt;/p&gt;

&lt;p&gt;
Materials:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Instant message logs&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Email&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Photographs&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
All of these show you who you were talking to or who you were with.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;friends&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;friends&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;1423-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/av1">
        <dc:format>text/html</dc:format>
        <dc:date>2023-09-24T01:46:00+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>av1</title>
        <link>https://wiki.qlyoung.net/av1</link>
        <description>
&lt;p&gt;
&lt;a href=&quot;https://en.wikipedia.org/wiki/AV1&quot; class=&quot;interwiki iw_wp&quot; title=&quot;https://en.wikipedia.org/wiki/AV1&quot;&gt;AV1&lt;/a&gt; is a new video coding format. It achieves better compression than H264, similar compression to H265 and unlike H265, has an uncomplicated licensing scheme and is royalty free. There is a sister format for images called &lt;a href=&quot;https://en.wikipedia.org/wiki/AVIF&quot; class=&quot;interwiki iw_wp&quot; title=&quot;https://en.wikipedia.org/wiki/AVIF&quot;&gt;AVIF&lt;/a&gt;. You will be seeing more of these files on the web as time goes on.
&lt;/p&gt;

&lt;p&gt;
If you got here because a video wouldn&amp;#039;t play, it&amp;#039;s probably because macOS and iOS do not support AV1. This is Apple&amp;#039;s fault, not mine. There is no legitimate reason that Apple cannot or should not support AV1; they simply chose not to. If you are a macOS or iOS user (as I am), I encourage you to bring this to Apple&amp;#039;s attention.
&lt;/p&gt;

&lt;p&gt;
If you&amp;#039;re on Windows and it doesn&amp;#039;t play, &lt;a href=&quot;https://apps.microsoft.com/store/detail/av1-video-extension/9MVZQVXJBQ9V&quot; class=&quot;urlextern&quot; title=&quot;https://apps.microsoft.com/store/detail/av1-video-extension/9MVZQVXJBQ9V&quot; rel=&quot;ugc nofollow&quot;&gt;try this&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
You are probably wondering, why not use &lt;a href=&quot;https://en.wikipedia.org/wiki/Advanced Video Coding&quot; class=&quot;interwiki iw_wp&quot; title=&quot;https://en.wikipedia.org/wiki/Advanced Video Coding&quot;&gt;H264&lt;/a&gt; since everyone can play it? That&amp;#039;s because H264 is restrictively licensed and collects royalties, and AV1 does not. I apologize for the inconvenience, but it is a way of driving AV1 adoption, which ultimately serves the greater good. Thank you for your sacrifice.
&lt;/p&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/bird_bar">
        <dc:format>text/html</dc:format>
        <dc:date>2026-03-26T21:39:23+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>bird_bar</title>
        <link>https://wiki.qlyoung.net/bird_bar</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;bird_bar&quot;&gt;bird bar&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
Bird bar is a bird feeder with a camera on it. A computer vision model watches the feed to identify the species of birds that visit the feeder. The video feed is also live-streamed to the internet, where live info about the birds is shown. Patron data is collected in a time series database for analysis.
&lt;/p&gt;

&lt;p&gt;
Live stream: &lt;a href=&quot;https://twitch.tv/thebirdbar&quot; class=&quot;urlextern&quot; title=&quot;https://twitch.tv/thebirdbar&quot; rel=&quot;ugc nofollow&quot;&gt;https://twitch.tv/thebirdbar&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Stats: &lt;a href=&quot;https://birdcam.qlyoung.net/&quot; class=&quot;urlextern&quot; title=&quot;https://birdcam.qlyoung.net/&quot; rel=&quot;ugc nofollow&quot;&gt;https://birdcam.qlyoung.net/&lt;/a&gt;
&lt;/p&gt;
&lt;div id=&quot;plugin__gallery_42ba&quot; class=&quot;plugin-gallery align-full&quot;&gt;&lt;div class=&quot;gallery-page&quot; id=&quot;gallery__42ba_0&quot; style=&quot;grid-template-columns: repeat(auto-fill, minmax(150px, 1fr))&quot;&gt;&lt;figure class=&quot;gallery-image&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/_detail/birdbar/screenshots/bluebird-and-chickadee.png?id=bird_bar&quot; title=&quot;Bluebird-and-chickadee&quot; data-caption=&quot;&amp;lt;b&amp;gt;Bluebird-and-chickadee&amp;lt;/b&amp;gt;&quot; class=&quot;lightbox JSnocheck&quot; rel=&quot;lightbox[gal-632d05e845b41f674fa5757d9eed]&quot; data-url=&quot;/_media/birdbar/screenshots/bluebird-and-chickadee.png&quot;&gt;&lt;img width=&quot;300&quot; height=&quot;300&quot; src=&quot;/_media/birdbar/screenshots/bluebird-and-chickadee.png?w=300&amp;amp;h=300&amp;amp;tok=36e7c6&quot; alt=&quot;bluebird-and-chickadee.png&quot; loading=&quot;lazy&quot; /&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;gallery-image&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/_detail/birdbar/screenshots/dove-on-feeder-two.png?id=bird_bar&quot; title=&quot;Dove-on-feeder-two&quot; data-caption=&quot;&amp;lt;b&amp;gt;Dove-on-feeder-two&amp;lt;/b&amp;gt;&quot; class=&quot;lightbox JSnocheck&quot; rel=&quot;lightbox[gal-632d05e845b41f674fa5757d9eed]&quot; data-url=&quot;/_media/birdbar/screenshots/dove-on-feeder-two.png?w=1600&amp;amp;h=813&amp;amp;tok=1b6dd7&quot;&gt;&lt;img width=&quot;300&quot; height=&quot;300&quot; src=&quot;/_media/birdbar/screenshots/dove-on-feeder-two.png?w=300&amp;amp;h=300&amp;amp;tok=d221da&quot; alt=&quot;dove-on-feeder-two.png&quot; loading=&quot;lazy&quot; /&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;gallery-image&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/_detail/birdbar/screenshots/dove-on-feeder.png?id=bird_bar&quot; title=&quot;Dove-on-feeder&quot; data-caption=&quot;&amp;lt;b&amp;gt;Dove-on-feeder&amp;lt;/b&amp;gt;&quot; class=&quot;lightbox JSnocheck&quot; rel=&quot;lightbox[gal-632d05e845b41f674fa5757d9eed]&quot; data-url=&quot;/_media/birdbar/screenshots/dove-on-feeder.png?w=1600&amp;amp;h=820&amp;amp;tok=3de434&quot;&gt;&lt;img width=&quot;300&quot; height=&quot;300&quot; src=&quot;/_media/birdbar/screenshots/dove-on-feeder.png?w=300&amp;amp;h=300&amp;amp;tok=b4b5e7&quot; alt=&quot;dove-on-feeder.png&quot; loading=&quot;lazy&quot; /&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;gallery-image&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/_detail/birdbar/screenshots/stats-display.png?id=bird_bar&quot; title=&quot;Stats-display&quot; data-caption=&quot;&amp;lt;b&amp;gt;Stats-display&amp;lt;/b&amp;gt;&quot; class=&quot;lightbox JSnocheck&quot; rel=&quot;lightbox[gal-632d05e845b41f674fa5757d9eed]&quot; data-url=&quot;/_media/birdbar/screenshots/stats-display.png?w=1600&amp;amp;h=904&amp;amp;tok=c017a8&quot;&gt;&lt;img width=&quot;300&quot; height=&quot;300&quot; src=&quot;/_media/birdbar/screenshots/stats-display.png?w=300&amp;amp;h=300&amp;amp;tok=5000d4&quot; alt=&quot;stats-display.png&quot; loading=&quot;lazy&quot; /&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;bird bar&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;bird_bar&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-444&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit2&quot; id=&quot;history&quot;&gt;History&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
At the start of 2021 I received a window-mount bird feeder as a secret santa gift. As a bird lover I was excited to put it up and get a close up view of some of the birds that inhabited the woods around where I lived. Within around 3 days I had birds showing up regularly.
&lt;/p&gt;

&lt;p&gt;
With the floor plan of my apartment at the time, the only sensible place to put the feeder was on the kitchen window; there was a screened porch on my bedroom window, or I would have put it there. Since my work desk was in my bedroom, this meant that I couldn&amp;#039;t watch it while I worked. If that had been an option, the rest of the project may never have materialized.
&lt;/p&gt;

&lt;p&gt;
Shortly after installing the feeder I had the idea to mount a camera pointing at it and stream it to Twitch, so that I could watch the birds while I was at my computer in another room. While watching I found myself wondering about a few of the species I saw and looking up pictures trying to identify them. Then it hit me - this is a textbook computer vision problem. I could build something that used realtime computer vision to identify birds as they appeared on camera.
&lt;/p&gt;

&lt;p&gt;
Fast forward a few years and this has bloomed into a pretty large project, with multiple upgrades to both the hardware, software and feeder setup. It&amp;#039;s definitely the most popular project I&amp;#039;ve made; my friends think it&amp;#039;s cool. It&amp;#039;s also served as a good test bed to keep up to date on advances in machine learning and accelerated computing.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;History&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;history&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;445-1924&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit3&quot; id=&quot;feeder&quot;&gt;Feeder&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
This section covers the evolution of the feeder construction &amp;amp; installation details.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Feeder&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;feeder&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;1925-2030&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit4&quot; id=&quot;v1&quot;&gt;v1&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
The first feeder was a generic acrylic bird feeder. The camera was an old webcam I had lying around. Since it&amp;#039;s mounted outside it needed to be weatherproofed. I did that with plastic wrap. Subsequent attempts greatly improved the design.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/birdbar/feeder/feeder-with-camera.jpg?id=bird_bar&quot; class=&quot;media&quot; title=&quot;birdbar:feeder:feeder-with-camera.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/birdbar/feeder/feeder-with-camera.jpg?h=300&amp;amp;tok=8cb42f&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;Bird feeder showing webcam pointed at it&quot; alt=&quot;Bird feeder showing webcam pointed at it&quot; height=&quot;300&quot; /&gt;&lt;/a&gt; &lt;a href=&quot;https://wiki.qlyoung.net/_detail/feeder-with-camera-outside.jpg?id=bird_bar&quot; class=&quot;media&quot; title=&quot;feeder-with-camera-outside.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/feeder-with-camera-outside.jpg?h=300&amp;amp;tok=8567de&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;Picture of webcam attached to the side of my apartment building&quot; alt=&quot;Picture of webcam attached to the side of my apartment building&quot; height=&quot;300&quot; /&gt;&lt;/a&gt; &lt;a href=&quot;https://wiki.qlyoung.net/_detail/webcam-condom.jpg?id=bird_bar&quot; class=&quot;media&quot; title=&quot;webcam-condom.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/webcam-condom.jpg?h=300&amp;amp;tok=3a51af&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;Picture of the webcam completely wrapped in plastic wrap secured by orange duct tape sitting on my window sill&quot; alt=&quot;Picture of the webcam completely wrapped in plastic wrap secured by orange duct tape sitting on my window sill&quot; height=&quot;300&quot; /&gt;&lt;/a&gt; &lt;a href=&quot;https://wiki.qlyoung.net/_detail/webcam-tupperware-lid.jpg?id=bird_bar&quot; class=&quot;media&quot; title=&quot;webcam-tupperware-lid.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/webcam-tupperware-lid.jpg?h=300&amp;amp;tok=621d1a&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;Picture of a tupperware lid taped over the camera as a sort of primitive rain shield&quot; alt=&quot;Picture of a tupperware lid taped over the camera as a sort of primitive rain shield&quot; height=&quot;300&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Additional weatherproofing measures included a plastic tupperware lid taped over the camera as a sort of primitive precipitation shield.
&lt;/p&gt;

&lt;p&gt;
Say what you will, but this setup survived a thunderstorm immediately followed by freezing temperatures and several hours of snow. All for $0.
&lt;/p&gt;

&lt;p&gt;
I should also mention this installation is located on the second floor of my apartment building. Putting it up and performing maintenance involves leaning out of the window, so I was anxious to build a durable, maintenance free installation.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;v1&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;v1&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:4,&amp;quot;range&amp;quot;:&amp;quot;2031-3267&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit5&quot; id=&quot;hardware&quot;&gt;Hardware&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
As of 2026 birdbar runs on an &lt;a href=&quot;https://www.nvidia.com/en-us/autonomous-machines/embedded-systems/jetson-orin/nano-super-developer-kit/&quot; class=&quot;urlextern&quot; title=&quot;https://www.nvidia.com/en-us/autonomous-machines/embedded-systems/jetson-orin/nano-super-developer-kit/&quot; rel=&quot;ugc nofollow&quot;&gt;Nvidia Jetson Orin Nano&lt;/a&gt;. With a TensorRT-optimized kernel and recent firmware upgrades to the board to unlock additional performance, classification takes about 50 ms/frame, achieving around 20 frames per second. Classification runs at 480&amp;times;800 resolution.
&lt;/p&gt;

&lt;p&gt;
In the past (2021 thru early 2026) the setup was an Intel NUC with a discrete PNY GTX 1070 connected via Thunderbolt 3 in an eGPU enclosure. It&amp;#039;s wild that what used to take a dedicated computer and discrete graphics card has now shrunk to fit entirely on a SODIMM SoM package!
&lt;/p&gt;

&lt;p&gt;
Webcam is a Logitech Streamcam.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Hardware&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;hardware&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:5,&amp;quot;range&amp;quot;:&amp;quot;3268-3996&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit6&quot; id=&quot;bird_identification&quot;&gt;Bird Identification&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Birds arriving at the feeder are identified using &lt;a href=&quot;https://github.com/ultralytics/yolov5&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/ultralytics/yolov5&quot; rel=&quot;ugc nofollow&quot;&gt;YOLOv5&lt;/a&gt; fine tuned on &lt;a href=&quot;https://dl.allaboutbirds.org/nabirds&quot; class=&quot;urlextern&quot; title=&quot;https://dl.allaboutbirds.org/nabirds&quot; rel=&quot;ugc nofollow&quot;&gt;NABirds&lt;/a&gt;.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Bird Identification&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;bird_identification&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:6,&amp;quot;range&amp;quot;:&amp;quot;3997-4192&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit7&quot; id=&quot;background&quot;&gt;Background&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
I’d read about &lt;a href=&quot;https://pjreddie.com/darknet/yolo/&quot; class=&quot;urlextern&quot; title=&quot;https://pjreddie.com/darknet/yolo/&quot; rel=&quot;ugc nofollow&quot;&gt;YOLO&lt;/a&gt; some years before and began to reacquaint myself. It’s come quite far and seems to be more or less the state of the art for realtime computer vision object detection and classification. I downloaded the latest version (&lt;a href=&quot;https://github.com/ultralytics/yolov5&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/ultralytics/yolov5&quot; rel=&quot;ugc nofollow&quot;&gt;YOLOv5&lt;/a&gt; at time of writing) and ran the webcam demo. It ran well over 30fps with good accuracy on my RTX3080, correctly picking out myself as “person”, my phone as “cell phone”, and my light switch as “clock”.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/me-with-phone-yolo-detection.png?id=bird_bar&quot; class=&quot;media&quot; title=&quot;me-with-phone-yolo-detection.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/me-with-phone-yolo-detection.png?w=400&amp;amp;tok=06edc3&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;Screen capture of webcam feed after applying YOLOv5&amp;#039;s out-of-box &amp;#039;small&amp;#039; model to a scene of me holding up my cell phone. Picture is heavily blurred&quot; alt=&quot;Screen capture of webcam feed after applying YOLOv5&amp;#039;s out-of-box &amp;#039;small&amp;#039; model to a scene of me holding up my cell phone. Picture is heavily blurred&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Out of the box YOLOv5 is trained on COCO, which is a dataset of _co_mmon objects in _co_ntext. This dataset is able to identify a picture of a Carolina chickadee as “bird”. Tufted titmice are also identified as “bird”. All birds are “bird” to COCO (at least the ones I tried).
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/chickadee-2.jpg?id=bird_bar&quot; class=&quot;media&quot; title=&quot;chickadee-2.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/chickadee-2.jpg?w=400&amp;amp;tok=e541c8&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;Image of chickadee with a poorly sized bounding box drawn around it with the label &amp;quot;bird&amp;quot; and a confidence rating of 0.31&quot; alt=&quot;Image of chickadee with a poorly sized bounding box drawn around it with the label &amp;quot;bird&amp;quot; and a confidence rating of 0.31&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Pretty good, but not exactly what I was going for. YOLO needed to be trained to recognize specific bird species.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Background&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;background&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:7,&amp;quot;range&amp;quot;:&amp;quot;4193-5504&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit8&quot; id=&quot;dataset&quot;&gt;Dataset&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
A quick Google search for “north american birds dataset” yielded probably the most convenient dataset I could possibly have asked for. Behold, &lt;a href=&quot;https://dl.allaboutbirds.org/nabirds&quot; class=&quot;urlextern&quot; title=&quot;https://dl.allaboutbirds.org/nabirds&quot; rel=&quot;ugc nofollow&quot;&gt;NABirds&lt;/a&gt;!
&lt;/p&gt;
&lt;blockquote&gt;&lt;div class=&quot;no&quot;&gt;
&lt;blockquote&gt;&lt;div class=&quot;no&quot;&gt;
 NABirds V1 is a collection of 48,000 annotated photographs of the 400 species of birds that are commonly observed in North America. More than 100 photographs are available for each species, including separate annotations for males, females and juveniles that comprise 700 visual categories. This dataset is to be used for fine-grained visual categorization experiments.&lt;/div&gt;&lt;/blockquote&gt;
&lt;/div&gt;&lt;/blockquote&gt;

&lt;p&gt;
Thank you Cornell! Without this dataset, this project probably would not have been possible.
&lt;/p&gt;

&lt;p&gt;
The dataset is well organized. There’s a directory tree containing the images, and a set of text files mapping the file IDs to various metadata. For example, one text file maps the file ID to the bounding box coordinates and dimensions, another maps ID to class, and so on.
&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;
&lt;em&gt;Did I mention this dataset also contains bounding box information for individual parts of the birds? Yes, each of the over 48,000 bird pictures is subdivided into bill, crown, nape, left eye, right eye, belly, breast, back, tail, left wing, right wing components. It’s like the holy grail for bird × computer vision projects. For this project I did not need the parts boxes, but it’s awesome that Cornell has made such a comprehensive dataset available to the public.&lt;/em&gt;
&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;
While well organized, I’m pretty sure this is a non-standard dataset format. YOLOv5 requires data to be organized in its own dataset format, which is fortunately quite simple. To make matters easier, NABirds comes with a Python module that provides functions for loading data from the various text files. Converting NABirds into a YOLOv5 compatible format was fairly straightforward with some additional code. Cornell’s terms of use require that the dataset not be redistributed, so I cannot provide the converted dataset, but I can provide the code I used to process it.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Dataset&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;dataset&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:8,&amp;quot;range&amp;quot;:&amp;quot;5505-7531&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit9&quot; id=&quot;training&quot;&gt;Training&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
With the dataset correctly formatted, the next step was to train YOLOv5 on it. This can be somewhat difficult depending on one’s patience and access to hardware resources. Training machine learning models involves lots of operations that are highly amenable to acceleration by GPUs, and to train them in a reasonable timeframe, a GPU or tensor processor is required. I have an RTX3080 at my disposal, so it was easy to get started on my personal desktop.
&lt;/p&gt;

&lt;p&gt;
YOLOv5 offers multiple network sizes, from n to x (n for nano, x for x). n, s and m sizes are recommended for mobile or workstation deployments, while l and x variants are geared towards cloud / datacenter deployments (i.e. expensive datacenter GPUs / tensor processors). The larger variants take longer to train and longer to evaluate. Since the model needed to be evaluated on each frame in a video feed, holding all else constant, for this project the choice of model size would ultimately dictate the achievable framerate.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/yolo-model-comparison-graph.png?id=bird_bar&quot; class=&quot;media&quot; title=&quot;yolo-model-comparison-graph.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/yolo-model-comparison-graph.png?w=400&amp;amp;tok=8c84bc&quot; class=&quot;mediaright&quot; align=&quot;right&quot; loading=&quot;lazy&quot; title=&quot;Graph showing evaluation time versus average precision against the COCO dataset&quot; alt=&quot;Graph showing evaluation time versus average precision against the COCO dataset&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Since the webcam demo with the s model ran at a good framerate on my GPU I chose that one to start.
&lt;/p&gt;

&lt;p&gt;
Training certain kinds of machine learning models is memory intensive. In the case of YOLO, which uses a convolutional neural network (CNN), less GPU memory means fewer images can be processed in a single gradient descent step (the ‘batch size’), significantly increasing the time needed to train. It’s worth noting that training with a smaller batch size shouldn’t affect the final outcome too much, so patience can compensate for lack of GPU memory if necessary. In my case the largest batch size I could get on my GPU was 8.
&lt;/p&gt;

&lt;p&gt;
Training YOLOv5s on my RTX3080 took about 23 hours to train 100 epochs with a batch size of 8. Each epoch represents one complete processing of the training dataset. General advice for YOLO is to use about 300 epochs. Since training typically involves a trial-and-error process of tweaking parameters and retraining, and I wanted to try using the m model, clearly the 3080 was not going to be sufficient to get this project done in the desired timeframe. This was a holiday project for me, I wanted it done before the end of the holidays.
&lt;/p&gt;

&lt;p&gt;
I knew people use cloud compute services to train these things, so it was time to find some cloud resources with ML acceleration hardware. To that end I logged into Google Compute. Some hours later I logged into AWS. Some hours later I logged into Linode. At Linode, I had an RTX6000 enabled instance within about 10 minutes. To this day, I still cannot get GPU instances on GCP or AWS - and not for lack of trying. GCP seemingly had no GPUs available in any region I tried, and I tried several, each with advertised GPU availability. AWS required me to open a support case to get my quota for P class EC2 instances increased, which was denied after three days. I find it amusing that Linode is able to provide an infinitely better experience than Google and Amazon. Speculating, it seems plausible that perhaps AWS and GCP are being flooded by cryptocurrency workloads and Linode is not, or is more strict about banning miners. I have no insight into that. Overall, it seems cloud GPU compute is more difficult to get than I had imagined.
&lt;/p&gt;

&lt;p&gt;
The RTX6000 allowed me to use a batch size of 32, which brought training within a reasonable timeframe of about 2 days for the m model at 300 epochs. Good thing, because at $1.50 an hour, those VMs aren’t cheap. A few days of training was about as much as I was willing to spend on this project, and the graphs indicated that the classification loss was on the order of 3% - more than sufficient for a fun project.
&lt;/p&gt;

&lt;p&gt;
Note: Machine learning models are typically trained on some subset of the total available data, with the rest being used to evaluate the model’s performance on data it has not seen before. One of the metadata files provided with the dataset defines a suggested train/test split, classifying each image by its suggested usage. I tried training with the suggested split, and results were significantly worse than using a standard 80/10/10 (train/test/validate) split.
&lt;/p&gt;

&lt;p&gt;
For the data oriented, here is the summary information for the training run of the model I ended up using:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/final-training-results.png?id=bird_bar&quot; class=&quot;media&quot; title=&quot;final-training-results.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/final-training-results.png?h=400&amp;amp;tok=3c9572&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;Composite image with various graphs depicting various training metrics, including recall, precision, loss, and mean average precision on both training and validation sets&quot; alt=&quot;Composite image with various graphs depicting various training metrics, including recall, precision, loss, and mean average precision on both training and validation sets&quot; height=&quot;400&quot; /&gt;&lt;/a&gt; &lt;a href=&quot;https://wiki.qlyoung.net/_detail/pr-curve.png?id=bird_bar&quot; class=&quot;media&quot; title=&quot;pr-curve.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/pr-curve.png?h=400&amp;amp;tok=c4fd41&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;PR curve&quot; alt=&quot;PR curve&quot; height=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
These metrics are all good and show that the model trained very nicely on the dataset.
&lt;/p&gt;

&lt;p&gt;
At this point after waiting for training to finish I was quite excited and ready to try it out. I ran it on a couple of clips from my bird stream and was amazed at the results.
&lt;/p&gt;

&lt;p&gt;
Trying it on an image with three species of chickadee, that to my eye look almost identical:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/three-chickadee.png?id=bird_bar&quot; class=&quot;media&quot; title=&quot;three-chickadee.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/three-chickadee.png?w=400&amp;amp;tok=5b3531&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;Composite picture of carolina, mountain and black-capped chickadees&quot; alt=&quot;Composite picture of carolina, mountain and black-capped chickadees&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
I’m not sure if these were in the training set; I just searched for the first images of each species I found on Google Images.
&lt;/p&gt;

&lt;p&gt;
The accuracy is impressive. YOLOv5 has certainly achieved its goal of making state of the art computer vision accessible to people outside that field.
&lt;/p&gt;

&lt;p&gt;
If you’d like to use my models, they’re available &lt;a href=&quot;https://github.com/qlyoung/birdcam/tree/master/models&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/qlyoung/birdcam/tree/master/models&quot; rel=&quot;ugc nofollow&quot;&gt;here&lt;/a&gt;.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Training&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;training&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:9,&amp;quot;range&amp;quot;:&amp;quot;7532-12978&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit10&quot; id=&quot;video_flow&quot;&gt;Video flow&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Having demonstrated that it could identify birds with relative accuracy, it was time to get it working on a live video feed.
&lt;/p&gt;

&lt;p&gt;
Even though YOLO is amazingly fast relative to other methods, it still needs a GPU in order perform inference fast enough to produce inference results for each frame of a video feed. I set a goal of 30fps; on my 3080, my final model averages roughly 0.020s per frame, sufficient to pull around 40-50fps. This is a good tradeoff between model size/accuracy and evaluation speed. The average with the s model looks to be roughly 0.015s, technically sufficient to pull 60fps, but without much headroom. There are spikes up to the 0.02 range, suggesting that 60fps would likely be jittery. The webcam I was using at the time didn’t really suffice for 60fps anyway.
&lt;/p&gt;

&lt;p&gt;
So while I had a GPU that could perform inference fast enough, it was in the computer in my bedroom and I didn’t much feel like moving it into the kitchen. I considered several approaches to this problem, including:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; A very long USB extender to the webcam&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
I actually tried this, and it did work. The problem was that it required me to have my bedroom window open a little bit to route the cable through it, which isn’t ideal for several reasons. Also, when using YOLO&amp;#039;s video detection script on a webcam device it produces annotated frames of size 640&amp;times;640. I wanted to stream the full resolution of the camera (1920&amp;times;1080). I briefly investigated patching detect.py, but the holidays were drawing to a close and I wanted a functioning stream sooner rather than later, so for the time being I resolved to use a video feed since the script produced annotated at the native resolution when given a video.
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; eGPU enclosure attached to the webcam host&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
By this point in the project I’d replaced the laptop with a NUC I happened to have lying around. If this was a recent NUC with Thunderbolt 3 support, an eGPU enclosure would have been the cleanest and easiest solution. I wanted to avoid buying a new NUC and eGPU enclosure since together those cost quite a bit, and I didn&amp;#039;t care to wait for delivery so I nixed this option.
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Some kind of network streaming setup&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Advantages: no cables through windows, no new hardware. I have a pretty good home network and am pretty handy with this stuff so I went with that. After several hours experimenting with RTMP servers, HTTP streaming tools, and the like, I ended up with this setup:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/video-routing.png?id=bird_bar&quot; class=&quot;media&quot; title=&quot;video-routing.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/video-routing.png?w=400&amp;amp;tok=3032c9&quot; class=&quot;mediaright&quot; align=&quot;right&quot; loading=&quot;lazy&quot; title=&quot;Video routing setup; shows video feed streaming from webcam host in the kitchen, to my desktop where inference is performed, then to Twitch&quot; alt=&quot;Video routing setup; shows video feed streaming from webcam host in the kitchen, to my desktop where inference is performed, then to Twitch&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
I tried a bunch of other things, including streaming RTMP to a local NGINX server, using VLC as an RTSP source on the webcam box, etc, but this was the setup that was the most stable, had the highest framerate, and lowest artifacts. Actually detect.py does support consuming RTSP feeds directly, but whatever implementation OpenCV uses under the hood introduces some significant artifacts into the output. Using VLC to consume the RTSP feed and rebroadcast it locally as an HTTP stream turned out better. The downside to this is that VLC seems to crash from time to time, but a quick batch script fixed that right up:
&lt;/p&gt;
&lt;pre class=&quot;code powershell&quot;&gt;:start
&amp;nbsp;
taskkill &lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;f &lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;im &lt;span class=&quot;st0&quot;&gt;&amp;quot;vlc.exe&amp;quot;&lt;/span&gt;
&amp;nbsp;
start &lt;span class=&quot;st0&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;C:\Program Files (x86)\VideoLAN\VLC\vlc.exe&amp;quot;&lt;/span&gt; rtsp:&lt;span class=&quot;sy0&quot;&gt;//&lt;/span&gt;192.168.0.240:&lt;span class=&quot;nu0&quot;&gt;554&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;live &lt;span class=&quot;sy0&quot;&gt;--&lt;/span&gt;sout &lt;span class=&quot;co1&quot;&gt;#transcode{vcodec=h264,vb=800,acodec=mpga,ab=128,channels=2,samplerate=44100,scodec=none}:http{mux=ts,dst=:8081/localql} --no-sout-all --sout-keep&lt;/span&gt;
timeout &lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;t &lt;span class=&quot;nu0&quot;&gt;15&lt;/span&gt;
&amp;nbsp;
python detect.py &lt;span class=&quot;sy0&quot;&gt;--&lt;/span&gt;nosave &lt;span class=&quot;sy0&quot;&gt;--&lt;/span&gt;weights nabirds_v5m_b32_e300_stdsplit.pt &lt;span class=&quot;sy0&quot;&gt;--&lt;/span&gt;source http:&lt;span class=&quot;sy0&quot;&gt;//&lt;/span&gt;127.0.0.1:&lt;span class=&quot;nu0&quot;&gt;8081&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;localql
&amp;nbsp;
goto start&lt;/pre&gt;

&lt;p&gt;
Why yes, I do have Windows experience :’)
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/setup.jpg?id=bird_bar&quot; class=&quot;media&quot; title=&quot;setup.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/setup.jpg?w=400&amp;amp;tok=7b5b75&quot; class=&quot;mediaright&quot; align=&quot;right&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
I ran it that way for a couple months or so, but eventually the above setup proved too unreliable. It required running lots of software on both the camera host as well as my desktop, and since it used my desktop GPU for inference it limited what I could use my computer for (read: no gaming). Also, the stream went down every time I rebooted my computer.
&lt;/p&gt;

&lt;p&gt;
After deciding that I wanted to maintain this as a long term installation I ponied up for a NUC and an eGPU enclosure. I initially tried to use the enclosure with an RTX 3070, but I couldn’t get it working with that card so I used a spare 1070 instead which worked flawlessly. The 1070 runs at about 25fps when inferencing with my bird model which is more than enough to look snappy overlaid on a video feed. The whole thing sits on my kitchen floor and is relatively unobtrusive.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Video flow&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;video_flow&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:10,&amp;quot;range&amp;quot;:&amp;quot;12979-17568&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit11&quot; id=&quot;fps&quot;&gt;60fps&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Up to this point I was streaming the window with annotated frames displayed by YOLO’s detect.py convenience script. However, this window updates only as often as an inferencing run completes, so around 25fps. It doesn&amp;#039;t look good on a livestream. It would be better to stream video straight from the camera at native framerates (ideally 60fps) and overlay the labels on top of it.
&lt;/p&gt;

&lt;p&gt;
Doing this turned out to be rather difficult because you cannot multiplex camera devices on Windows; only one program can have a handle on the camera and its video feed to the exclusion of all others. Fortunately there is some &lt;a href=&quot;https://very-soft.com/product/webcamsplitter&quot; class=&quot;urlextern&quot; title=&quot;https://very-soft.com/product/webcamsplitter&quot; rel=&quot;ugc nofollow&quot;&gt;software&lt;/a&gt; which works around this. I purchased that software and used it to create two virtual camera feeds. OBS directly consumes one feed and the other one goes into YOLO for inferencing. The resulting labeled frames are displayed in a live preview window. I patched YOLO so that the preview window, which normally displays the source frame annotated with the inferencing results, only displayed the annotations on a black background without the source frame. That window is used as a layer in OBS with a luma filter applied to make the black parts transparent. With some additional tweaks to get the canvas sizing and aspect ratio correct this allowed me to composite the 25fps inferencing results on top of the high quality 60fps video coming from the camera.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/nuc-perf.jpg?id=bird_bar&quot; class=&quot;media&quot; title=&quot;nuc-perf.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/nuc-perf.jpg?w=400&amp;amp;tok=d1b2f2&quot; class=&quot;mediaright&quot; align=&quot;right&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
For encoding I use Nvenc on the 1070. That keeps the stream at a solid 60fps, which the NUC CPU can’t accomplish. Between inferencing and video encode the card is getting put to great use.
&lt;/p&gt;

&lt;p&gt;
This was stable for over a year, until I decided to install Windows 11. What could go wrong?
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;60fps&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;fps&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:1,&amp;quot;secid&amp;quot;:11,&amp;quot;range&amp;quot;:&amp;quot;17569-19320&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit12&quot; id=&quot;camera&quot;&gt;Camera&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
The original setup used an off-brand 720p webcam wrapped in a righteous amount of plastic wrap for weatherproofing. Surprisingly the weatherproofing worked well and there was never a major failure while using the first camera. However, the quality and color on that camera wasn’t good and an upgrade was due. I already had a Logitech Brio 4k webcam intended for remote work, but it ended up largely unused so it was repurposed for birdwatching.
&lt;/p&gt;

&lt;p&gt;
While the plastic wrap method never had any major failures it wasn’t ideal either. Heavy humidity created fogging inside the plastic that could take a few hours to wear off. It needed replacing anytime the camera was adjusted. Due to these problems and the higher cost of the Brio I decided to build a weatherproof enclosure.
&lt;/p&gt;

&lt;p&gt;
The feeder is constructed of acrylic. My initial plan was to use acrylic sheeting build out an extension to the feeder big enough to house the camera. I picked up some acrylic sheeting from Amazon and began researching appropriate adhesives. It turns out most adhesives don’t work very well on acrylic, at least not for my use case – the load bearing joints between the sheets were thin and I needed the construction to be rigid enough to support its own weight and the weight of the camera without sagging. Since the enclosure would be suspended over air relying on its inherent rigidity for structure the adhesive needed to be strong.
&lt;/p&gt;

&lt;p&gt;
The best way to adhere acrylic to itself is using acrylic cement. Acrylic cement dissolves the surfaces of the two pieces to be bonded, allowing them to mingle, and then evaporates away. This effectively fuses the two pieces together with a fairly strong bond (though not as strong as if the piece had been manufactured that way).
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/acrylic-cement.jpg?id=bird_bar&quot; class=&quot;media&quot; title=&quot;acrylic-cement.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/acrylic-cement.jpg?w=400&amp;amp;tok=baed2a&quot; class=&quot;medialeft&quot; align=&quot;left&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/assembled-box.jpg?id=bird_bar&quot; class=&quot;media&quot; title=&quot;assembled-box.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/assembled-box.jpg?w=400&amp;amp;tok=60c79e&quot; class=&quot;medialeft&quot; align=&quot;left&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Three sides were opaque to prevent sunlight reflections within the box. Joints were caulked and taped the joints to increase weather resistance. I played around with using magnets to secure the enclosure to the main feeder body but didn’t come up with anything I liked, so I glued it to the feeder with more acrylic cement, threw my camera in there and called it a day.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/full-setup-with-first-weather-shield.jpg?id=bird_bar&quot; class=&quot;media&quot; title=&quot;full-setup-with-first-weather-shield.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/full-setup-with-first-weather-shield.jpg?w=600&amp;amp;tok=ba6feb&quot; class=&quot;mediacenter&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;600&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
This weatherproofing solution turned out great. It successfully protected the camera from all inclement weather until I retired that feeder, surviving rain, snow, and high winds over the course of the year.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Camera&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;camera&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:1,&amp;quot;secid&amp;quot;:12,&amp;quot;range&amp;quot;:&amp;quot;19321-21785&amp;quot;} --&gt;
&lt;h1 class=&quot;sectionedit13&quot; id=&quot;switching_to_linux&quot;&gt;Switching to Linux&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
As it turns out, the webcam splitter software appears to rely on some undocumented / unofficial Windows 10 APIs and does not work on Windows 11. I decided to bite the bullet and just put Linux on the NUC.
&lt;/p&gt;

&lt;p&gt;
Similar to Windows, on Linux only one device can be reading from a camera device file at a time. Unlike Windows there is a very easy way to work around this called &lt;a href=&quot;https://wiki.archlinux.org/title/V4l2loopback&quot; class=&quot;urlextern&quot; title=&quot;https://wiki.archlinux.org/title/V4l2loopback&quot; rel=&quot;ugc nofollow&quot;&gt;v4l2loopback&lt;/a&gt;. This is a kernel module that allows you to create virtual video device files that can be fed video from a userspace application such as gstreamer. These virtual device files support multiple clients.
&lt;/p&gt;

&lt;p&gt;
tl;dr:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;co0&quot;&gt;# load v4l2loopback module; this creates a few loopbacks&lt;/span&gt;
&lt;span class=&quot;kw2&quot;&gt;sudo&lt;/span&gt; modprobe v4l2loopback
&lt;span class=&quot;co0&quot;&gt;# set desired parameters on loopback device /dev/video2&lt;/span&gt;
&lt;span class=&quot;kw2&quot;&gt;sudo&lt;/span&gt; v4l2loopback-ctl set-fps &lt;span class=&quot;nu0&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;dev&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;video2
&lt;span class=&quot;co0&quot;&gt;# pipe video from camera device /dev/video0 to loopback&lt;/span&gt;
gst-launch-&lt;span class=&quot;nu0&quot;&gt;1.0&lt;/span&gt; v4l2src &lt;span class=&quot;re2&quot;&gt;device&lt;/span&gt;=&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;dev&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;video0 &lt;span class=&quot;sy0&quot;&gt;!&lt;/span&gt; video&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;x-raw,&lt;span class=&quot;re2&quot;&gt;width&lt;/span&gt;=&lt;span class=&quot;nu0&quot;&gt;1920&lt;/span&gt;,&lt;span class=&quot;re2&quot;&gt;height&lt;/span&gt;=&lt;span class=&quot;nu0&quot;&gt;1080&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;!&lt;/span&gt; videoconvert &lt;span class=&quot;sy0&quot;&gt;!&lt;/span&gt; v4l2sink &lt;span class=&quot;re2&quot;&gt;device&lt;/span&gt;=&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;dev&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;video2&lt;/pre&gt;

&lt;p&gt;
After this, any number of clients can read from /dev/video2. I replicated my video + annotations compositing setup in OBS as described earlier without issue.
&lt;/p&gt;

&lt;p&gt;
On the off chance this helps someone, here&amp;#039;s how you set video camera parameters on device 0 (/dev/video0) from the command line:
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;sudo v4l2-ctl -d 0 -c focus_automatic_continuous=0
sudo v4l2-ctl -d 0 -c focus_absolute=75
sudo v4l2-ctl -d 0 -c backlight_compensation=0
sudo v4l2-ctl -d 0 -c auto_exposure=3&lt;/pre&gt;

&lt;p&gt;
Around this time I also replaced the feeder since the last one was all scratched up and the acrylic had gotten all cloudy. I also replaced the cheap camera with a Logitech Streamcam duct taped to the feeder. I found that the Brio I was using didn&amp;#039;t correctly handle gain adjustment so bright sunlight would blow it out. The Streamcam, which is much more affordable, doesn&amp;#039;t have that issue.
&lt;/p&gt;

&lt;p&gt;
You can watch the &lt;a href=&quot;https://twitch.tv/thebirdbar&quot; class=&quot;urlextern&quot; title=&quot;https://twitch.tv/thebirdbar&quot; rel=&quot;ugc nofollow&quot;&gt;livestream here&lt;/a&gt;.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Switching to Linux&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;switching_to_linux&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:1,&amp;quot;secid&amp;quot;:13,&amp;quot;range&amp;quot;:&amp;quot;21786-23797&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit14&quot; id=&quot;birds&quot;&gt;Birds&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
These were the first birds to appear and be identified live on camera. Congratulations.
&lt;/p&gt;

&lt;p&gt;
&lt;video class=&quot;mediacenter&quot; width=&quot;320&quot; height=&quot;240&quot; controls=&quot;controls&quot;&gt;
&lt;source src=&quot;/_media/success.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_media/success.mp4&quot; class=&quot;media mediafile mf_mp4&quot; title=&quot;success.mp4 (2.2 MB)&quot;&gt;success.mp4&lt;/a&gt;&lt;/video&gt;

&lt;/p&gt;

&lt;p&gt;
In the case of sexually dimorphic species that also have appropriate training examples, such as house finches, it’s even capable of distinguishing the sex.
&lt;/p&gt;

&lt;p&gt;
&lt;video class=&quot;mediacenter&quot; width=&quot;320&quot; height=&quot;240&quot; controls=&quot;controls&quot;&gt;
&lt;source src=&quot;/_media/birdbar/screenshots/finches.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_media/birdbar/screenshots/finches.mp4&quot; class=&quot;media mediafile mf_mp4&quot; title=&quot;birdbar:screenshots:finches.mp4 (4.1 MB)&quot;&gt;finches.mp4&lt;/a&gt;&lt;/video&gt;

&lt;/p&gt;

&lt;p&gt;
In a few cases, such as the nuthatch and the pine warbler, the model taught me something I did not know before. Reflecting on that, I think that makes this one of my favorite projects. Building a system that teaches you new things is cool.
&lt;/p&gt;

&lt;p&gt;
&lt;video class=&quot;mediacenter&quot; width=&quot;320&quot; height=&quot;240&quot; controls=&quot;controls&quot;&gt;
&lt;source src=&quot;/_media/pine-warbler.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_media/pine-warbler.mp4&quot; class=&quot;media mediafile mf_mp4&quot; title=&quot;pine-warbler.mp4 (4.8 MB)&quot;&gt;pine-warbler.mp4&lt;/a&gt;&lt;/video&gt;

&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Birds&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;birds&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:3,&amp;quot;secid&amp;quot;:14,&amp;quot;range&amp;quot;:&amp;quot;23798-24392&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit15&quot; id=&quot;statistics&quot;&gt;Statistics&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
I was curious how many birds were visiting per day, what peak hours were and what the species distribution looked like. In order to get this information the first step was data collection, so I thought about how to best collect data on visitors. Conceptually I wanted to log each visitor along with some relevant data such as species, time of visit, length of visit etc. as a single record – one per visitor. However, this is one of those problems that seems easy but is actually quite hard. The classification algorithm runs once per video frame but there is no context retained between frames; it can’t tell you that the bird detected in frame N_1 is the same one it detected a moment ago in frame N_0. Extracting this sort of context is probably feasible, perhaps by combining some heuristics with positional information (is the bird of species X in this frame in the same position as bird of species X in the previous frame? If so, probably the same bird). However, this fell in the realm of “too hard for this project.” Instead of storing bird visits I decided to store the data in its elementary format as a time series of detection events.
&lt;/p&gt;

&lt;p&gt;
I have some familiarity with time series databases, having used InfluxDB for a &lt;a href=&quot;https://github.com/qlyoung/lagopus&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/qlyoung/lagopus&quot; rel=&quot;ugc nofollow&quot;&gt;different project&lt;/a&gt;. Influx was nice for that project but I wanted to learn something new so I chose &lt;a href=&quot;https://www.timescale.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.timescale.com/&quot; rel=&quot;ugc nofollow&quot;&gt;TimescaleDB&lt;/a&gt; this time around. Timescale is an extension to Postgres and I wanted to learn more about Postgres anyway so it was a good choice from a learning perspective. Since the plan was to insert a new record for every frame where an object was detected and the detection loop runs at about 25fps I was expecting a lot of data. I had read that Timescale handles that sort of scale a bit better than Influx, so that was another factor in choosing Timescale.
&lt;/p&gt;

&lt;p&gt;
After setting up Timescale on a VPS it was time to design the database schema. I had a vague understanding of how relational databases worked and had written a few lines of SQL, but otherwise lacked database experience. After writing out a naive (in retrospect) schema it occurred to me that there was probably a way to do it right. I began learning about schema design and database normalization, a topic I found surprisingly interesting. This is the schema I ended up with, and while I don&amp;#039;t think it&amp;#039;s perfectly normalized it’s better than what I had initially.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/whiteboard.jpg?id=bird_bar&quot; class=&quot;media&quot; title=&quot;whiteboard.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/whiteboard.jpg?w=400&amp;amp;tok=638937&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;whiteboard drawing of various database table schemas&quot; alt=&quot;whiteboard drawing of various database table schemas&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Note the data for the feed present in the feeder. I thought it would be cool to correlate feed with frequency of species, but since I set things up so that changing the feed required a manual record insert I got lazy and stopped doing it so I don&amp;#039;t have the dataset needed now.
&lt;/p&gt;

&lt;p&gt;
After setting up the database on the server, I made some hacks in the YOLOv5 code to push detection data directly to the database whenever a detection occurred. After a short time I had tons of visitor records in my database. Next I wanted to answer questions like “what is the most common species over the last week?” and “what time of day has peak volume?“ To accomplish this I set up a &lt;a href=&quot;https://birdcam.qlyoung.net&quot; class=&quot;urlextern&quot; title=&quot;https://birdcam.qlyoung.net&quot; rel=&quot;ugc nofollow&quot;&gt;grafana dashboard&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
Then I thought it would be cool to show these graphs on the livestream. It turns out Grafana supports embedding individual graphs, and since OBS supports rendering browser views it was easy to get those set up.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/birdbar/screenshots/stats-display.png?id=bird_bar&quot; class=&quot;media&quot; title=&quot;birdbar:screenshots:stats-display.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/birdbar/screenshots/stats-display.png?w=400&amp;amp;tok=1ad1dc&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;screenshot from the stream with a bar graph showing species counts&quot; alt=&quot;screenshot from the stream with a bar graph showing species counts&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
I left these up for a while, but ultimately I felt they were taking up too much space in the stream so I took them down.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Statistics&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;statistics&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:3,&amp;quot;secid&amp;quot;:15,&amp;quot;range&amp;quot;:&amp;quot;24393-28101&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit16&quot; id=&quot;thoughts&quot;&gt;Thoughts&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Overall I was extremely satisfied with the results of this project. The initial project took about 5 days to complete. I learned something at each step.
&lt;/p&gt;

&lt;p&gt;
I can think of a lot of things to do with this project, in no particular order:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Bot that posts somewhere when a bird is seen&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Retrain with background images to reduce false positives&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/birdbar/screenshots/bluebird-and-chickadee.png?id=bird_bar&quot; class=&quot;media&quot; title=&quot;birdbar:screenshots:bluebird-and-chickadee.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/birdbar/screenshots/bluebird-and-chickadee.png?w=400&amp;amp;tok=7b048c&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;Eastern bluebird and Carolina chickadee feed together&quot; alt=&quot;Eastern bluebird and Carolina chickadee feed together&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/from_blog?do=showtag&amp;amp;tag=from_blog&quot; class=&quot;wikilink1&quot; title=&quot;tag:from_blog&quot; rel=&quot;tag&quot;&gt;from blog&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/technology?do=showtag&amp;amp;tag=technology&quot; class=&quot;wikilink1&quot; title=&quot;tag:technology&quot; rel=&quot;tag&quot;&gt;technology&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/machine_learning?do=showtag&amp;amp;tag=machine_learning&quot; class=&quot;wikilink1&quot; title=&quot;tag:machine_learning&quot; rel=&quot;tag&quot;&gt;machine learning&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/nature?do=showtag&amp;amp;tag=nature&quot; class=&quot;wikilink1&quot; title=&quot;tag:nature&quot; rel=&quot;tag&quot;&gt;nature&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Thoughts&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;thoughts&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:3,&amp;quot;secid&amp;quot;:16,&amp;quot;range&amp;quot;:&amp;quot;28102-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/blog">
        <dc:format>text/html</dc:format>
        <dc:date>2023-09-09T01:27:10+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>blog</title>
        <link>https://wiki.qlyoung.net/blog</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;why_wiki&quot;&gt;why wiki?&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
I used to do a blog. In September 2023, I realized that the blog format was preventing me from writing more, because I felt like every blog post had to be finished before publication. I also kept wanting to go back and update old posts because things were changing, but blog posts are dated and aren&amp;#039;t meant to be living documents like that. Another problem is that without a page history, if you change something then readers can&amp;#039;t get back to the old version without resorting to internet archive or finding the git repo for the site. Wikis have page histories, and every page is a living document, so wiki is better for me.
&lt;/p&gt;

&lt;p&gt;
tl;dr go read &lt;a href=&quot;https://shii.bibanon.org/shii.org/knows/Everything_Shii_Knows_About.html#Purpose&quot; class=&quot;urlextern&quot; title=&quot;https://shii.bibanon.org/shii.org/knows/Everything_Shii_Knows_About.html#Purpose&quot; rel=&quot;ugc nofollow&quot;&gt;this&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
All my blog post URLs are still alive, since I don&amp;#039;t want that content to be lost, and I don&amp;#039;t know who has linked to them. But probably I foresee no more blog posts.
&lt;/p&gt;

&lt;p&gt;
If you got here from a link in one of my blog posts, and are looking for the corresponding wiki page, here is a list of pages which were spawned from blog posts:
&lt;/p&gt;
&lt;div class=&quot;table&quot;&gt;&lt;table class=&quot;ul plgn__pglist&quot;&gt;&lt;tr&gt;&lt;td class=&quot;page&quot;&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://wiki.qlyoung.net/adaptive_cruise&quot; class=&quot;wikilink1&quot; title=&quot;adaptive_cruise&quot;  data-wiki-id=&quot;adaptive_cruise&quot;&gt;adaptive cruise&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;td class=&quot;date&quot;&gt;2023/09/08 23:51&lt;/td&gt;&lt;td class=&quot;user&quot;&gt;qlyoung&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;page&quot;&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://wiki.qlyoung.net/bird_bar&quot; class=&quot;wikilink1&quot; title=&quot;bird_bar&quot;  data-wiki-id=&quot;bird_bar&quot;&gt;bird bar&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;td class=&quot;date&quot;&gt;2023/09/08 18:20&lt;/td&gt;&lt;td class=&quot;user&quot;&gt;qlyoung&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;page&quot;&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://wiki.qlyoung.net/car_scuba_rack&quot; class=&quot;wikilink1&quot; title=&quot;car_scuba_rack&quot;  data-wiki-id=&quot;car_scuba_rack&quot;&gt;car scuba rack&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;td class=&quot;date&quot;&gt;2023/09/09 00:45&lt;/td&gt;&lt;td class=&quot;user&quot;&gt;qlyoung&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;page&quot;&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://wiki.qlyoung.net/consistency&quot; class=&quot;wikilink1&quot; title=&quot;consistency&quot;  data-wiki-id=&quot;consistency&quot;&gt;consistency&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;td class=&quot;date&quot;&gt;2023/09/09 00:50&lt;/td&gt;&lt;td class=&quot;user&quot;&gt;qlyoung&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;page&quot;&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://wiki.qlyoung.net/disable_bracketed_paste_in_gnu_readline&quot; class=&quot;wikilink1&quot; title=&quot;disable_bracketed_paste_in_gnu_readline&quot;  data-wiki-id=&quot;disable_bracketed_paste_in_gnu_readline&quot;&gt;disable bracketed paste in gnu readline&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;td class=&quot;date&quot;&gt;2023/09/09 00:07&lt;/td&gt;&lt;td class=&quot;user&quot;&gt;qlyoung&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;page&quot;&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://wiki.qlyoung.net/diy_oxygen_analyzer&quot; class=&quot;wikilink1&quot; title=&quot;diy_oxygen_analyzer&quot;  data-wiki-id=&quot;diy_oxygen_analyzer&quot;&gt;diy oxygen analyzer&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;td class=&quot;date&quot;&gt;2023/09/08 23:33&lt;/td&gt;&lt;td class=&quot;user&quot;&gt;qlyoung&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;page&quot;&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://wiki.qlyoung.net/iphone&quot; class=&quot;wikilink1&quot; title=&quot;iphone&quot;  data-wiki-id=&quot;iphone&quot;&gt;iphone&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;td class=&quot;date&quot;&gt;2023/09/08 23:44&lt;/td&gt;&lt;td class=&quot;user&quot;&gt;qlyoung&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;page&quot;&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://wiki.qlyoung.net/music_management&quot; class=&quot;wikilink1&quot; title=&quot;music_management&quot;  data-wiki-id=&quot;music_management&quot;&gt;music management&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;td class=&quot;date&quot;&gt;2023/09/12 00:39&lt;/td&gt;&lt;td class=&quot;user&quot;&gt;qlyoung&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;page&quot;&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://wiki.qlyoung.net/network_simulation_with_k8s-topo_on_raspi_cluster&quot; class=&quot;wikilink1&quot; title=&quot;network_simulation_with_k8s-topo_on_raspi_cluster&quot;  data-wiki-id=&quot;network_simulation_with_k8s-topo_on_raspi_cluster&quot;&gt;network simulation with k8s-topo on raspberry pi 3b+ cluster&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;td class=&quot;date&quot;&gt;2023/09/09 00:59&lt;/td&gt;&lt;td class=&quot;user&quot;&gt;qlyoung&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;page&quot;&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://wiki.qlyoung.net/personal_infrastructure&quot; class=&quot;wikilink1&quot; title=&quot;personal_infrastructure&quot;  data-wiki-id=&quot;personal_infrastructure&quot;&gt;personal infrastructure&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;td class=&quot;date&quot;&gt;2023/09/08 22:50&lt;/td&gt;&lt;td class=&quot;user&quot;&gt;qlyoung&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/books">
        <dc:format>text/html</dc:format>
        <dc:date>2026-02-16T05:10:43+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>books</title>
        <link>https://wiki.qlyoung.net/books</link>
        <description>
&lt;p&gt;
This isn&amp;#039;t all of them by any stretch, I read countless books in my youth.
2026
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  ✅ ★★★☆☆ The Hound of the Baskervilles&lt;/pre&gt;

&lt;p&gt;
2025
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  ✅ ★★★★☆ The Name of the Wind
  ⏳ ★★★☆☆ The Martian&lt;/pre&gt;

&lt;p&gt;
2024
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  ✅ ★★★★☆ A Canticle for Leibowitz
  ✅ ★★★☆☆ Echopraxia
  ✅ ★★★★☆ Blindsight&lt;/pre&gt;

&lt;p&gt;
2023
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  ⏳ ★★★★☆ The Education of a Cave Diver
  ⏳ ★★★★☆ Cave Diving: Articles and Opinions
  ✅ ★★★★☆ Cryptonomicon
  ✅ ★★★★☆ Deep Into Cave Diving
  ✅ ★★★★☆ Caverns Measureless to Man&lt;/pre&gt;

&lt;p&gt;
2022
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  ✅ ★★★★☆ Die With Zero
  ⏳ ★★★☆☆ Mona Lisa Overdrive
  ✅ ★★★★☆ Death&amp;#039;s End
  ✅ ★★★★★ The Dark Forest
  ✅ ★★★★★ The Three-Body Problem
  ✅ ★★★☆☆ The Lifecycle of Software Objects
  ✅ ★★★★☆ Understand
  ✅ ★★★★☆ Anathem
  ✅ ★★★☆☆ Second Foundation
  ✅ ★★★☆☆ Foundation and Empire
  ✅ ★★★☆☆ Foundation
  ✅ ★★★★☆ God Emperor of Dune&lt;/pre&gt;

&lt;p&gt;
2021
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  ✅ ★★★★☆ Double Star
  ✅ ★★★☆☆ Children of Dune
  ✅ ★★★☆☆ Dune Messiah
  ✅ ★★★★☆ Story of Your Life
  ✅ ★★★★★ Shadow Divers
  ✅ ★★★★☆ The Last Dive
  ✅ ★★★★★ Deco for Divers&lt;/pre&gt;

&lt;p&gt;
2020
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  ✅ ★★★★★ Dune&lt;/pre&gt;

&lt;p&gt;
2019
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  ✅ ★★★★★ The Rise of Endymion
  ✅ ★★★★★ Endymion
  ✅ ★★★★★ The Fall of Hyperion
  ✅ ★★★★★ Hyperion&lt;/pre&gt;

&lt;p&gt;
2018
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  ✅ ★★★☆☆ Count Zero
  ✅ ★★★★★ Neuromancer
  ✅ ★★★★★ Snow Crash&lt;/pre&gt;

&lt;p&gt;
distant past…
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  ✅ ★★★★★ The Moon is a Harsh Mistress
  ✅ ★★★★★ Ringworld
  ✅ ★★★★☆ Ringworld Engineers
  &lt;/pre&gt;

&lt;p&gt;
Very distant past…
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  ✅ ★★★★☆ Halo: Contact Harvest&lt;/pre&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/literature?do=showtag&amp;amp;tag=literature&quot; class=&quot;wikilink1&quot; title=&quot;tag:literature&quot; rel=&quot;tag&quot;&gt;literature&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/car_scuba_rack">
        <dc:format>text/html</dc:format>
        <dc:date>2024-01-06T05:23:37+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>car_scuba_rack</title>
        <link>https://wiki.qlyoung.net/car_scuba_rack</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;car_scuba_rack&quot;&gt;car scuba rack&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
Diving is a passion of mine. I&amp;#039;ve been diving more often lately and, as any diver knows, transporting equipment is one of the logistical challenges of the sport. Having acquired a larger vehicle recently I decided to build a small equipment rack for it to support my usual configuration, namely two single AL80&amp;#039;s.
&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;
&lt;em&gt;Update December 2021&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
Yeah…I no longer dive single 80s. I assumed that when I started diving doubles I would have to find something to do with the PVC rack, but as it turns out, if I just fold the seats of my car down the rack can be slid up further towards the cockpit. At that point doubles fit “in front” of the rack (on the hatch side), a storage bin can be put to the side of that, and the rack can hold another bin and two single bottles - which works perfectly for stages.
&lt;/p&gt;

&lt;p&gt;
Conclusion: this rack can grow with you &lt;img src=&quot;https://wiki.qlyoung.net/lib/images/smileys/smile.svg&quot; class=&quot;icon smiley&quot; alt=&quot;:-)&quot; /&gt;
&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/rack_with_cylinders.jpg?id=car_scuba_rack&quot; class=&quot;media&quot; title=&quot;rack_with_cylinders.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/rack_with_cylinders.jpg?w=400&amp;amp;tok=9fe4ea&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;Rack with cylinders&quot; alt=&quot;Rack with cylinders&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Tools:
&lt;/p&gt;

&lt;p&gt;
- Hacksaw
- Hammer / mallet (optional, helps with seating PVC)
&lt;/p&gt;

&lt;p&gt;
Materials:
&lt;/p&gt;

&lt;p&gt;
- x4 5&amp;#039; lengths of 1.25“ PVC
- x4 T-joints
- x4 4-way joints
- x4 3-way (“side outlet elbow”) joints
&lt;/p&gt;

&lt;p&gt;
I chose not to epoxy the PVC because I foresee making changes to the rack design in the future. If you want to epoxy the PVC into the joints, you&amp;#039;ll also need:
&lt;/p&gt;

&lt;p&gt;
- PVC primer &amp;amp; epoxy
- 200-250 grit sandpaper
&lt;/p&gt;

&lt;p&gt;
The 1.25” PVC can be found at your local hardware store. Neither Lowes nor Home Depot carried any of the 1.25“ joints so I got those on Amazon.
&lt;/p&gt;

&lt;p&gt;
I have 43” of trunk space between the wheel wells in my vehicle. This rack is 41“ wide, which ended up being a snug fit.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/rack_empty.jpg?id=car_scuba_rack&quot; class=&quot;media&quot; title=&quot;rack_empty.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/rack_empty.jpg?w=400&amp;amp;tok=e04357&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;Rack without cylinders or gear bag&quot; alt=&quot;Rack without cylinders or gear bag&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
The sections that run parallel to the cylinders are ~27”. The short connector sections perpendicular to the cylinders are each 5“.
&lt;/p&gt;

&lt;p&gt;
I chose to leave a large central space for my gear bag. Adding additional 27” sections using the T-joints adds capacity for up to 3 additional single AL80s for a total capacity of 5. In the future I may move sections around to support a twinset, which is why I didn&amp;#039;t epoxy anything yet.
&lt;/p&gt;

&lt;p&gt;
Overall I think it turned out pretty good. Total cost of materials was about $100. I believe the demand for PVC to build ventilators has slightly increased the price. Tank racks can be had premade for much cheaper, but I wanted something fit to my use case, equipment and vehicle. Plus it&amp;#039;s fun to build it yourself.
&lt;/p&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/from_blog?do=showtag&amp;amp;tag=from_blog&quot; class=&quot;wikilink1&quot; title=&quot;tag:from_blog&quot; rel=&quot;tag&quot;&gt;from blog&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/making?do=showtag&amp;amp;tag=making&quot; class=&quot;wikilink1&quot; title=&quot;tag:making&quot; rel=&quot;tag&quot;&gt;making&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/diving?do=showtag&amp;amp;tag=diving&quot; class=&quot;wikilink1&quot; title=&quot;tag:diving&quot; rel=&quot;tag&quot;&gt;diving&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/cocktail_bars">
        <dc:format>text/html</dc:format>
        <dc:date>2025-06-15T16:24:14+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>cocktail_bars</title>
        <link>https://wiki.qlyoung.net/cocktail_bars</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;cocktail_bars&quot;&gt;cocktail bars&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
I like cocktail bars. Here&amp;#039;s some notable ones I liked in a variety of cities.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;cocktail bars&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;cocktail_bars&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-107&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit2&quot; id=&quot;vancouver&quot;&gt;Vancouver&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://thekeeferbar.com/&quot; class=&quot;urlextern&quot; title=&quot;https://thekeeferbar.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Keefer Bar&lt;/a&gt; - Red and black anatomical theme, mirrors and laser etched plastic. Very creative cocktail list and skilled bartenders. Live music too. Love it. Intimate and loud, best with a companion&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Vancouver&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;vancouver&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;108-359&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit3&quot; id=&quot;edmonton&quot;&gt;Edmonton&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://honihonitiki.com/&quot; class=&quot;urlextern&quot; title=&quot;https://honihonitiki.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Honi Honi Tiki Lounge&lt;/a&gt; - solid tiki bar in downtown YEG. Nice selection of rums and some unusual-looking agave options. Rum barrel and white pearl were my favorites, and the ti&amp;#039;punch had a really interesting Mexican agricole in it&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.barclementine.ca/&quot; class=&quot;urlextern&quot; title=&quot;https://www.barclementine.ca/&quot; rel=&quot;ugc nofollow&quot;&gt;Clementine&lt;/a&gt; - barkeeper&amp;#039;s bar. Cozy and intimate interior, tiny bar area with lots of selection. Good for meeting strangers&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Edmonton&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;edmonton&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;360-805&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit4&quot; id=&quot;raleigh&quot;&gt;Raleigh&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://foundationnc.com/&quot; class=&quot;urlextern&quot; title=&quot;https://foundationnc.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Foundation&lt;/a&gt; - The best cocktail bar in the Triangle. The proprietor of The Alderman (Chicago) knew these folks.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://auntybettysbar.com/&quot; class=&quot;urlextern&quot; title=&quot;https://auntybettysbar.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Aunty Betty&amp;#039;s&lt;/a&gt; - A gin &amp;amp; absinthe bar. Didn&amp;#039;t have absinthe when I went, but it did have a very knowledgeable and awesome bartender.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.dramanddraught.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.dramanddraught.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Dram and Draught&lt;/a&gt; - Whiskey bar with good cocktails. Haven&amp;#039;t gotten to know this one too well.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://indyweek.com/music/after-a-decade-of-jazz-c-grace-closed-saturday/&quot; class=&quot;urlextern&quot; title=&quot;https://indyweek.com/music/after-a-decade-of-jazz-c-grace-closed-saturday/&quot; rel=&quot;ugc nofollow&quot;&gt;C Grace&lt;/a&gt; (RIP) Used to be an amazing jazz bar, no more.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Raleigh&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;raleigh&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:4,&amp;quot;range&amp;quot;:&amp;quot;806-1410&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit5&quot; id=&quot;chicago&quot;&gt;Chicago&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.thealdermanchicago.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.thealdermanchicago.com/&quot; rel=&quot;ugc nofollow&quot;&gt;The Alderman&lt;/a&gt; - Reservation only but we got in without one. Incredible bar and a very quirky and personable owner-bartender.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://www.cindysrooftop.com/&quot; class=&quot;urlextern&quot; title=&quot;http://www.cindysrooftop.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Cindy&amp;#039;s Rooftop&lt;/a&gt; - Very vanilla bar but has a nice view of the city and millennium park.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Chicago&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;chicago&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:5,&amp;quot;range&amp;quot;:&amp;quot;1411-1723&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit6&quot; id=&quot;long_beach&quot;&gt;Long Beach&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.theordinarie.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.theordinarie.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Ordinarie&lt;/a&gt; - A very cozy bar.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.secretisland.shannonscorner.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.secretisland.shannonscorner.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Secret Island&lt;/a&gt; - Cool tiki bar&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.bambooclublb.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.bambooclublb.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Bamboo Club&lt;/a&gt; - A tiki bar that knows its history. Down the street from &lt;a href=&quot;https://wiki.qlyoung.net/restaurants&quot; class=&quot;wikilink1&quot; title=&quot;restaurants&quot; data-wiki-id=&quot;restaurants&quot;&gt;Chiang Rai&lt;/a&gt;.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Long Beach&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;long_beach&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:6,&amp;quot;range&amp;quot;:&amp;quot;1724-2031&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit7&quot; id=&quot;los_angeles&quot;&gt;Los Angeles&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://web.archive.org/web/20240226014113/https://theneverlands.com/pacific-seas/&quot; class=&quot;urlextern&quot; title=&quot;https://web.archive.org/web/20240226014113/https://theneverlands.com/pacific-seas/&quot; rel=&quot;ugc nofollow&quot;&gt;Clifton&amp;#039;s Pacific Seas&lt;/a&gt; - My first tiki bar and a very special one. One of the best interiors of any tiki bar I have been in.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Los Angeles&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;los_angeles&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:7,&amp;quot;range&amp;quot;:&amp;quot;2032-2270&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit8&quot; id=&quot;denver&quot;&gt;Denver&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.hellorhighwatertiki.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.hellorhighwatertiki.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Hell or High Water&lt;/a&gt; - gay/queer tiki bar in Denver. Unique selection of glassware and server garb.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Denver&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;denver&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:8,&amp;quot;range&amp;quot;:&amp;quot;2271-2432&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit9&quot; id=&quot;seattle&quot;&gt;Seattle&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.canonseattle.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.canonseattle.com/&quot; rel=&quot;ugc nofollow&quot;&gt;canon&lt;/a&gt; - notable for having the largest spirit collection in America, I felt like I was wasting the bar&amp;#039;s time ordering off the menu.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.rumbaonpike.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.rumbaonpike.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Rumba&lt;/a&gt; - canon for rum drinkers. Told the server I was interested in rhum agricoles - received a flight and a 20 minute crash course + q&amp;amp;a on the subject when he returned. Amazing bar.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.insidepassageseattle.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.insidepassageseattle.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Inside Passage&lt;/a&gt; (inside Rumba) - A neat tiki spot that focuses more on novelty than other tiki bars I&amp;#039;ve attended. The cocktails are good but premixed to save space, you can&amp;#039;t order off menu here.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Seattle&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;seattle&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:9,&amp;quot;range&amp;quot;:&amp;quot;2433-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/consistency">
        <dc:format>text/html</dc:format>
        <dc:date>2024-01-06T05:28:20+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>consistency</title>
        <link>https://wiki.qlyoung.net/consistency</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;consistency&quot;&gt;consistency&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
consistency in doing something disagreeable is one of the harder things, even
when its something you want
&lt;/p&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/from_blog?do=showtag&amp;amp;tag=from_blog&quot; class=&quot;wikilink1&quot; title=&quot;tag:from_blog&quot; rel=&quot;tag&quot;&gt;from blog&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/pithy?do=showtag&amp;amp;tag=pithy&quot; class=&quot;wikilink1&quot; title=&quot;tag:pithy&quot; rel=&quot;tag&quot;&gt;pithy&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/stupid?do=showtag&amp;amp;tag=stupid&quot; class=&quot;wikilink1&quot; title=&quot;tag:stupid&quot; rel=&quot;tag&quot;&gt;stupid&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/degoogling">
        <dc:format>text/html</dc:format>
        <dc:date>2026-02-12T17:07:34+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>degoogling</title>
        <link>https://wiki.qlyoung.net/degoogling</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;degoogling&quot;&gt;degoogling&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
Sometime around 2021 I began to feel that use of Google services constituted a grave threat to my digital sovereignty, and began slowly moving off of them.
&lt;/p&gt;

&lt;p&gt;
Here in 2025 I still have a Google account and probably will for the foreseeable future, because Google Fi and Google Fiber remain the best cell service and internet service providers, and you need a Google account to pay for them.
&lt;/p&gt;

&lt;p&gt;
Today I use my Google account for five things:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; As an OAuth identity for services that require one - literally just Tailscale, I have replaced everything else with email-based accounts&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Filling out Google Forms - please stop using these folks, they often require a Google account&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Access to shared Google Photos albums&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Google Fi (cell service)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Google Fiber (internet)&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Despite these limited use cases, which are all provided for free with a personal-tier Google account, I still have to pay for Google Suite ($8/month). Why? Because years ago when I wanted to use a custom domain on my Gmail account, I started paying for Google Suite to enable this functionality. When you do this, your Google account is converted from a “Personal” to a “Business” account. This is a one way transition. Once this happens, if you stop paying for it, your level of services drops *below* that offered to a free personal Google account. So unless I want to completely disable my Google account (spoilers: I do, but not yet) I have to continue to pay Google, because they don&amp;#039;t offer a downgrade path.
&lt;/p&gt;

&lt;p&gt;
Below I share what I have replaced various Google services with.
&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;degoogling&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;degoogling&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-1592&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit2&quot; id=&quot;gmail&quot;&gt;Gmail&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Migrated from Gmail first to Protonmail, and then in 2023 to Fastmail, which I am much more satisfied with. I was paying for GSuite for Business in order to use a custom domain name, now I pay Fastmail for that.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Gmail&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;gmail&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;1593-1822&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit3&quot; id=&quot;search&quot;&gt;Search&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
I pay for &lt;a href=&quot;https://kagi.com/&quot; class=&quot;urlextern&quot; title=&quot;https://kagi.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Kagi&lt;/a&gt;; and in 2025 onward, I don&amp;#039;t need to use that much either now that we have effective LLMs.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Search&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;search&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;1823-1968&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit4&quot; id=&quot;google_drive&quot;&gt;Google Drive&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Gdrive has many uses. Replacing it means replacing each function.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4 id=&quot;file_sharing&quot;&gt;File sharing&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Tried and rejected:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.filestash.app/&quot; class=&quot;urlextern&quot; title=&quot;https://www.filestash.app/&quot; rel=&quot;ugc nofollow&quot;&gt;Filestash&lt;/a&gt; - kept running out of space on cloud VMs, S3 integration setup is a little scary, software buggy&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://nextcloud.com/&quot; class=&quot;urlextern&quot; title=&quot;https://nextcloud.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Nextcloud&lt;/a&gt; - I use this on my intranet, but it&amp;#039;s too heavy to host a separate public instance just for filesharing&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://github.com/magic-wormhole/magic-wormhole&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/magic-wormhole/magic-wormhole&quot; rel=&quot;ugc nofollow&quot;&gt;magic wormhole&lt;/a&gt; - works, but people don&amp;#039;t want to install programs to receive files and the mobile story is bad. shout out &lt;a href=&quot;https://rymdport.github.io/&quot; class=&quot;urlextern&quot; title=&quot;https://rymdport.github.io/&quot; rel=&quot;ugc nofollow&quot;&gt;rymdport&lt;/a&gt; for favorite client.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://syncthing.net/&quot; class=&quot;urlextern&quot; title=&quot;https://syncthing.net/&quot; rel=&quot;ugc nofollow&quot;&gt;Syncthing&lt;/a&gt; - It&amp;#039;s a sync program, it isn&amp;#039;t intended for unidirectional file transfer and thus doesn&amp;#039;t work well for that purpose&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
I now pay for Dropbox. Upload to folder, share link. Sometimes you just gotta pay and move on.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4 id=&quot;cloud_storage&quot;&gt;Cloud storage&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Okay, “cloud storage” is not really a function. There&amp;#039;s two main reasons we use personal cloud storage:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; So that we can store files in one logical “place” and then access them from any device&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; To save space on our devices&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
There&amp;#039;s other reasons, mostly to do with reliability engineering, but those are the main two.
&lt;/p&gt;

&lt;p&gt;
&lt;abbr title=&quot;Too long; didn&amp;#039;t read&quot;&gt;TL;DR&lt;/abbr&gt; I now use &lt;a href=&quot;https://wiki.qlyoung.net/tiered_storage&quot; class=&quot;wikilink1&quot; title=&quot;tiered_storage&quot; data-wiki-id=&quot;tiered_storage&quot;&gt;tiered storage&lt;/a&gt;; what little need I have for internet-attached storage is now met by my home server, which has a lot of storage and is always accessible via tailscale. More on that in &lt;a href=&quot;https://wiki.qlyoung.net/personal_infrastructure&quot; class=&quot;wikilink1&quot; title=&quot;personal_infrastructure&quot; data-wiki-id=&quot;personal_infrastructure&quot;&gt;personal infrastructure&lt;/a&gt;.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Google Drive&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;google_drive&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:4,&amp;quot;range&amp;quot;:&amp;quot;1969-3465&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit5&quot; id=&quot;google_photos&quot;&gt;Google Photos&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
For hosting my personal photos, I initially replaced Google Photos with &lt;a href=&quot;https://www.photoprism.app/&quot; class=&quot;urlextern&quot; title=&quot;https://www.photoprism.app/&quot; rel=&quot;ugc nofollow&quot;&gt;Photoprism&lt;/a&gt;. I also tried &lt;a href=&quot;https://immich.app/&quot; class=&quot;urlextern&quot; title=&quot;https://immich.app/&quot; rel=&quot;ugc nofollow&quot;&gt;Immich&lt;/a&gt; and while I know this is considered heretical, it doesn&amp;#039;t suit my needs. I ultimately gave up on server-based photo management, but that&amp;#039;s &lt;a href=&quot;https://wiki.qlyoung.net/photo_management&quot; class=&quot;wikilink1&quot; title=&quot;photo_management&quot; data-wiki-id=&quot;photo_management&quot;&gt;another story&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
The short of it is that I now store all my photos in a plain local directory synced across devices.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4 id=&quot;sharing&quot;&gt;Sharing&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Sharing photos is more complicated. The usual situation for me is that I go on a trip with a bunch of people, and afterwards someone sets up a shared Google Photos album, everyone uploads their pictures to “the shared album”.
&lt;/p&gt;

&lt;p&gt;
When you upload pictures to a “shared album”, they are in fact uploaded to your personal Google Photos account and then are published such that anyone with access to the shared album has access to those photos. Consequently you can&amp;#039;t add photos to shared albums without storing photos in Google Photos. If you later delete those photos they disappear from the shared album.
&lt;/p&gt;

&lt;p&gt;
I have tried various methods of sharing my photos with gphotos users, including:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Uploading my photos “to” the shared album, advising all participants that they will be deleted in 2 weeks, and then deleting them&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Zipping the photos and sharing the zip via one of the methods described in the previous section&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
My current solution is to dump all my photos into a Dropbox album and send people the link. Whenever I need to free up space I delete the dropbox folder. I still have all the photos locally in my own library so I can reshare if needed.
&lt;/p&gt;

&lt;p&gt;
This is much to the chagrin of people I go on group trips with, because they don&amp;#039;t understand why I can&amp;#039;t just use Google Photos like them and I end up being viewed as a source of inconvenience.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Google Photos&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;google_photos&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:5,&amp;quot;range&amp;quot;:&amp;quot;3466-5295&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit6&quot; id=&quot;google_contacts_calendar&quot;&gt;Google Contacts &amp;amp; Calendar&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
I run Nextcloud on my home server and have replaced G services with Nextcloud&amp;#039;s &lt;a href=&quot;https://en.wikipedia.org/wiki/CardDAV&quot; class=&quot;interwiki iw_wp&quot; title=&quot;https://en.wikipedia.org/wiki/CardDAV&quot;&gt;CardDAV&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/CalDAV&quot; class=&quot;interwiki iw_wp&quot; title=&quot;https://en.wikipedia.org/wiki/CalDAV&quot;&gt;CalDAV&lt;/a&gt; implementations. These legitimately work better than Google Calendar and Google Contacts, especially on iOS which has first class support for CardDAV and CalDAV baked into the system; consequently system apps and facilities that rely on accurate calendar and contact sync work flawlessly.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Google Contacts &amp;amp; Calendar&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;google_contacts_calendar&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:6,&amp;quot;range&amp;quot;:&amp;quot;5296-5735&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit7&quot; id=&quot;youtube&quot;&gt;YouTube&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
For hosting my own videos, I have replaced it with &lt;a href=&quot;https://qtube.qlyoung.net/&quot; class=&quot;urlextern&quot; title=&quot;https://qtube.qlyoung.net/&quot; rel=&quot;ugc nofollow&quot;&gt;Peertube&lt;/a&gt;, although my YouTube channel is still up because I haven&amp;#039;t gotten around to wiping it yet.
&lt;/p&gt;

&lt;p&gt;
Unfortunately &lt;a href=&quot;https://www.youtube.com/watch?v=LhFsz0OqOJA&quot; class=&quot;urlextern&quot; title=&quot;https://www.youtube.com/watch?v=LhFsz0OqOJA&quot; rel=&quot;ugc nofollow&quot;&gt;one video&lt;/a&gt; I made has popped off, and since it&amp;#039;s educational I&amp;#039;m a little reluctant to take it down. I will probably make a video explaining how to get to my Peertube, upload that to YouTube, and then take everything else down. Then when I finally get around to deleting my Google account, that will disappear too.
&lt;/p&gt;

&lt;p&gt;
Of course I still watch videos on YouTube, and I still subscribe to channels. However, I do it via a self-hosted instance of &lt;a href=&quot;https://invidious.io/&quot; class=&quot;urlextern&quot; title=&quot;https://invidious.io/&quot; rel=&quot;ugc nofollow&quot;&gt;invidious&lt;/a&gt;. This way all my subscription data information etc is kept on my home server. On my iPhone I use &lt;a href=&quot;https://github.com/yattee/yattee&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/yattee/yattee&quot; rel=&quot;ugc nofollow&quot;&gt;yattee&lt;/a&gt; as a client to my Invidious instance, so I get a YouTube app-like experience. Also has the side effect of being ad-free &lt;img src=&quot;https://wiki.qlyoung.net/lib/images/smileys/smile.svg&quot; class=&quot;icon smiley&quot; alt=&quot;:-)&quot; /&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;YouTube&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;youtube&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:7,&amp;quot;range&amp;quot;:&amp;quot;5736-6741&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit8&quot; id=&quot;keep&quot;&gt;Keep&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
See &lt;a href=&quot;https://wiki.qlyoung.net/note_taking_programs&quot; class=&quot;wikilink1&quot; title=&quot;note_taking_programs&quot; data-wiki-id=&quot;note_taking_programs&quot;&gt;note taking programs&lt;/a&gt;.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Keep&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;keep&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:8,&amp;quot;range&amp;quot;:&amp;quot;6742-6788&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit9&quot; id=&quot;androidplay_services&quot;&gt;Android / Play Services&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Bought an &lt;a href=&quot;https://wiki.qlyoung.net/iphone&quot; class=&quot;wikilink1&quot; title=&quot;iphone&quot; data-wiki-id=&quot;iphone&quot;&gt;iPhone&lt;/a&gt;.
&lt;/p&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/self-hosting?do=showtag&amp;amp;tag=self-hosting&quot; class=&quot;wikilink1&quot; title=&quot;tag:self-hosting&quot; rel=&quot;tag&quot;&gt;self-hosting&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/technology?do=showtag&amp;amp;tag=technology&quot; class=&quot;wikilink1&quot; title=&quot;tag:technology&quot; rel=&quot;tag&quot;&gt;technology&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Android \/ Play Services&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;androidplay_services&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:9,&amp;quot;range&amp;quot;:&amp;quot;6789-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/digital_mapping">
        <dc:format>text/html</dc:format>
        <dc:date>2024-07-02T21:54:55+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>digital_mapping</title>
        <link>https://wiki.qlyoung.net/digital_mapping</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;digital_mapping&quot;&gt;digital mapping&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
OpenStreetMap publishes maps of all Earth regions, including the &lt;a href=&quot;https://planet.openstreetmap.org/&quot; class=&quot;urlextern&quot; title=&quot;https://planet.openstreetmap.org/&quot; rel=&quot;ugc nofollow&quot;&gt;whole planet&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
To use this data as a visual map, you need to &lt;a href=&quot;https://wiki.openstreetmap.org/wiki/Rendering&quot; class=&quot;urlextern&quot; title=&quot;https://wiki.openstreetmap.org/wiki/Rendering&quot; rel=&quot;ugc nofollow&quot;&gt;render&lt;/a&gt; the raw data - which is available in XML and &lt;a href=&quot;https://wiki.openstreetmap.org/wiki/PBF_Format&quot; class=&quot;urlextern&quot; title=&quot;https://wiki.openstreetmap.org/wiki/PBF_Format&quot; rel=&quot;ugc nofollow&quot;&gt;PBF&lt;/a&gt; (you want pbf) - into either &lt;a href=&quot;https://en.wikipedia.org/wiki/Vector graphics&quot; class=&quot;interwiki iw_wp&quot; title=&quot;https://en.wikipedia.org/wiki/Vector graphics&quot;&gt;vector&lt;/a&gt; or &lt;a href=&quot;https://en.wikipedia.org/wiki/Raster_graphics&quot; class=&quot;interwiki iw_wp&quot; title=&quot;https://en.wikipedia.org/wiki/Raster_graphics&quot;&gt;raster&lt;/a&gt; &lt;a href=&quot;https://wiki.openstreetmap.org/wiki/Tiles&quot; class=&quot;urlextern&quot; title=&quot;https://wiki.openstreetmap.org/wiki/Tiles&quot; rel=&quot;ugc nofollow&quot;&gt;tiles&lt;/a&gt;. There are a variety of tools that do that; the easiest is probably &lt;a href=&quot;https://github.com/onthegomap/planetiler&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/onthegomap/planetiler&quot; rel=&quot;ugc nofollow&quot;&gt;planetiler&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
When you render, you need to pick an appropriate &lt;a href=&quot;https://wiki.openstreetmap.org/wiki/Zoom_levels&quot; class=&quot;urlextern&quot; title=&quot;https://wiki.openstreetmap.org/wiki/Zoom_levels&quot; rel=&quot;ugc nofollow&quot;&gt;zoom level&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
On my enthusiast desktop, planetiler took around 2 hours to generate vector pmtiles of the planet with all zoom levels. The resulting file was 69gb.
&lt;/p&gt;

&lt;p&gt;
After that, you can serve the tiles using a &lt;a href=&quot;https://wiki.openstreetmap.org/wiki/Tile_server&quot; class=&quot;urlextern&quot; title=&quot;https://wiki.openstreetmap.org/wiki/Tile_server&quot; rel=&quot;ugc nofollow&quot;&gt;tile server&lt;/a&gt;. Connect an appropriate client to view. Some tile servers like &lt;a href=&quot;https://github.com/maptiler/tileserver-gl&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/maptiler/tileserver-gl&quot; rel=&quot;ugc nofollow&quot;&gt;tileserver-gl&lt;/a&gt; serve a web-based viewer so you don&amp;#039;t need a client.
&lt;/p&gt;

&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/disable_bracketed_paste_in_gnu_readline">
        <dc:format>text/html</dc:format>
        <dc:date>2024-01-06T05:25:43+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>disable_bracketed_paste_in_gnu_readline</title>
        <link>https://wiki.qlyoung.net/disable_bracketed_paste_in_gnu_readline</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;disable_bracketed_paste_in_gnu_readline&quot;&gt;disable bracketed paste in gnu readline&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
In early 2021 GNU Readline, the venerated line editing library used in everything from bash to &lt;a href=&quot;http://docs.frrouting.org/projects/dev-guide/en/latest/vtysh.html&quot; class=&quot;urlextern&quot; title=&quot;http://docs.frrouting.org/projects/dev-guide/en/latest/vtysh.html&quot; rel=&quot;ugc nofollow&quot;&gt;vtysh&lt;/a&gt;, enabled bracketed paste mode by default. This is a significant behavior change, and while it&amp;#039;s &lt;a href=&quot;https://lwn.net/Articles/839213/&quot; class=&quot;urlextern&quot; title=&quot;https://lwn.net/Articles/839213/&quot; rel=&quot;ugc nofollow&quot;&gt;noted&lt;/a&gt; that there is a compile time option to change this default, generally you can&amp;#039;t tell users to recompile some system library to get a feature back.
&lt;/p&gt;

&lt;p&gt;
The Readline docs themselves are in the typical overly-terse GNU style so this is a quick note on how to restore the old behavior in your readline-using program. There&amp;#039;s a function in readline that takes a string and interprets it as if it were present in the user&amp;#039;s &lt;code&gt;.inputrc&lt;/code&gt; file; this can be leveraged to disable bracketed paste mode like so:
&lt;/p&gt;
&lt;pre class=&quot;code c&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;*&lt;/span&gt;disable_bracketed_paste &lt;span class=&quot;sy0&quot;&gt;=&lt;/span&gt; strdup&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;set enable-bracketed-paste off&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;;&lt;/span&gt;
rl_parse_and_bind&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;disable_bracketed_paste&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;;&lt;/span&gt;
&lt;a href=&quot;http://www.opengroup.org/onlinepubs/009695399/functions/free.html&quot;&gt;&lt;span class=&quot;kw3&quot;&gt;free&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;disable_bracketed_paste&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;;&lt;/span&gt;&lt;/pre&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/from_blog?do=showtag&amp;amp;tag=from_blog&quot; class=&quot;wikilink1&quot; title=&quot;tag:from_blog&quot; rel=&quot;tag&quot;&gt;from blog&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/technology?do=showtag&amp;amp;tag=technology&quot; class=&quot;wikilink1&quot; title=&quot;tag:technology&quot; rel=&quot;tag&quot;&gt;technology&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/programming?do=showtag&amp;amp;tag=programming&quot; class=&quot;wikilink1&quot; title=&quot;tag:programming&quot; rel=&quot;tag&quot;&gt;programming&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/dive_light_burn_times">
        <dc:format>text/html</dc:format>
        <dc:date>2024-07-02T21:57:16+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>dive_light_burn_times</title>
        <link>https://wiki.qlyoung.net/dive_light_burn_times</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;dive_light_burn_times&quot;&gt;dive light burn times&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
I like to go diving in dark places. In dark places like caves, lights are important, and we carry a bunch of them. The rule in cave diving is to carry at least three lights for redundancy: one primary and two or more backups.
&lt;/p&gt;

&lt;p&gt;
I use these lights as backup lights: &lt;a href=&quot;https://www.divegearexpress.com/dgx-600-twist-handheld-light&quot; class=&quot;urlextern&quot; title=&quot;https://www.divegearexpress.com/dgx-600-twist-handheld-light&quot; rel=&quot;ugc nofollow&quot;&gt;https://www.divegearexpress.com/dgx-600-twist-handheld-light&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
They are ruggedly built, simple, and so far quite reliable. They do take rechargeable batteries. There is debate on whether non rechargeable batteries are better suited for use here, centered on discharge characteristics and the human factors involved in remembering to charge a light that you intentionally never use. Nevertheless I do currently use them. I had the pleasure of meeting Bill Main in the parking lot of Little River springs one day; he uses them as well. Consider that a celebrity endorsement. Bill also uses a straight-through manifold without an isolator valve, but that is for another wiki page.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/img_008926.heic.avif?id=dive_light_burn_times&quot; class=&quot;media&quot; title=&quot;img_008926.heic.avif&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/img_008926.heic.avif?w=600&amp;amp;tok=2c76d3&quot; class=&quot;mediaright&quot; align=&quot;right&quot; loading=&quot;lazy&quot; title=&quot;Picture of DGX 600 twist light&quot; alt=&quot;Picture of DGX 600 twist light&quot; width=&quot;600&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
It&amp;#039;s important to know how long your main lights and backup lights can burn for. Therefore, I did some burn tests.
&lt;/p&gt;

&lt;p&gt;
My setup is a cheap webcam (the same one used for &lt;a href=&quot;https://wiki.qlyoung.net/bird_bar&quot; class=&quot;wikilink1&quot; title=&quot;bird_bar&quot; data-wiki-id=&quot;bird_bar&quot;&gt;bird bar&lt;/a&gt;) plugged into a cheap macbook. I placed the webcam on a chair pointing at a wall. On the chair I also placed a tupperware full of water. This simulates the diving environment. Then I fully charged the light battery, turned the light on and placed it in the tupperware facing the wall. Finally, I recorded the camera image using OBS. This way I could check the recording to see when the light stopped burning, as well as view the luminance characteristics as the light discharged, without needing to monitor the light.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/img_008905.heic.avif?id=dive_light_burn_times&quot; class=&quot;media&quot; title=&quot;img_008905.heic.avif&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/img_008905.heic.avif?w=600&amp;amp;tok=37faff&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;Picture of test setup for testing light discharge&quot; alt=&quot;Picture of test setup for testing light discharge&quot; width=&quot;600&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
It turns out the DGX 600 twist light burns for between 90-100 minutes. Over this time period it gets gradually less bright until it finally goes out completely. It is still usefully bright until the moment it turns off.
&lt;/p&gt;

&lt;p&gt;
&lt;video class=&quot;media&quot; title=&quot;Video of light discharge behavior&quot; width=&quot;633&quot; height=&quot;360&quot; controls=&quot;controls&quot;&gt;
&lt;source src=&quot;/_media/lights-discharge.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_media/lights-discharge.mp4&quot; class=&quot;media mediafile mf_mp4&quot; title=&quot;lights-discharge.mp4 (5 MB)&quot;&gt;Video of light discharge behavior&lt;/a&gt;&lt;/video&gt;

&lt;/p&gt;

&lt;p&gt;
&lt;em&gt;&lt;a href=&quot;https://wiki.qlyoung.net/av1&quot; class=&quot;wikilink1&quot; title=&quot;av1&quot; data-wiki-id=&quot;av1&quot;&gt;this video doesn&amp;#039;t play!&lt;/a&gt;&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
The apparent increases in brightness in this video are caused by the automatic exposure control of the camera adjusting for decreasing brightness levels and do not occur in reality.
&lt;/p&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/diving?do=showtag&amp;amp;tag=diving&quot; class=&quot;wikilink1&quot; title=&quot;tag:diving&quot; rel=&quot;tag&quot;&gt;diving&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/diy_oxygen_analyzer">
        <dc:format>text/html</dc:format>
        <dc:date>2024-01-06T05:21:51+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>diy_oxygen_analyzer</title>
        <link>https://wiki.qlyoung.net/diy_oxygen_analyzer</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;diy_oxygen_analyzer&quot;&gt;diy oxygen analyzer&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
In July 2021 I assembled an oxygen analyzer. The purpose of this device is to measure the oxygen content of a gas; for my purposes I&amp;#039;m using it to analyze breathing gases for diving. Commercial analyzers are available at reasonable life-support prices. Having an interest in electronics hardware, I opted to use a &lt;a href=&quot;https://www.oxycheq.com/products/el-cheapo-ii-analyzer-kit&quot; class=&quot;urlextern&quot; title=&quot;https://www.oxycheq.com/products/el-cheapo-ii-analyzer-kit&quot; rel=&quot;ugc nofollow&quot;&gt;DIY kit&lt;/a&gt; instead. For the search engines, that&amp;#039;s the “El Cheapo II oxygen analyzer” by OxyCheq. The kit is all the components to build an analyzer and a paper printout of instructions on how to put them together. I found the printout charming. Unfortunately I left it at the hardware store when I went there to get some snips and solder.  This was a significant setback for me as this was my first electronics project of any sort and I had no idea what the hell I was doing. There&amp;#039;s no digital copy available for public download and obtaining one from the gentleman who owns &amp;amp; operates OxyCheq took some patience, but eventually I was able to get a replacement for the instructions and begin work on the analyzer.
&lt;/p&gt;

&lt;p&gt;
Progress was slow. Building the unit involves cutting appropriate mounting holes into the generic black plastic case that comes in the kit. I found a Dremel ill suited to this task; the fiberglass cutting blades I had melted rather than cut the plastic. Nothing some filing couldn&amp;#039;t fix and eventually I had my holes cut. After mounting all the stuff into the board I was ready for the electronics portion, which the instructions cheerily describe as the easy part. It turns out this is only true if you have a good soldering iron. The soldering iron I used at first was butane powered with a rather thick tip. With my unskilled, slow soldering operations, the butane kept running out. Part of building the kit involves removing a surface mount resistor. Doing this with a soldering tip that is as big as the entire surface mount component is difficult.  Soldering components to the pads the SMT used to be on was, for my hand, nearly impossible. Almost immediately I ripped off one of the pads.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/pm128-missing-pad.jpg?id=diy_oxygen_analyzer&quot; class=&quot;media&quot; title=&quot;pm128-missing-pad.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/pm128-missing-pad.jpg?w=400&amp;amp;tok=a63a57&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;PM-128 with R3 solder pad missing&quot; alt=&quot;PM-128 with R3 solder pad missing&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
This was very disappointing. Being new to electronics I assumed I had destroyed the board and set about ordering a new one. I was able to find them &lt;a href=&quot;https://www.circuitspecialists.com/lcd-panel-meter-pm-128a.html&quot; class=&quot;urlextern&quot; title=&quot;https://www.circuitspecialists.com/lcd-panel-meter-pm-128a.html&quot; rel=&quot;ugc nofollow&quot;&gt;here&lt;/a&gt;. There are several varieties of that board listed &lt;a href=&quot;https://www.circuitspecialists.com/lcd-panel-meters&quot; class=&quot;urlextern&quot; title=&quot;https://www.circuitspecialists.com/lcd-panel-meters&quot; rel=&quot;ugc nofollow&quot;&gt;here&lt;/a&gt; with various possibly desirable features if you sort of know what you&amp;#039;re doing. I didn&amp;#039;t so I got the same model.
&lt;/p&gt;

&lt;p&gt;
In the meantime some friends online who know things about electronics set me straight. They had a look at the board and noted that the bit of missing solder mask between the lower R3 and R2 pads, which suggested that they might be connected.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/pm128-missing-pad-closeup.png?id=diy_oxygen_analyzer&quot; class=&quot;media&quot; title=&quot;pm128-missing-pad-closeup.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/pm128-missing-pad-closeup.png?w=400&amp;amp;tok=bb279d&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;PM-128 with R3 solder pad missing; closeup&quot; alt=&quot;PM-128 with R3 solder pad missing; closeup&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
My friend made this helpful schematic.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/pm128-missing-pad-closeup-schematic-guesses.png?id=diy_oxygen_analyzer&quot; class=&quot;media&quot; title=&quot;pm128-missing-pad-closeup-schematic-guesses.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/pm128-missing-pad-closeup-schematic-guesses.png?w=400&amp;amp;tok=f4a791&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;PM-128 with R3 solder pad missing; closeup with overlaid speculative schematic markings&quot; alt=&quot;PM-128 with R3 solder pad missing; closeup with overlaid speculative schematic markings&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Unfortunately with no R2 pad I couldn&amp;#039;t verify this with an ohmeter, but I assumed that this was correct and proceeded to do a very bad solder joint to R3, which you can see in this picture along with some other similarly bad joints.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/pm128-horrible-solder-job.jpg?id=diy_oxygen_analyzer&quot; class=&quot;media&quot; title=&quot;pm128-horrible-solder-job.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/pm128-horrible-solder-job.jpg?w=400&amp;amp;tok=5568ea&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;PM-128 showing various bad solder joints&quot; alt=&quot;PM-128 showing various bad solder joints&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
(Later, after completing the project, I found &lt;a href=&quot;https://www.scubaboard.com/community/threads/el-cheapo-ii-diy-o2-analyzer-success-franken-analyzer-is-alive.508882/&quot; class=&quot;urlextern&quot; title=&quot;https://www.scubaboard.com/community/threads/el-cheapo-ii-diy-o2-analyzer-success-franken-analyzer-is-alive.508882/&quot; rel=&quot;ugc nofollow&quot;&gt;|this thread&lt;/a&gt; which describes doing the exact same thing I did - and verifies that R2 and R3 pads are indeed connected. Oh well.)
&lt;/p&gt;

&lt;p&gt;
After doing this joint and all the others, I plugged in the sensor, flipped on the meter, and to my dismay observed that the meter showed very negative, very positive or otherwise garbage values. Moreover it changed values if it was picked up.
&lt;/p&gt;

&lt;p&gt;
By this time the other PM-128A I had ordered arrived, along with a much better soldering iron. Armed with proper tools and a fresh board I reworked all of the electronics. With the benefit of practice and my new iron the joints came out much better this time. Unfortunately the meter was as wrong as ever. To cut a long story short, the oxygen sensor connects via a standard 3.5mm TRS jack. There are 3 pins on this jack. The jack part provided with the kit was different from the one described in the printout instructions. I had connected the voltmeter to the wrong pins on the jack, and one of the wrong pins was rather loose. Shaking this pin corresponded to the meter changes. I changed up the pins and the problem was solved.
&lt;/p&gt;

&lt;p&gt;
Since I had trouble with some of the wiring instructions, I&amp;#039;m posting the correct wiring here in hopes someone else might find them useful.
&lt;/p&gt;

&lt;p&gt;
Here&amp;#039;s the complete wiring diagram from the instructions:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/ec2-wiring-diagram.png?id=diy_oxygen_analyzer&quot; class=&quot;media&quot; title=&quot;ec2-wiring-diagram.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/ec2-wiring-diagram.png?w=400&amp;amp;tok=cdcaad&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;Full wiring diagram for El Cheapo II oxygen analyzer&quot; alt=&quot;Full wiring diagram for El Cheapo II oxygen analyzer&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
(See what I mean by charming? &lt;img src=&quot;https://wiki.qlyoung.net/lib/images/smileys/smile.svg&quot; class=&quot;icon smiley&quot; alt=&quot;:-)&quot; /&gt; Jack wiring from the instructions:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/ec2-jack-wiring-diagram.png?id=diy_oxygen_analyzer&quot; class=&quot;media&quot; title=&quot;ec2-jack-wiring-diagram.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/ec2-jack-wiring-diagram.png?w=400&amp;amp;tok=942861&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;TRS jack wiring diagram&quot; alt=&quot;TRS jack wiring diagram&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Correct jack wiring (note wire colors):
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/jack-correct-wiring-1.jpg?id=diy_oxygen_analyzer&quot; class=&quot;media&quot; title=&quot;jack-correct-wiring-1.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/jack-correct-wiring-1.jpg?w=400&amp;amp;tok=0e45c4&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;Correct TRS jack to voltmeter wiring&quot; alt=&quot;Correct TRS jack to voltmeter wiring&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/jack-correct-wiring-2.jpg?id=diy_oxygen_analyzer&quot; class=&quot;media&quot; title=&quot;jack-correct-wiring-2.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/jack-correct-wiring-2.jpg?w=400&amp;amp;tok=4c6691&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;Correct TRS jack to voltmeter wiring&quot; alt=&quot;Correct TRS jack to voltmeter wiring&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
I also opted to use the upgraded 10 turn potentiometer, available from OxyCheq, rather than the standard one that comes with the kit. The difference is that it&amp;#039;s much more precise - the same range of values are covered in 10 turns of the knob versus 1, so it&amp;#039;s much easier to calibrate the meter down to a tenth of a percent. The downside is that the diagram in the instructions doesn&amp;#039;t cover this potentiometer, so some trial and error was required to find the appropriate wiring.
&lt;/p&gt;

&lt;p&gt;
Pot wiring from the instructions:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/ec2-pot-wiring-diagram.png?id=diy_oxygen_analyzer&quot; class=&quot;media&quot; title=&quot;ec2-pot-wiring-diagram.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/ec2-pot-wiring-diagram.png?w=400&amp;amp;tok=3f77a6&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;pot wiring diagram&quot; alt=&quot;pot wiring diagram&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
The correct pot wiring for the 10 turn pot is visible in this overall shot of the completed circuit.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/o2-analyzer-finished-shot.jpg?id=diy_oxygen_analyzer&quot; class=&quot;media&quot; title=&quot;o2-analyzer-finished-shot.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/o2-analyzer-finished-shot.jpg?w=400&amp;amp;tok=037f7a&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;Side view of completed analyzer internals&quot; alt=&quot;Side view of completed analyzer internals&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
I know the joint on the lower leg of the pot looks horrible, I fixed it after that photo.
&lt;/p&gt;

&lt;p&gt;
So does it work? Hell yeah! It really prefers being used on DIN valves with the DIN adapter available from OxyCheq, but I was able to get good test readings on EAN30 on a yoke valve with a bit of technique. I used it on a trip to the Spiegel Grove last week and it worked, although at the end of the trip the solder joint to the pot came loose, resulting in a constant reading of .03%. The important thing is that when it failed it failed in an obvious way.
&lt;/p&gt;

&lt;p&gt;
The sensor used is a standard O2 sensor that you might find in a rebreather. Because it&amp;#039;s an off the shelf sensor, it can be easily replaced at the end of its service life, which is a significant advantage over some of the commercial analyzers.
&lt;/p&gt;

&lt;p&gt;
I learned a lot from this project. In no particular order: - Butane soldering irons and large tips are not ideal How to use an ohmeter and its applications Various types of solder and the utility of flux Much can be learned from solder mask
&lt;/p&gt;

&lt;p&gt;
Thanks to the friends that helped me through my first electronics project!
&lt;/p&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/from_blog?do=showtag&amp;amp;tag=from_blog&quot; class=&quot;wikilink1&quot; title=&quot;tag:from_blog&quot; rel=&quot;tag&quot;&gt;from blog&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/diving?do=showtag&amp;amp;tag=diving&quot; class=&quot;wikilink1&quot; title=&quot;tag:diving&quot; rel=&quot;tag&quot;&gt;diving&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/technology?do=showtag&amp;amp;tag=technology&quot; class=&quot;wikilink1&quot; title=&quot;tag:technology&quot; rel=&quot;tag&quot;&gt;technology&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/documentation">
        <dc:format>text/html</dc:format>
        <dc:date>2026-02-12T17:05:04+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>documentation</title>
        <link>https://wiki.qlyoung.net/documentation</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;documentation&quot;&gt;documentation&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
I strongly appreciate excellent technical documentation. You can have a great piece of software but if it&amp;#039;s difficult to understand how to use it, at best users will fail to appreciate it and at worst huge amounts of time will be wasted and vast quantities of errors committed.
&lt;/p&gt;

&lt;p&gt;
Because I appreciate good documentation for tools I use I do my best to write useful documentation. In addition to the obvious benefit, which is that good documentation saves everyone time, I find there are several additional benefits specific to the act of writing documentation:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Writing about a subject cements, organizes and structures knowledge in my mind&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; The process frequently reveals areas that I lack knowledge in or don&amp;#039;t fully understand&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Collating and structuring information often reveals performance or security issues that I hadn&amp;#039;t seen before&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; It improves my general writing skills, vocabulary and cognitive processes&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
A common pitfall I see is that people who write documentation turn very hostile towards users who ask questions covered by the documentation. I used to be this person. User asked a question? Oh HELL yeah, now I get to berate them for not reading the FUCKING MANUAL!
&lt;/p&gt;

&lt;p&gt;
This is stupid. Most users are asking questions for one of these reasons:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; They didn&amp;#039;t know where the documentation was or that it even existed&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; They could not find the information they needed in the documentation&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; The documentation was not clear&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
If you work or play in technology at all, you have likely been in each of these scenarios. Put yourself in the user&amp;#039;s shoes. In nearly every case a user asking a question is not an annoyance, it&amp;#039;s an opportunity to do two things:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1 node&quot;&gt;&lt;div class=&quot;li&quot;&gt; Improve the documentation&lt;/div&gt;
&lt;ol&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; If the user couldn&amp;#039;t find the docs, how we make it more discoverable?&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; If the information already exists but they couldn&amp;#039;t find it - how can we make it easier to find? Should we structure the docs differently?&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; If the reader couldn&amp;#039;t understand the information, this is a perfect opportunity to identify a deficiency in clarity. How can we explain it more clearly? Odds are other readers are having issues too.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li class=&quot;level1 node&quot;&gt;&lt;div class=&quot;li&quot;&gt; Improve the user&lt;/div&gt;
&lt;ol&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; If we contribute a documentation fix in response to a user concern, it demonstrates to the user that the docs are actively maintained. This makes them more likely to use them.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; For users who do not believe in writing docs for their own things, it demonstrates by example why the docs are useful&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
Now, yes, there are the users who show up, ask questions, are referred to the documentation and become combative, refuse to read it, are insulting, etc. Documentation helps us in dealing with these users as well. If the information is well documented, you don&amp;#039;t have to interact with them at all! Just refer them to the documentation and move on. You get to completely avoid interacting with toxic people.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;documentation&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;documentation&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-2905&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit2&quot; id=&quot;code_is_law&quot;&gt;&amp;#039;code is law&amp;#039;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
You probably know, or you may yourself be, a person who takes an attitude along the lines of “docs suck, code is law. &lt;a href=&quot;https://wiki.qlyoung.net/just&quot; class=&quot;wikilink1&quot; title=&quot;just&quot; data-wiki-id=&quot;just&quot;&gt;just&lt;/a&gt; read the code lol.”
&lt;/p&gt;

&lt;p&gt;
There&amp;#039;s a lot of potential things that can be going on in this person to cause them to take this view:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; They suck at writing and therefore don&amp;#039;t want to do it. This is the case for many, many people. Writing good, clear documentation is not a natural skill for most. You need to develop it.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; They have an ego and it makes them feel superior to imply that you lack skilz because you can&amp;#039;t efficiently comprehend an entire software package in an hour or two by simply reading the code. This is very common in junior engineers and people who are good at computers but lack well rounded social skills.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; They have been burned by out of date or poor documentation in the past and reacted to this by distilling the experience into the opinion “docs = bad”&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; They know that documentation takes a lot of time and value either the experience or the result (or both) of technical work more, and therefore need a reason to argue against producing documentation if questioned&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
I think all of these are bull.
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; If you suck at writing - this is an opportunity to get better&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; If you feel the need to flex on people - grow up&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; If you have been burned by documentation - who said you had to make bad docs? Be the change.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; If you want to write code - I feel for you. Don&amp;#039;t you want people to use that code?&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
The last point is important - if you have zero interest or responsibility for anyone else to use what you&amp;#039;re writing, by all means, do not write docs if you don&amp;#039;t want to. I am not saying that you need to document everything you make. But for things you intend other people to use, writing docs will make them love you. If you do it professionally, I will double down and claim that competency in technical writing is part of being a well rounded and competent engineer.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;&amp;#039;code is law&amp;#039;&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;code_is_law&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;2906-4865&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit3&quot; id=&quot;culture&quot;&gt;culture&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
In a professional context, company culture frequently does not incentivize documentation. Technical output is rewarded. Documentation output is not. This is extremely common, but it is bad for the organization as a whole. Suppose you commit an engineer to building out a complex system, but there are no cultural incentives for the engineer to write good documentation, so they don&amp;#039;t. How many man hours are subsequently wasted by users who are forced to reverse engineer that system in order to use it? Probably a lot, right? Imagine if the engineer was rewarded for writing good documentation and did so. Yes, the engineer spent time writing documentation when they could have been doing technical work. But how many hours are now saved for other users?
&lt;/p&gt;

&lt;p&gt;
Of course, some teams are explicitly aware of that tradeoff. Externalizing cost to other teams means your team can be more productive - at the cost of the org. Dsyfunctional workplaces incentivize this behavior.
&lt;/p&gt;

&lt;p&gt;
In extreme cases, this can result in the entire product being rebuilt by a different person or team because no one can understand how to use the first product.
&lt;/p&gt;

&lt;p&gt;
There is a service I interact with that has an HTTP &lt;abbr title=&quot;Application Programming Interface&quot;&gt;API&lt;/abbr&gt;. Said &lt;abbr title=&quot;Application Programming Interface&quot;&gt;API&lt;/abbr&gt; requires authentication. This service is maintained by a team. Yet, nobody has bothered to write down how to authenticate to this service. It&amp;#039;s extremely simple, so simple you can explain it with just two lines of example shell code. But nobody has taken the 2 minutes to locate an appropriate place to write that down, jot it down and provide a couple notes on it.
&lt;/p&gt;

&lt;p&gt;
What are the consequences of this? Now every time someone wants to use that &lt;abbr title=&quot;Application Programming Interface&quot;&gt;API&lt;/abbr&gt;:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; The user must locate an appropriate channel (email, slack, etc) to ask their question&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Ask their question and hope a knowledgeable person (KP) sees it&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Said KP must now explain how to do it - probably not for the first time (!)&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
This entire transaction is repeated countless times. Consider the alternative:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; User asks a question&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; KP sees the question and instinctively reaches for the docs to give the user&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; KP realizes there are no docs. KP spends 2 minutes creating a docs page, writes down the answer, gives it to the user.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
Now the user has that docs page to share with anyone facing the same problem and KP can spend less time answering that question in the future. This saves everybody time and improves morale.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;culture&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;culture&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;4866-7272&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit4&quot; id=&quot;tools&quot;&gt;tools&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
I&amp;#039;m not going to talk about what docs tools I like here. Actually I am going to talk about the opposite.
&lt;/p&gt;

&lt;p&gt;
Maxim:
&lt;/p&gt;
&lt;blockquote class=&quot;blockquote-plugin&quot;&gt;
&lt;p&gt;
Docs are worthless if nobody writes them.&lt;cite class=&quot;blockquote-plugin&quot;&gt;Me&lt;/cite&gt;
&lt;/p&gt;

&lt;/blockquote&gt;
&lt;p&gt;
Yep. You know how you get people to write docs? I can tell you that it is not by pointing them at a `docs/` directory with instructions to just make a PR. It is by making it as frictionless, painless, as easy as possible to edit the docs. This is triply true in organizations where people are not rewarded for writing docs. Which is most of them, at least in my experience.
&lt;/p&gt;

&lt;p&gt;
If I have to make a git commit to edit docs, the platform is DOA. If I cannot drag and drop a screenshot into the editor, the platform is DOA. If I have to learn a syntax to edit docs - even if it is Markdown - the platform is DOA. If it takes me more than 5 seconds or so to make an edit or add a screenshot or correct a typo, the platform is DOA.
&lt;/p&gt;

&lt;p&gt;
I say this as someone who built &lt;a href=&quot;https://docs.frrouting.org/&quot; class=&quot;urlextern&quot; title=&quot;https://docs.frrouting.org/&quot; rel=&quot;ugc nofollow&quot;&gt;https://docs.frrouting.org/&lt;/a&gt;. These docs are, technologically, fantastic. reStructuredText is an amazing markup system for technical documentation and has rich structural capabilities with cross referencing, glossaries, syntax highlighting, parsers, et cetera. It can be compiled into any format you want - PDF, &lt;abbr title=&quot;HyperText Markup Language&quot;&gt;HTML&lt;/abbr&gt;, manpage, transpiled into almost any other markup. It&amp;#039;s version controlled in git and as a plain text format you can use all the great tooling you use with code to view it, diff it, edit it. From a technical perspective it&amp;#039;s really close to the best system out there. Yet the FRR docs are years out of date in most areas. Do you know why? It&amp;#039;s because to edit these docs you need to:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Know reStructuredText&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Make a git commit&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Submit a PR.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
That&amp;#039;s it. Each of these things individually adds so much friction that when you put them in front of someone who is anything less than passionate about documentation, and isn&amp;#039;t paid to write it, the idea of updating the docs is going to vanish from their mind leaving a spotless void that can be filled with code or a cocktail or any number of other things.
&lt;/p&gt;

&lt;p&gt;
Now put this person in front of a webpage with an clearly visible “Edit” button. That typo staring them in the face? All you have to do to fix it is click that button, delete that one extra `e` and click save. It costs nothing to do it and you get the instant satisfaction of contribution.
&lt;/p&gt;

&lt;p&gt;
Docs with low friction at least have a chance of tending towards becoming ground truth and references. Docs with high friction enter a self reinforcing cycle of decay. Nobody edits docs that are out of date. Nobody wants to polish a turd. But a fleck of dust on a beautiful object can motivate even the most depraved docs hater to brush it away.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;tools&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;tools&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:4,&amp;quot;range&amp;quot;:&amp;quot;7273-10010&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit5&quot; id=&quot;docs_pitfalls&quot;&gt;docs pitfalls&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Hitting these issues are also reasons why people form the opinion that docs are bad.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;docs pitfalls&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;docs_pitfalls&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:5,&amp;quot;range&amp;quot;:&amp;quot;10011-10123&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit6&quot; id=&quot;the_docs_are_too_detailed&quot;&gt;the docs are too detailed&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
I have seen “docs” that go into excruciating detail. Sometimes, like in formal &lt;abbr title=&quot;Application Programming Interface&quot;&gt;API&lt;/abbr&gt; specs in safety applications, this is necessary. But for many products it is not, and it will cause many problems:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; The docs will go out of date very rapidly&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Engineers will spend extreme amounts of time keeping docs up to date, impacting productivity and reducing morale (engineers enjoy building)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Users will frequently encounter incorrect documentation, lowering trust in docs overall, increasing support requests, impacting productivity&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;the docs are too detailed&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;the_docs_are_too_detailed&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:6,&amp;quot;range&amp;quot;:&amp;quot;10124-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/fall">
        <dc:format>text/html</dc:format>
        <dc:date>2024-07-02T21:59:50+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>fall</title>
        <link>https://wiki.qlyoung.net/fall</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;fall&quot;&gt;fall&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Fall is definitely my favorite season.
&lt;/p&gt;

&lt;p&gt;
I find that it&amp;#039;s the season in which I make the most personal progress. For some reason the changing leaves, the cooling weather, makes me feel like I&amp;#039;m coming out of a hazy dream. It wakes me up and prompts me to review my life.
&lt;/p&gt;

&lt;p&gt;
I also do my best work (as in, job) in the fall. In the summer it&amp;#039;s so pretty out that I hate being inside, and that distracts me. In the fall it&amp;#039;s still nice to be outside, but inside becomes the warm comfy place. This takes a psychological load off and increases my focus; I don&amp;#039;t feel like I&amp;#039;m wasting time by being inside, so I can sink into my computer-based work and flow more easily.
&lt;/p&gt;

&lt;p&gt;
The concept of “spring cleaning” applies but for me it happens in the fall. I know I&amp;#039;m about to spend a lot of time indoors so I clean out all the unnecessary junk in my place to make it as habitable as I can.
&lt;/p&gt;

&lt;p&gt;
And of course, North Carolina is a state cut out of a forest so the whole place becomes a beautiful mix of green, mute browns, soft oranges and blazing reds for two months. It&amp;#039;s really beautiful.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/1h1a0036.jpg?id=fall&quot; class=&quot;media&quot; title=&quot;1h1a0036.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/1h1a0036.jpg?w=800&amp;amp;tok=9e5a1a&quot; class=&quot;medialeft&quot; align=&quot;left&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;800&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/nature?do=showtag&amp;amp;tag=nature&quot; class=&quot;wikilink1&quot; title=&quot;tag:nature&quot; rel=&quot;tag&quot;&gt;nature&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/humanity?do=showtag&amp;amp;tag=humanity&quot; class=&quot;wikilink1&quot; title=&quot;tag:humanity&quot; rel=&quot;tag&quot;&gt;humanity&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/file_transfer">
        <dc:format>text/html</dc:format>
        <dc:date>2025-10-06T03:36:50+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>file_transfer</title>
        <link>https://wiki.qlyoung.net/file_transfer</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;file_transfer&quot;&gt;file transfer&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
File transfer sure is hard. Why is it so hard?
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;file transfer&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;file_transfer&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-75&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit2&quot; id=&quot;computer_to_computer&quot;&gt;Computer to computer&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; My favorite for 1-1 file transfer is &lt;a href=&quot;https://magic-wormhole.readthedocs.io/en/latest/welcome.html&quot; class=&quot;urlextern&quot; title=&quot;https://magic-wormhole.readthedocs.io/en/latest/welcome.html&quot; rel=&quot;ugc nofollow&quot;&gt;magic wormhole&lt;/a&gt; protocol via &lt;a href=&quot;https://github.com/Jacalz/rymdport&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/Jacalz/rymdport&quot; rel=&quot;ugc nofollow&quot;&gt;rymdport&lt;/a&gt;, although it has significant &lt;a href=&quot;https://github.com/Jacalz/rymdport/issues/165&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/Jacalz/rymdport/issues/165&quot; rel=&quot;ugc nofollow&quot;&gt;bugs&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; There is also &lt;a href=&quot;https://github.com/schollz/croc&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/schollz/croc&quot; rel=&quot;ugc nofollow&quot;&gt;croc&lt;/a&gt; although it needs a comprehensive security review (along with magic wormhole)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; For 1-to-many transfer, I usually prefer Bittorrent&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; For many-to-many, I like SFTP. Yep, good old SFTP. My preferred server is &lt;a href=&quot;https://sftpgo.com/&quot; class=&quot;urlextern&quot; title=&quot;https://sftpgo.com/&quot; rel=&quot;ugc nofollow&quot;&gt;SFTPGo&lt;/a&gt;. It supports S3-compatible cloud storage as a storage backend and allows you to mount as many of those as you want in a virtual filesystem scoped to specific users. Very good. I host it at &lt;a href=&quot;https://files.qlyoung.net&quot; class=&quot;urlextern&quot; title=&quot;https://files.qlyoung.net&quot; rel=&quot;ugc nofollow&quot;&gt;https://files.qlyoung.net&lt;/a&gt;.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Honestly, I hate saying it, but dropbox works fine too&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Computer to computer&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;computer_to_computer&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;76-951&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit3&quot; id=&quot;phone_to_phone&quot;&gt;Phone to phone&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
If it&amp;#039;s iOS to iOS and just 1-to-1 or 1-to-many, Airdrop works well. Many-to-many quickly becomes unmanageable.
&lt;/p&gt;

&lt;p&gt;
For cross-platform iOS ↔ Android transfers, I usually reach for &lt;a href=&quot;https://snapdrop.net/&quot; class=&quot;urlextern&quot; title=&quot;https://snapdrop.net/&quot; rel=&quot;ugc nofollow&quot;&gt;snapdrop&lt;/a&gt;. However, for transfers of more than 1gb or so, it starts to get sketchy with phone lock timeouts and background task kills frequently resulting in failed transfers.
&lt;/p&gt;

&lt;p&gt;
I&amp;#039;m not aware of any reliable and broadly available tool that exists in this space. A magic wormhole client for mobile would be amazing. Do you know of one?
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Phone to phone&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;phone_to_phone&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;952-1516&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit4&quot; id=&quot;lol&quot;&gt;Lol&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
I made this page with its name before seeing this appropriate XKCD: &lt;a href=&quot;https://xkcd.com/949/&quot; class=&quot;urlextern&quot; title=&quot;https://xkcd.com/949/&quot; rel=&quot;ugc nofollow&quot;&gt;https://xkcd.com/949/&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
At least I&amp;#039;m not alone.
&lt;/p&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/technology?do=showtag&amp;amp;tag=technology&quot; class=&quot;wikilink1&quot; title=&quot;tag:technology&quot; rel=&quot;tag&quot;&gt;technology&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Lol&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;lol&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:4,&amp;quot;range&amp;quot;:&amp;quot;1517-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/gambling">
        <dc:format>text/html</dc:format>
        <dc:date>2024-07-02T21:55:34+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>gambling</title>
        <link>https://wiki.qlyoung.net/gambling</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;gambling&quot;&gt;gambling&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
In October 2023 I went to Las Vegas for the first time as an adult, and was introduced to gambling. Up to that point I&amp;#039;d never &lt;em&gt;really&lt;/em&gt; gambled. Poker with friends didn&amp;#039;t really count, it just added some stakes to increase the fun and retain interest in the cards. Risky investments, like shorting $DHI and buying the $BLZE IPO, were the closest to Vegas I got prior to going.
&lt;/p&gt;

&lt;p&gt;
Vegas gambling was really interesting and novel to me. Trying a variety of games in a variety of casinos, with a variety of friends and around a bunch of types of strangers, showed me a lot of different reasons that people gamble. It also served as an exploration of my own approach to risk. I actually found Vegas to be beneficial because I left with a better understanding of what risk profiles I enjoy. Aside from the gambling itself, there were also things I enjoyed getting exposure to that happen adjacent to gambling.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;gambling&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;gambling&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-928&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit2&quot; id=&quot;gambling_adjacent_elements&quot;&gt;gambling adjacent elements&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
There&amp;#039;s a couple elements adjacent to Vegas gambling that I enjoy, some of which are:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Socializing / hanging out with friends&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Free drinks&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; The sheer spectacle of the setting&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
I had the most fun in Vegas sitting around the machine craps or standing around the real craps table with friends, all of us engaged in the outcome of a game of chance, while being served free drinks with a DJ playing a set. Socializing is good. Free drinks are fun, to the extent that they taste good and drinking is enjoyable. And it&amp;#039;s exciting to take risks together!
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;gambling adjacent elements&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;gambling_adjacent_elements&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;929-1526&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit3&quot; id=&quot;gambling_elements&quot;&gt;gambling elements&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
The casino offers games for every risk appetite.
&lt;/p&gt;

&lt;p&gt;
After checking into the hotel me and a friend went to a casino in search of a pai gow poker table. I didn&amp;#039;t know what this was. My friend explained that he likes pai gow because in pai gow money moves slowly. Most of the time the outcome is a push (no win or loss). This allows you to play for a long time, creating an experience that emphasizes hanging out, drinking, with some element of risk to add excitement.
&lt;/p&gt;

&lt;p&gt;
After gambling for a week in vegas and then doing a bit online as well, I have arrived at a similar conclusion. For me, gambling is most enjoyable when the outcome is mostly neutral and you can play for free.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;gambling elements&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;gambling_elements&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;1527-2232&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit4&quot; id=&quot;online_gambling_vs_casino_gambling&quot;&gt;online gambling vs casino gambling&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
After returning from Vegas, I tried a few different online gambling experiences.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4 id=&quot;vegas_infinite&quot;&gt;vegas infinite&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
The reason I was in Vegas was to attend a demo event for an upcoming VR game being developed by a studio that most of my friends work at. Thus, all of us are VR enthusiasts and when we got home we started looking for a way to replicate the casino experience in VR. There&amp;#039;s a game called Vegas Infinite that implements a virtual casino; the available games include slots, blackjack, poker, crash, roulette, and craps. You get a random amount of the in-game currency each day for free. We played that for a while but after some time we all stopped. The reason I personally stopped was that while I enjoyed the social aspect, after playing virtual craps for a few hours there was no excitement in it. Mentally the wins and losses didn&amp;#039;t mean anything, the currency is totally useless anywhere outside the game. You can&amp;#039;t even use it to purchase cosmetics in the game. It&amp;#039;s just a number that goes up and down. If the game allowed you to purchase cosmetics using your winnings that might offer some engagement, but it doesn&amp;#039;t. The virtual currency is completely worthless even in virtual terms, so any games that use it have absolutely zero stakes. The social aspect became boring because the focus of the socializing was on the worthless virtual currency. It&amp;#039;s not like we were having conversations about something else and just using the virtual gambling as an excuse to occupy the same virtual space. I still log in occasionally to collect the daily currency drop, just in case we ever want to play poker as a group or something, but that game isn&amp;#039;t interesting at all.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4 id=&quot;crypto_casinos&quot;&gt;crypto casinos&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
&lt;em&gt;This section is entirely hypothetical, as I would never actually engage in cryptocurrency gambling. It&amp;#039;s just how I imagine it would go if I were to do such a thing, and is written in that style.&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
There are various websites that implement cryptocurrency gambling. These are only available outside the US but are accessible via VPN. After Vegas Infinite became boring, and having a few hundred USD worth of various crypto that I haven&amp;#039;t touched in years, I decided to try gambling with it to see how it made me feel. Games I tried included dice, plinko, and a simplified version of roulette.
&lt;/p&gt;

&lt;p&gt;
At first this was fun. Gambling with a fungible asset means there&amp;#039;s actually some stakes involved, inasmuch as winning increases your ability to get actual stuff, and losing decreases it. This resulted in being more invested in the outcome, making the game more exciting. However, after a while this too lost its luster. After some reflection I think this happened for two reasons:
&lt;/p&gt;

&lt;p&gt;
1. The amounts I gambled with are completely immaterial to me. The maximum I ever staked on any one bet was $10 with a maximum payout of something like $30. I spend $30 on curry delivery without a second thought, I really don&amp;#039;t care about those amounts. The maximum I ever had deposited in the site wallet was $300 - even if this was stolen by the site operators I would not care. Ultimately there was still not anything real at stake.
&lt;/p&gt;

&lt;p&gt;
2. Losing streaks hurt more and were more tangible than the excitement gained by winning streaks. Winning gave a brief dopamine hit but similar to cigarettes and poppers, the most enjoyable part was the microsecond after winning, and then the feeling was gone. I&amp;#039;ve learned through life experience that this sort of pleasure is ultimately more of a drain than anything else, and that knowledge plus the knowledge that house edge meant that a net loss was imminent made me unable to enjoy it. Personally I think that to get any concrete enjoyment out of pure gambling - meaning the activity itself without elements like free drinks, socializing, etc - you have to lack a visceral understanding of house edge. If you understand house edge then you know in your core that all wins are just local fluctuations in a graph that trends down, and that sucks.
&lt;/p&gt;

&lt;p&gt;
3. Lack of any of the social elements or perks like free drinks or music meant that only the core activity of gambling remained.
&lt;/p&gt;

&lt;p&gt;
That said, I still enjoy short sessions of online gambling on games where most of the time you push. This minimizes the negative feelings of a loss, while still retaining the excitement of volatility, and rewards you with the excitement of the occasional win. You still lose in the long term, but there&amp;#039;s a sweet spot where you can gamble for 2 hours straight with a $20 stake and walk away with $10 that makes spending that $10 a worthwhile price for the 2 hours of entertainment. &lt;sup&gt;&lt;a href=&quot;#fn__1&quot; id=&quot;fnt__1&quot; class=&quot;fn_top&quot;&gt;1)&lt;/a&gt;&lt;/sup&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;online gambling vs casino gambling&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;online_gambling_vs_casino_gambling&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:4,&amp;quot;range&amp;quot;:&amp;quot;2233-7034&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit5&quot; id=&quot;conclusions&quot;&gt;conclusions&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
All of these experiences together led me to my personal viewpoint of gambling, which is that it&amp;#039;s a good way to do two things:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Enjoy the company of people&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Be entertained by volatility in a controlled manner&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
Gambling with the intention to win money is a fool&amp;#039;s errand. That is not an opinion, it&amp;#039;s just a fact. Playing any game with a house edge where your enjoyment is predicated on winning is a waste of life. As a shared activity, played responsibly, it can be a great way to socialize. And played alone, responsibly, it can be a fun diversion not too dissimilar from playing a video game.
&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;
As a random thought tacked onto the end here, I bet that some people would be better off gambling than engaging in activities that are actually gambling but given the label of investing. For example, some options trades are realistically just pure gambling, but because it&amp;#039;s done on an asset market where people also make rational investments it lets people rationalize the behavior by calling it investing. Such behavior might be less destructive if the label on it was “gambling”, with all the social trappings and pressures (ie, expectations of appropriate risk management) that come along with that. But then, either you know you&amp;#039;re gambling or you don&amp;#039;t, and if you&amp;#039;re self aware enough to acknowledge that you&amp;#039;re gambling maybe you&amp;#039;re better off doing it on the market where it&amp;#039;s legal and where you&amp;#039;re less likely to be judged for engaging in it.
&lt;/p&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/gambling?do=showtag&amp;amp;tag=gambling&quot; class=&quot;wikilink1&quot; title=&quot;tag:gambling&quot; rel=&quot;tag&quot;&gt;gambling&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/philosophy?do=showtag&amp;amp;tag=philosophy&quot; class=&quot;wikilink1&quot; title=&quot;tag:philosophy&quot; rel=&quot;tag&quot;&gt;philosophy&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;conclusions&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;conclusions&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:5,&amp;quot;range&amp;quot;:&amp;quot;7035-&amp;quot;} --&gt;&lt;div class=&quot;footnotes&quot;&gt;
&lt;div class=&quot;fn&quot;&gt;&lt;sup&gt;&lt;a href=&quot;#fnt__1&quot; id=&quot;fn__1&quot; class=&quot;fn_bot&quot;&gt;1)&lt;/a&gt;&lt;/sup&gt; 
&lt;div class=&quot;content&quot;&gt;Note: shortly after completing this article I hypothetically bet all of the remaining $150 from my initial $300 on a single dice roll and lost. Draw your own conclusions.&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/insurance">
        <dc:format>text/html</dc:format>
        <dc:date>2025-10-06T03:33:19+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>insurance</title>
        <link>https://wiki.qlyoung.net/insurance</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;insurance&quot;&gt;insurance&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Oh, uh, none of this is legal advice, I&amp;#039;m not a lawyer and hold no credentials associated with insurance, etc.
&lt;/p&gt;

&lt;p&gt;
Now I know what you&amp;#039;re thinking. This is so lame, this isn&amp;#039;t the culture, look at this loser using his personal syndication platform to write about boring ass insurance of all things. I&amp;#039;m gonna go read a phrack or gwern or the new yorker or whatever it is you read.
&lt;/p&gt;

&lt;p&gt;
You&amp;#039;re right. Anyway, on to the article.
&lt;/p&gt;

&lt;p&gt;
In May 2024, prompted by something random, I did a complete review of all my insurance policies (except for medical) and learned a lot of things about insurance which I should have learned about 10 years ago. Here I&amp;#039;ve jotted down some of what I&amp;#039;ve learned in the hopes that maybe it will prompt someone else who was similarly uninformed to learn these things.
&lt;/p&gt;

&lt;p&gt;
In short: &lt;strong&gt;If you are American and most of the contents of this page are new to you, you are probably at risk of having your life ruined and should learn about insurance&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
Being underinsured is very dangerous in the US. It&amp;#039;s one of those risks that will probably never materialize into a real problem but if it does, it can ruin your life without recourse, game over. Here in the US we love having these little life-ruining booby traps sprinkled around, that way everyone has to spend time thinking about them instead of pursuing happiness or whatever.
&lt;/p&gt;

&lt;p&gt;
All of the information here is available online, but here&amp;#039;s some rough notes on what insurances the average American might want and what they cover. Not legal advice.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4 id=&quot;terms_and_abbreviations&quot;&gt;Terms and abbreviations&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; BI - bodily injury&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; PD - property damage&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; UM - uninsured motorist&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; UIM - underinsured motorist&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;insurance&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;insurance&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-1672&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit2&quot; id=&quot;general_thoughts_opinions&quot;&gt;General thoughts &amp;amp; opinions&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
You could also call this the “insurance is fucking boring, give me a tl;dr” section.
&lt;/p&gt;

&lt;p&gt;
I am going to make some blanket statements here that none of the websites that talk about this will make, which will certainly not be true for some people, but will likely be true for most people. Please understand that these are only my opinions and you need to fully understand your own situation to make accurate judgements. Do not believe what you read here.
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; If your liability limits on your auto policy are at or below 100/200/50 for both BI/PD and you have no umbrella policy, you are likely underinsured&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; If you do not understand the previous bullet point you are likely underinsured&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; If you don&amp;#039;t have an umbrella policy, you are likely underinsured&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Basically every person whose financial situation lies somewhere between “destitute” and “billionaire” should have umbrella insurance&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Let&amp;#039;s see, what else…
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Low assets does not make you safe from huge liability&lt;/strong&gt;. “Good luck getting blood from a stone” - wrong. Your wages can be garnished for the rest of your life turning you into a slave laborer. Imagine if you had just paid for insurance instead!&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;General thoughts &amp;amp; opinions&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;general_thoughts_opinions&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;1673-2883&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit3&quot; id=&quot;auto&quot;&gt;Auto&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Auto insurance pays out in various situations involving motor vehicles. The main categories you will find offered by an insurance company are:
&lt;/p&gt;
&lt;div class=&quot;table sectionedit4&quot;&gt;&lt;table class=&quot;inline&quot;&gt;
	&lt;thead&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;th class=&quot;col0&quot;&gt; Name &lt;/th&gt;&lt;th class=&quot;col1&quot;&gt; When it pays out &lt;/th&gt;&lt;th class=&quot;col2&quot;&gt; Relative cost &lt;/th&gt;
	&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tr class=&quot;row1&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; Property damage liability &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; You are responsible for paying for damage to someone else&amp;#039;s property &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; Expensive &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row2&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; Bodily injury liability &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; You are responsible for paying for someone&amp;#039;s medical costs or lost wages &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; Expensive &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row3&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; Collision &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Your vehicle is damaged by collision with something that is not covered by someone else&amp;#039;s insurance &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; Expensive &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row4&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; Comprehensive &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Your vehicle is damaged by something other than a collision (e.g. hail) &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; Moderate &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row5&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; Uninsured/underinsured motorist property damage (UMPD) &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Your property is damaged by someone and that person&amp;#039;s &amp;#039;Property damage liability&amp;#039; limit for their auto insurance is not enough to cover the damage &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; Cheap &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row6&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; Uninsured/underinsured motorist bodily injury (UMBI) &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; You are injured by someone and that person&amp;#039;s &amp;#039;Bodily injury liability&amp;#039; limit for their auto insurance is not enough to cover your medical costs &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; Cheap &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row7&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; Medical benefit &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; You have medical costs which are not covered by any of the above but still involve a vehicle &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; Cheap &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row8&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; Transportation expenses &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Your car is unavailable due to a covered event and you need to rent a vehicle &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; Cheap &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;table&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;table&amp;quot;,&amp;quot;secid&amp;quot;:4,&amp;quot;range&amp;quot;:&amp;quot;3046-4214&amp;quot;} --&gt;
&lt;p&gt;
General notes:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Which specific coverages are required and at what levels, as well as other details, varies widely state by state&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Insurance for things which are under your control is expensive. Insurance for things which are out of your control is cheap.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; To what degree a motor vehicle has to be involved in order for auto insurance to pay out depends on your specific policy, which you should read. The devil is in the details. For example, suppose you are a pedestrian and hit by an uninsured driver. What insurance is going to cover your medical costs? 🤔&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Specific notes:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Many insurers require that bodily injury liability and property damage liability have the same limits&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Medical benefit - this one kicks in before your medical insurance deductible for very specific vehicle-related situations. For example, you are standing on your car in order to clean your gutters and fall off - your medical insurance would cover the medical costs, but the medical benefit from your auto policy could pay out to cover your medical deductible.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
UMBI/UMPD warrant their own point. Why do you need to pay to insure yourself against someone else NOT buying insurance? That doesn&amp;#039;t seem fair. Well, it isn&amp;#039;t fair. Fortunately it&amp;#039;s cheap. The reason it&amp;#039;s cheap is that after covering you, the insurance company turns around and pursues every option available to them to recoup that money from the party with insufficient insurance.
&lt;/p&gt;

&lt;p&gt;
In May 2024 I noticed my auto insurance policy had a limit of $50k USD for property damage liability coverage. That&amp;#039;s the coverage that pays out if you are liable for damage to someone else&amp;#039;s property (usually their car). This seemed rather low since many cars on the road are themselves worth more than $50k, and even a car worth less than $50k may be loaded with property whose value exceeds $50k. You could also hit a house or something else and cause more than $50k of damage to it and be liable for that. In all of those cases, with just $50k of coverage, you would have to pay the remaining costs out of pocket. Depending on the liability amount and your financial situation that could range from annoying to life ruining.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Auto&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;auto&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;2884-6386&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit5&quot; id=&quot;renter_s&quot;&gt;Renter&amp;#039;s&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Renter&amp;#039;s insurance pays for things like your apartment flooding and destroying your collection of fine paintings and your gaming computer. My understanding is that home owner&amp;#039;s insurance is similar but different and has provisions specific to things that can happen to owned or mortgaged homes. If you own a home and are reading this to learn about homeowner&amp;#039;s insurance, please stop.
&lt;/p&gt;
&lt;div class=&quot;table sectionedit6&quot;&gt;&lt;table class=&quot;inline&quot;&gt;
	&lt;thead&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;th class=&quot;col0&quot;&gt; Name &lt;/th&gt;&lt;th class=&quot;col1&quot;&gt; When it pays out &lt;/th&gt;&lt;th class=&quot;col2&quot;&gt; Relative cost &lt;/th&gt;
	&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tr class=&quot;row1&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; Personal property &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Property in your rented home is damaged or destroyed &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; Moderate &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row2&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; Loss of use &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; You incur costs above your normal living costs as a result of not being able to use resources normally available in your property &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; Moderate &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row3&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; Personal liability &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Someone is injured in your home (or their property is damaged) and you are liable &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt;&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row4&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; Medical payments &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; You have medical costs and have to pay an out of pocket deductible &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt;&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;table&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;table1&amp;quot;,&amp;quot;secid&amp;quot;:6,&amp;quot;range&amp;quot;:&amp;quot;6795-7282&amp;quot;} --&gt;
&lt;p&gt;
Frankly I don&amp;#039;t understand the fine points of renter&amp;#039;s all that well and need to read more about it.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Renter&amp;#039;s&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;renter_s&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:5,&amp;quot;range&amp;quot;:&amp;quot;6387-7385&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit7&quot; id=&quot;umbrella&quot;&gt;Umbrella&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Prior to my grand review I did not understand umbrella insurance. Now that I understand I am of the mind that nearly everyone should have it.
&lt;/p&gt;

&lt;p&gt;
In short, umbrella insurance covers you for liabilities in excess of any other policy. Suppose you crash your &amp;#039;99 Camry into a McLaren Artura (about $300k), totaling it, and you have $150k of PD liability coverage on your auto policy. After that pays out you are still liable for $150k. If you do not have an umbrella policy you are screwed and must come up with that $150k out of pocket. If you do have an umbrella policy, your umbrella policy kicks in after your auto policy has hit its limit and covers the excess (up to the umbrella policy&amp;#039;s maximum), and you pay nothing out of pocket. So in this case the first $150k would pay out from your auto policy and your umbrella policy would pay the remaining $150k.
&lt;/p&gt;

&lt;p&gt;
You are probably thinking, wow, that sounds very nice. It must be expensive, right? Nope! When I got quotes for them recently, an average $1M umbrella policy was around $150/yr. Relative to an unexpected 5 or 6 figure out of pocket expense, that is extremely cheap. That is why I think most people should have an umbrella policy.
&lt;/p&gt;

&lt;p&gt;
Other notes:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Umbrella policies are usually purchased in $1M increments&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; They typically require that you have certain minimum coverage levels in auto and renters/homeowners policies before you can get one - these minimums can vary widely, shop around&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; They only pay out when you are liable for something&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Umbrella&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;umbrella&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:7,&amp;quot;range&amp;quot;:&amp;quot;7386-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/iphone">
        <dc:format>text/html</dc:format>
        <dc:date>2024-08-24T22:09:16+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>iphone</title>
        <link>https://wiki.qlyoung.net/iphone</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;iphone&quot;&gt;iphone&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
Bit of a pace change here, I don&amp;#039;t particularly care about consumer electronics but mobile computers aka phones are an exception since for better or worse they&amp;#039;re inextricably tied up with much of my life.
&lt;/p&gt;

&lt;p&gt;
After being a longtime user of Pixel devices (which pairly nicely with Google Fi cell service), the screen on my Pixel 4a broke in June 2022. At the same time I went to Yosemite with some friends and noticed that the pictures they took with their iPhones looked a lot better than the pictures I&amp;#039;ve taken with any Android phones. I was always skeptical of iPhone&amp;#039;s ostensibly restrictive user experience (relative to Android), but being rather bored with technology and not finding any really good Android phones on the market (where are the microsd slots?) decided to pick up an iPhone. Apple has a decent 14 day return policy, so I figured I&amp;#039;d try it out and if the device annoyed me I&amp;#039;d just return it and get whatever the current number pixel is. In short, I&amp;#039;ve been pleasantly surprised by iPhone and am keeping it. I bought the iPhone 13 Mini with 512gb of storage.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;iphone&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;iphone&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-1101&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit2&quot; id=&quot;preconceptions&quot;&gt;Preconceptions&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
My main hangups coming into iPhone numbered three:
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Preconceptions&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;preconceptions&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;1102-1181&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit3&quot; id=&quot;no_filesystem_access&quot;&gt;No filesystem access&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
I&amp;#039;d vaguely heard that it is difficult to access iPhone&amp;#039;s filesystem. This is one of the things that kept me from seriously looking into iPhone in the past. I know how computers work, I know consumer computers have a filesystem that has files on it, one of the main things I do with computers is work with files on the filesystem. I&amp;#039;m generally not interested in learning abstractions over the filesystem and if I&amp;#039;m prevented from doing things I normally do with the filesystem, like moving files around, that&amp;#039;s a nonstarter for me. Turns out this concern was misplaced. The iOS concept of the filesystem is more complicated than the one on Android, but from a functional perspective I can still accomplish the same tasks. I can mount various parts of the iPhone filesystem on my Arch laptop via a FUSE module and get to the files I need to get to. On iOS, apps apparently have their own private filesystems, which is kind of a neat concept and makes perfect sense from an application sandboxing perspective. These can also be mounted on Linux via FUSE. I can copy files to and from application filesystems and I can copy to and from the directories iOS exposes for use as mass storage.
&lt;/p&gt;

&lt;p&gt;
By far the biggest filesystem-related task I do with phones is to sync my music collection to the device. With the way I manage my music collection, I prefer to do this with &lt;code&gt;rsync&lt;/code&gt;, it&amp;#039;s simple and clean and does what I need it to. On Android I would mount the phone filesystem via &lt;code&gt;jmtpfs&lt;/code&gt; and &lt;code&gt;rsync&lt;/code&gt; my music to a directory in the filesystem; on iOS I mount the application filesystem for my music player application (VLC) using &lt;code&gt;ifuse&lt;/code&gt; and do the same thing.
&lt;/p&gt;

&lt;p&gt;
Android:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;jmtpfs ~&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;mnt&lt;/pre&gt;

&lt;p&gt;
Mounts the whole phone filesystem on &lt;code&gt;~/mnt&lt;/code&gt;. Now I can copy my music to &lt;code&gt;~/mnt/Music&lt;/code&gt; with &lt;code&gt;rsync&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
iOS:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;ifuse &lt;span class=&quot;re5&quot;&gt;-o&lt;/span&gt; allow_other &lt;span class=&quot;re5&quot;&gt;--documents&lt;/span&gt; org.videolan.vlc-ios ~&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;mnt&lt;/pre&gt;

&lt;p&gt;
Mounts VLC&amp;#039;s application filesystem on &lt;code&gt;~/mnt&lt;/code&gt;. Now I can copy my music to &lt;code&gt;~/mnt/Music&lt;/code&gt; with &lt;code&gt;rsync&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
A little more regarding application filesystems. The first thing I noticed when using iPhone was that when I wanted to save a picture from the browser to the phone, I had to choose where I wanted it to end up. The two options iOS presents in are &lt;code&gt;Files&lt;/code&gt; and &lt;code&gt;Photos&lt;/code&gt;. This is strange, conceptually I am saving a file, so the distinction between these two things isn&amp;#039;t clear. After I figured out the application filesystem thing this makes a lot more sense; choosing &lt;code&gt;Photos&lt;/code&gt; saves the picture to the &lt;code&gt;Photos&lt;/code&gt; application&amp;#039;s filesystem, saving it to &lt;code&gt;Files&lt;/code&gt; saves it to the &lt;code&gt;Files&lt;/code&gt; application&amp;#039;s filesystem. This seemed like a bizarre abstraction over the very simple concept of a filesystem until I realized that this ties into how iOS does application sandboxing.
&lt;/p&gt;

&lt;p&gt;
iOS doesn&amp;#039;t work like Android where an app either has access to the filesystem or it doesn&amp;#039;t; on iOS you grant individual apps access to specific paths in the filesystem. There also seems to be a concept of apps asking other apps for permission to access parts of their app filesystems, although this may be the same thing (not sure as I haven&amp;#039;t done any iOS development). This is awesome from a security perspective and is something I didn&amp;#039;t know I needed until I used it. For example, if I want to send a picture to a channel in Discord on Android, I open the Discord file picker and all photos on my phone are visible from within the application; i.e. Discord has unfettered access to the filesystem. A malicious or buggy Discord could choose to send any picture it wanted, there&amp;#039;s no access controls preventing it from doing that. Contrast with iOS; I open the Discord file picker, and by default I see no photos. Instead there&amp;#039;s iOS UX there that allows me to grant Discord access to *specific* photos. Once I grant access, then Discord allows me to pick from the photos I&amp;#039;ve granted it access to. This is a huge deal. This is absolutely how it should be. Filesystem access should be heavily locked down and mediated by the operating system on mobile devices. In retrospect Android is extremely primitive in this regard. It&amp;#039;s difficult to explain in text without seeing how it works in practice, but once I used it, I had a moment of “ah, I get it now” and was sold on it.
&lt;/p&gt;

&lt;p&gt;
One other note on this; I&amp;#039;d heard people say “oh but iOS has the Files app now” in response to concerns of restricted filesystem access. It does indeed have a Files app, and this app is interesting. It&amp;#039;s a surprisingly comprehensive file management app. It allows you to access select application filesystems - it seems to be limited to ones that apps choose to expose - and some other parts of the filesystem, although the parts that are exposed are clearly curated by iOS. There&amp;#039;s obviously no view of the root filesystem. Functionally it feels like porcelain over a bunch of bind mounts or a virtual filesystem being used to present a clean, access controlled interface to stuff iOS has decided you should see. To be honest, I rather like it. The Files app also has the capability to mount remote shares, another pleasant surprise; I was able to add my home Samba server easily and played around with copying music files from it into the VLC app filesystem using just the Files app, which actually worked.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;No filesystem access&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;no_filesystem_access&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;1182-6488&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit4&quot; id=&quot;no_webm_support&quot;&gt;No webm support&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
I watch a lot of webms. For some reason - I really don&amp;#039;t care about the justification - Apple has decided webm is not an ordained format and refuses to implement a system codec for it.
&lt;/p&gt;

&lt;p&gt;
Yeah, this one kind of sucks. Webms just don&amp;#039;t load or present as file downloads instead of embedded playback in places where they&amp;#039;d normally embed as videos on Android. Installing VLC and opening the file in VLC works well enough to live with it, but still, this is stupid and for a company that really cares about user experience, they should just implement webms. I don&amp;#039;t care if there&amp;#039;s a royalty situation or a nonfree codec situation or whatever the issue is. Apple is the world&amp;#039;s most valuable company, they can afford to pay royalties so that I can play webms on my $1000+ mobile computer that I bought from them. No excuses.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;No webm support&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;no_webm_support&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:4,&amp;quot;range&amp;quot;:&amp;quot;6489-7336&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit5&quot; id=&quot;lock-in&quot;&gt;Lock-in&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Vague concerns about being locked into the Apple ecosystem, forced to use only apps approved by Apple, integration with iCloud, etc…
&lt;/p&gt;

&lt;p&gt;
This one is also valid. Lack of first class support for sideloading is a legitimate gripe here. Functionally this means I can&amp;#039;t install Newpipe to watch YouTube without ads. I don&amp;#039;t know who the hell watches YouTube ads, but I&amp;#039;m not about to spend hours of my life watching that trash. My time is more valuable than that.
&lt;/p&gt;

&lt;p&gt;
Using YouTube frontends like Invidious in the browser works, but it&amp;#039;s not really a great experience. Notably, it is still better than the YouTube app with ads, but it&amp;#039;s worse than Newpipe where I had my own curated playlists and subscriptions managed entirely through the app (not tied to my Google account).
&lt;/p&gt;

&lt;p&gt;
I&amp;#039;m told that the data shows that people who buy iPhone are much more willing to pay money to solve problems than other people are. I imagine most people solve this problem by paying for YouTube Pro or YouTube Red or whatever the premium YouTube version is that doesn&amp;#039;t show you ads. I may end up there but not today. As a matter of principle I refuse. I also think it&amp;#039;s complete bullshit that I can&amp;#039;t sideload apps on my $1000+ computer. I paid for it, it&amp;#039;s mine, let me run my code on it. But this horse has been beat to death so many times, and Apple definitely knows that people will still use iPhone, and they&amp;#039;re right about that. I&amp;#039;m still using iPhone.
&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Lock-in&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;lock-in&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:5,&amp;quot;range&amp;quot;:&amp;quot;7337-8791&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit6&quot; id=&quot;positives&quot;&gt;Positives&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Things I like.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Positives&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;positives&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:6,&amp;quot;range&amp;quot;:&amp;quot;8792-8830&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit7&quot; id=&quot;ux&quot;&gt;UX&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Everyone knows this one. iPhone is famous for the UI being buttery smooth at all times. This is real. As much as I wish I didn&amp;#039;t, I use my phone probably a couple hours a day. Having nearly every interaction with the device be pretty much glitch/hang-free is such a tangible upgrade over Android.
&lt;/p&gt;

&lt;p&gt;
Minor things like picture-in-picture, context menus, the screenshot editor etc. all work together for an experience that is far more consistent and far more pleasant than Android. For a device that runs large swaths of my life, it&amp;#039;s a significant upgrade.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;UX&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;ux&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:7,&amp;quot;range&amp;quot;:&amp;quot;8831-9399&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit8&quot; id=&quot;pictures&quot;&gt;Pictures&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
When I first wrote this section after getting the phone, I wrote:
&lt;/p&gt;
&lt;blockquote class=&quot;blockquote-plugin&quot;&gt;
&lt;p&gt;
It is just a fact that the iPhone takes amazing pictures. Through whatever combination of camera and software, pictures on the iPhone turn out consistently better than any picture I ever took with any Android flagship - and I have owned a lot, from the Nexus 5 through the Pixel 4a. Having a wide angle camera is also awesome, I use that thing all the time.
&lt;/p&gt;

&lt;/blockquote&gt;
&lt;p&gt;
After two years of using the phone, I now hate the iPhone “look”. I think it&amp;#039;s because in that time I purchased a mirrorless camera and started doing enthusiast photography, prompting me to be much more conscious of colors, sharpening, contrast, noise and noise reduction, and all the other things you notice when you start grading your own photography. Now when I see photos produced by the stock iOS pipeline, I&amp;#039;m left wondering why they frequently look *so awful*. In some scenes the photos look good, but in many cases iOS will consistently
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; oversharpen&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; oversaturate&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; apply extreme local contrast&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
It is possible to shoot in raw on my iPhone, but doing so doesn&amp;#039;t match the type of photography I use my phone for. My phone camera is for quick snaps to capture a moment or preserve a memory when I don&amp;#039;t have my real camera, or don&amp;#039;t want to use it. Oftentimes I want to share the photos I take soon after shooting. Shooting in raw means I need to demosaic, grade, and export that photo. When I take 30 pictures in a day that is too much effort for each photo. I need the computational pipeline to produce something that looks at least OK. The extreme amount of oversharping and local contrast that the iOS pipeline produces just looks terrible.
&lt;/p&gt;

&lt;p&gt;
Here&amp;#039;s two versions of the same photo I took of a rock wall in Colorado. I shot this using &lt;a href=&quot;https://halide.cam/&quot; class=&quot;urlextern&quot; title=&quot;https://halide.cam/&quot; rel=&quot;ugc nofollow&quot;&gt;Halide&lt;/a&gt;, which produces both a raw and a HEIC that goes through the usual iOS image pipeline. I applied a neutral grade to the raw, only correcting for exposure, a little bit of chroma and a touch of contrast. Can you tell which one is the iOS photo and which is the raw? Hint: the iOS one looks like nothing that exists on planet Earth.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/ooc.jpg?id=iphone&quot; class=&quot;media&quot; title=&quot;ooc.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/ooc.jpg?w=600&amp;amp;tok=be6852&quot; class=&quot;media&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;600&quot; /&gt;&lt;/a&gt; &lt;a href=&quot;https://wiki.qlyoung.net/_detail/graded.jpg?id=iphone&quot; class=&quot;media&quot; title=&quot;graded.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/graded.jpg?w=600&amp;amp;tok=f9166a&quot; class=&quot;media&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;600&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
I guess the pipeline is optimized for what people want to see, but it looks awful to me.
&lt;/p&gt;

&lt;p&gt;
I&amp;#039;ve also noticed that iOS takes pictures of sunsets that in real life have delicate and beautiful shades of pink and maps all of the pink tones into…orange? It&amp;#039;s so egregious I genuinely believe that the pipeline detects sunset photos and applies a specific grade that crushes pinks and produces a very generic looking &lt;a href=&quot;https://tvtropes.org/pmwiki/pmwiki.php/Main/OrangeBlueContrast&quot; class=&quot;urlextern&quot; title=&quot;https://tvtropes.org/pmwiki/pmwiki.php/Main/OrangeBlueContrast&quot; rel=&quot;ugc nofollow&quot;&gt;orange and blue&lt;/a&gt; photo.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Pictures&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;pictures&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:8,&amp;quot;range&amp;quot;:&amp;quot;9400-12129&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit9&quot; id=&quot;security&quot;&gt;Security&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
I said most of what I wanted to say in the filesystem section, but yeah. The iOS security model is clearly much more mature than that of Android. At security conferences there&amp;#039;s always a talk or two about breaking iOS application sandboxing. No one gives talks like that for Android. I think that&amp;#039;s because breaking iOS is actually impressive. Nobody is surprised that Android is broken.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Security&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;security&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:9,&amp;quot;range&amp;quot;:&amp;quot;12130-12538&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit10&quot; id=&quot;magsafe&quot;&gt;MagSafe&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Wireless charging is dope, but MagSafe is the nicest system I&amp;#039;ve used out of all of them. I really like the MagSafe charger for iPhone. 
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;MagSafe&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;magsafe&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:10,&amp;quot;range&amp;quot;:&amp;quot;12539-12695&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit11&quot; id=&quot;general_build_quality&quot;&gt;General build quality&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Apple&amp;#039;s build quality is legendary and it&amp;#039;s on full display in the iPhone. Hardware silent mode switch is great.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;General build quality&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;general_build_quality&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:11,&amp;quot;range&amp;quot;:&amp;quot;12696-12842&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit12&quot; id=&quot;standard_support&quot;&gt;Standard support&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
This one actually surprised me. iOS has deeply integrated support for standard protocols and technologies into the &lt;abbr title=&quot;Operating System&quot;&gt;OS&lt;/abbr&gt;, including:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; CardDAV. Completely and seamlessly integrated into the &lt;abbr title=&quot;Operating System&quot;&gt;OS&lt;/abbr&gt;. Configured my CardDAV server in the &lt;abbr title=&quot;Operating System&quot;&gt;OS&lt;/abbr&gt; settings, contacts sync both ways. It just works. Notably it works better than the Google Contacts mess on Android.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; CalDAV. Again, configured in the &lt;abbr title=&quot;Operating System&quot;&gt;OS&lt;/abbr&gt;, and it just works. Calendar events sync both ways.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Samba. Yep. Go into the Files app, dots menu, “Connect to server”, put in a &lt;code&gt;smb://&lt;/code&gt; &lt;abbr title=&quot;Uniform Resource Locator&quot;&gt;URL&lt;/abbr&gt;. Login prompt, done. Mounted.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;abbr title=&quot;File Transfer Protocol&quot;&gt;FTP&lt;/abbr&gt;, SFTP and I believe WebDAV work same as above.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Overall the situation here is far better than what I experienced on Android.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Standard support&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;standard_support&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:12,&amp;quot;range&amp;quot;:&amp;quot;12843-13574&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit13&quot; id=&quot;negatives&quot;&gt;Negatives&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Negatives&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;negatives&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:13,&amp;quot;range&amp;quot;:&amp;quot;13575-13597&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit14&quot; id=&quot;the_media_situation&quot;&gt;The media situation&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
The iPhone media library is weird. Apparently iOS has a concept of music, videos and photos being some kind of special entities above and beyond regular files. This kind of makes sense from a historical perspective; the iPhone was originally launched as a 3-in-1 combo device, as Steve Jobs said, “A widescreen iPod with touch controls, a revolutionary mobile phone, and an internet communications device” or something like that. So the media situation on iPhone is a relic of it being conceptualized as having an iPod inside of it, and you use iTunes to manage iPods. I have fond memories from the early 2000&amp;#039;s of copying music to my iPod shuffle&amp;#039;s mass storage using Windows XP and then the songs just never playing on the iPod because I didn&amp;#039;t rebuild the database file…that&amp;#039;s still a thing on iPhone, somehow. Android has this in the bag, the way media is indexed and behaves on Android isn&amp;#039;t perfect but it&amp;#039;s better than iPhone. Media is files. I should be able to copy the files to disk and then the Music app should see them and play them, full stop. VLC for iOS is an all-in-one alternative solution to the iOS system media apps, but again, it shouldn&amp;#039;t be necessary. At the very least if Apple wants to lock users into iTunes it needs to support iTunes on all major operating systems. And Linux is a major operating system. I use Arch btw. 
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;The media situation&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;the_media_situation&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:14,&amp;quot;range&amp;quot;:&amp;quot;13598-14981&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit15&quot; id=&quot;sharing_videos&quot;&gt;Sharing videos&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;

&lt;h4 id=&quot;compression&quot;&gt;Compression&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
This one is bizarre. iOS heavily prioritizes the ability to share media. All of their commercials show users sharing pictures and videos. But what they don&amp;#039;t advertise is that it is extremely difficult to share files without incurring mandatory compression.
&lt;/p&gt;

&lt;p&gt;
You see, when you share a media file using the “Share” button from the Photos app, iOS determines whether or not the file is “too big”. If it&amp;#039;s too big, it will heavily compress the file before sending it. So for example, if I post a 4k video I took on my iPhone to a channel in Discord, iOS will first compress the video before sending it to Discord. The compressed result invariably is noticeably much worse quality than the original file. Furthermore, there is no option to control this behavior. There is no toggle to disable this compression.
&lt;/p&gt;

&lt;p&gt;
This behavior is super weird. iPhone can take 4k60 video, but if you try to share that video using the builtin &lt;abbr title=&quot;Operating System&quot;&gt;OS&lt;/abbr&gt; facilities, you never get 4k60 video. You get something like HD 30 video. In fact, short of plugging your phone into your computer, there is no way to get the full quality file out of the device! This makes NO SENSE! Why advertise the ability to take high quality media and then completely crush it when the user shares it?
&lt;/p&gt;

&lt;p&gt;
What Apple is doing here is prioritizing frictionless sharing over quality. Users do not want to wait 30 seconds for a 800mb video file to upload, and they don&amp;#039;t want to wait for the recipient to download it. Everything is much more responsive if the file is &amp;lt;100mb; the upload is near instant, the recipient can retrieve it faster, and the user gets the serotonin hit from the reaction sooner. So iOS compresses anything big. But this completely defeats the purpose of being able to take high quality video for me. The idea is that I&amp;#039;m supposed to be able to share everything right from my phone, right? Isn&amp;#039;t it very un-Apple to have to plug the device in and copy the file off to share it? This is exactly the sort of thing Apple explicitly designs out of its products, and yet here we are. Just give me a toggle that says I want to share the full size media file and not compress it, that&amp;#039;s enough!
&lt;/p&gt;

&lt;p&gt;
Don&amp;#039;t believe me?
&lt;a href=&quot;https://stackoverflow.com/questions/3159061/avoid-video-compression-when-selecting-movie-with-uiimagepickercontroller/5893066#5893066&quot; class=&quot;urlextern&quot; title=&quot;https://stackoverflow.com/questions/3159061/avoid-video-compression-when-selecting-movie-with-uiimagepickercontroller/5893066#5893066&quot; rel=&quot;ugc nofollow&quot;&gt;https://stackoverflow.com/questions/3159061/avoid-video-compression-when-selecting-movie-with-uiimagepickercontroller/5893066#5893066&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;

&lt;h4 id=&quot;imessage_video_length_limitations&quot;&gt;iMessage video length limitations&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
iMessage is Apple&amp;#039;s proprietary messaging protocol. Messages between iOS users are automatically upgraded to use iMessage, which adds capabilities over regular SMS like reactions, ability to share large files, and so on.
&lt;/p&gt;

&lt;p&gt;
However one thing that you cannot do is send long videos. I tried to send a 9 minute video to a friend via iMessage and received this message:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/2024-05-31_12-36.png?id=iphone&quot; class=&quot;media&quot; title=&quot;2024-05-31_12-36.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/2024-05-31_12-36.png?w=400&amp;amp;tok=650c5c&quot; class=&quot;media&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Video is too long. Note well that this does not say “Video is too big”. I could understand a file size limit. But this video is 18.4mb, very small by today&amp;#039;s standards. To check that this is an actual limit on length, I zipped the video with a resulting file size of 17.9mb and then tried sending the zip. That worked flawlessly.
&lt;/p&gt;

&lt;p&gt;
As is usually the case with looking up information on iOS online, nearly all of the explanations I found for this behavior (except &lt;a href=&quot;https://allthings.how/how-long-can-a-video-be-to-send-on-imessage/&quot; class=&quot;urlextern&quot; title=&quot;https://allthings.how/how-long-can-a-video-be-to-send-on-imessage/&quot; rel=&quot;ugc nofollow&quot;&gt;this one&lt;/a&gt; incorrectly cite maximum file size limitations as the reason “Video is too long” appears. We have just shown that it is, in fact, a length limitation and not a size limitation. There is zero technical reason I can think of that there should be a length limit on videos.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Sharing videos&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;sharing_videos&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:15,&amp;quot;range&amp;quot;:&amp;quot;14982-18589&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit16&quot; id=&quot;working_with_large_files&quot;&gt;Working with large files&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Sharing just a few files of relatively limited size works usually works flawlessly on iOS. But what if you want to share 7gb of photos? Should work exactly the same, right?
&lt;/p&gt;

&lt;p&gt;
If you try to share a large quantity of data, regardless of whether it is from the Photos app, or Files, iOS is liable to do one of the following:
&lt;/p&gt;

&lt;p&gt;
1. Nothing
2. Freeze
&lt;/p&gt;

&lt;p&gt;
Try it for yourself. Go into your Photos app and, as a test case, select every photo in your Recents album, and then hit the share button and see what happens. Odds are that nothing will happen. The usual share dialog will simply not appear, or it might show up after a few minutes. This is very bad UX. If you are going to fail, do it immediately.
&lt;/p&gt;

&lt;p&gt;
If you try this same exercise in the Files app, you may even see it freeze. Not only does the share dialog not appear, the app actually goes unresponsive. This is a native system app built and distributed by Apple, running on Apple&amp;#039;s operating system, on Apple&amp;#039;s hardware. This is exactly what Apple claims they prevent by imposing all these limitations on iOS, yet their own system applications fail miserably as soon as you put a heavy lift on them.
&lt;/p&gt;

&lt;p&gt;
This is a recurring theme. Apple has very clearly designed iOS to be optimized for handling small files, small amounts of data, with a focus on the speed of sharing rather than the quality of the shared data. Android is a very strong winner here.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Working with large files&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;working_with_large_files&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:16,&amp;quot;range&amp;quot;:&amp;quot;18590-20020&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit17&quot; id=&quot;icloud_integration&quot;&gt;iCloud integration&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Fuck off, I don&amp;#039;t want your cloud service and it shouldn&amp;#039;t be integrated into my $,$$$ computer.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;iCloud integration&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;icloud_integration&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:17,&amp;quot;range&amp;quot;:&amp;quot;20021-20148&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit18&quot; id=&quot;lightning&quot;&gt;Lightning&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
This isn&amp;#039;t a huge deal so far but I really wish it just had type c.
&lt;/p&gt;

&lt;p&gt;
&lt;em&gt;Update 07/23/22&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
Turns out this is a huge deal. When I purchased this incredibly expensive flagship smartphone which was released to market in September 2021, I assumed that it would provide a physical interface capable of supporting at least USB 3.0. I did not check this. USB 3.0 was published in 2008. Virtually every smartphone made in the past decade has had USB 3.0. Not iPhone! The lightning connector offers, unbelievably, USB 2.0. The USB 2.0 &lt;abbr title=&quot;specification&quot;&gt;spec&lt;/abbr&gt; was published in April 2000. It offers a maximum data rate of about 480 Mbit/s ≈ 60mbps. Compare to 3.0 which offers 5 Gbit/s ≈ 500mbps.
&lt;/p&gt;

&lt;p&gt;
This is absolutely irredeemable dog shit. It is unbelievable that Apple of all companies is selling computers that do not offer a 12 year old standard. If I had known this, I would not have purchased the iPhone.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Lightning&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;lightning&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:18,&amp;quot;range&amp;quot;:&amp;quot;20149-21054&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit19&quot; id=&quot;bluetooth&quot;&gt;Bluetooth&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Not even Apple can get this perfect it seems. I have multiple pairs of wireless earbuds and the iPhone seems keen on connecting to as many as it possibly can. I&amp;#039;ll be using one pair, and suddenly the audio will stop and I&amp;#039;ll hear a faint tinny sound coming from somewhere else in my domicile, because iPhone decided to connect to a different pair of headphones and route the music over there. It&amp;#039;s especially annoying when it happens on a VoIP call.
&lt;/p&gt;

&lt;p&gt;
I expected more from Apple in this department, they clearly pay obsessive attention to UX, bluetooth issues are oddly out of place.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Bluetooth&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;bluetooth&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:19,&amp;quot;range&amp;quot;:&amp;quot;21055-21659&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit20&quot; id=&quot;face_id&quot;&gt;Face ID?&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Face ID is cool. It works most of the time. The problem is that it doesn&amp;#039;t work all of the time, and in particular, it works less of the time than the fingerprint reader on my Pixel 4a did. The fingerprint reader didn&amp;#039;t work when my hands were wet, but other than that, it was much more consistent than Face ID has been.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Face ID?&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;face_id&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:20,&amp;quot;range&amp;quot;:&amp;quot;21660-22001&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit21&quot; id=&quot;price&quot;&gt;Price&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
My iPhone 13 Mini carrier-unlocked from Apple with AppleCare+ cost $1,100. That is insane.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Price&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;price&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:21,&amp;quot;range&amp;quot;:&amp;quot;22002-22110&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit22&quot; id=&quot;mids&quot;&gt;Mids&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Mids&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;mids&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:22,&amp;quot;range&amp;quot;:&amp;quot;22111-22128&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit23&quot; id=&quot;blue_bubble&quot;&gt;Blue bubble&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Ok, this is more of an anthropology piece than anything, but it&amp;#039;s kind of interesting to write about anyway. Apple has created a genius status symbol in iMessage. If two iPhone users text each other, Apple automatically upgrades the conversation to use iMessage, their encrypted messaging solution. In this case the text bubbles are blue. But if an iPhone user texts a non-iPhone user, iMessage is unavailable and the connection falls back to SMS. In this case, the iPhone user sees the messages as green.
&lt;/p&gt;

&lt;p&gt;
Because iPhones are seen as the premium choice of phone among millenials and later generations, being a “green bubble” is tantamount to failing a status symbol check. Youth refer to non-iPhone using youth as “green bubbles”. It&amp;#039;s like showing up to home room wearing gap instead of bape. Since many people retain this particular kind of lizard brain into adult life, having an iPhone can be social grease when texting such people. I don&amp;#039;t condone it but pretending it isn&amp;#039;t real is just being naïve.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Blue bubble&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;blue_bubble&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:23,&amp;quot;range&amp;quot;:&amp;quot;22129-23160&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit24&quot; id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
I&amp;#039;m keeping iPhone. Overall it is MUCH more enjoyable to use than flagship Android devices, and while I have gripes with it, I&amp;#039;ve solved all of them one way or another (except for ad-free YouTube (edit: solved with &lt;a href=&quot;https://invidious.io/&quot; class=&quot;urlextern&quot; title=&quot;https://invidious.io/&quot; rel=&quot;ugc nofollow&quot;&gt;Invidious&lt;/a&gt; + &lt;a href=&quot;https://apps.apple.com/us/app/yattee/id1595136629&quot; class=&quot;urlextern&quot; title=&quot;https://apps.apple.com/us/app/yattee/id1595136629&quot; rel=&quot;ugc nofollow&quot;&gt;Yattee&lt;/a&gt;). Security is clearly much better. It&amp;#039;s a pleasure to use because of the buttery smooth UX. Filesystem access is surprisingly good while also maintaining strong application sandboxing.
&lt;/p&gt;

&lt;p&gt;
Lack of sideloading sucks and the phone is overpriced. I wish there was a company that made a good phone.
&lt;/p&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/from_blog?do=showtag&amp;amp;tag=from_blog&quot; class=&quot;wikilink1&quot; title=&quot;tag:from_blog&quot; rel=&quot;tag&quot;&gt;from blog&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/technology?do=showtag&amp;amp;tag=technology&quot; class=&quot;wikilink1&quot; title=&quot;tag:technology&quot; rel=&quot;tag&quot;&gt;technology&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/products?do=showtag&amp;amp;tag=products&quot; class=&quot;wikilink1&quot; title=&quot;tag:products&quot; rel=&quot;tag&quot;&gt;products&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Conclusions&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;conclusions&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:24,&amp;quot;range&amp;quot;:&amp;quot;23161-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/just">
        <dc:format>text/html</dc:format>
        <dc:date>2026-04-03T03:37:03+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>just</title>
        <link>https://wiki.qlyoung.net/just</link>
        <description>
&lt;p&gt;
the word “just” is often used in response to stated problems, usually in sentences like this:
&lt;/p&gt;

&lt;p&gt;
“Why not just x the y and then you get z”
&lt;/p&gt;

&lt;p&gt;
I&amp;#039;ve observed that this is frequently counterproductive. There are a few possibilities:
&lt;/p&gt;

&lt;p&gt;
- The suggestion has already been thought of and is much harder than it seems
&lt;/p&gt;

&lt;p&gt;
In this case using the word “just” implies that the suggestion is simple, easy, anyone could do it. Usually this isn&amp;#039;t the case and using “just” both makes the suggester look dumb for trivializing the problem and annoying to the problem haver who&amp;#039;s already thought of this possibility.
&lt;/p&gt;

&lt;p&gt;
- The suggestion has not been thought of
&lt;/p&gt;

&lt;p&gt;
Why say “just”? Just say your suggestion. Using “just” makes it seem like it was obvious. Even if it is obvious, what purpose does it serve to highlight that?
&lt;/p&gt;

&lt;p&gt;
—
&lt;/p&gt;
&lt;blockquote class=&quot;blockquote-plugin&quot;&gt;
&lt;p&gt;
similar to the word obviously in a student/teacher environment
&lt;/p&gt;

&lt;/blockquote&gt;
&lt;p&gt;
someone i know
&lt;/p&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/humanity?do=showtag&amp;amp;tag=humanity&quot; class=&quot;wikilink1&quot; title=&quot;tag:humanity&quot; rel=&quot;tag&quot;&gt;humanity&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/technology?do=showtag&amp;amp;tag=technology&quot; class=&quot;wikilink1&quot; title=&quot;tag:technology&quot; rel=&quot;tag&quot;&gt;technology&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/programming?do=showtag&amp;amp;tag=programming&quot; class=&quot;wikilink1&quot; title=&quot;tag:programming&quot; rel=&quot;tag&quot;&gt;programming&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/location_history">
        <dc:format>text/html</dc:format>
        <dc:date>2025-10-10T20:15:03+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>location_history</title>
        <link>https://wiki.qlyoung.net/location_history</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;location_history&quot;&gt;location history&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Sometimes I want to see where I was at a given point in time. There&amp;#039;s many &lt;a href=&quot;https://wiki.qlyoung.net/autoarchaeology&quot; class=&quot;wikilink1&quot; title=&quot;autoarchaeology&quot; data-wiki-id=&quot;autoarchaeology&quot;&gt;sources&lt;/a&gt; of personal location history, some obvious, some less so.
&lt;/p&gt;

&lt;p&gt;
Google Timeline is particularly nice. It provides a UI to pick a calendar date and view GPS tracks on a map. But, it only shows points recorded by Google Maps. There are many more sources of location data which we can leverage. It would be nice to see those on the map too; and of course, not using Google services is always a plus.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;location history&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;location_history&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-526&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit2&quot; id=&quot;database&quot;&gt;Database&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
I want a time series of GPS coordinates. Each [timestamp, coordinate] pair should indicate where I was at that time. At time of writing I can only be one place at a time, which drastically simplifies the problem.
&lt;/p&gt;

&lt;p&gt;
Once I have that database we can leverage it for ✨ actionable insights ✨.
&lt;/p&gt;

&lt;p&gt;
To get this database, I need a thing that can ingest time series location data from any source. I mentioned these thoughts to a friend in mid 2025 and predictably he told me that several options already exist (like most good ideas I have). These include:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://traccar.org&quot; class=&quot;urlextern&quot; title=&quot;https://traccar.org&quot; rel=&quot;ugc nofollow&quot;&gt;Traccar&lt;/a&gt; - Primarily geared towards supporting hardware GPS devices, but has a robust storage framework and map view. Has a native iOS app. Points are stored in TimescaleDB which is exactly what I want&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://dawarich.app/&quot; class=&quot;urlextern&quot; title=&quot;https://dawarich.app/&quot; rel=&quot;ugc nofollow&quot;&gt;dawarich&lt;/a&gt; - I am using this now. It works but performance and stability seems poor. It has a native iOS app which is a killer feature.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://owntracks.org/&quot; class=&quot;urlextern&quot; title=&quot;https://owntracks.org/&quot; rel=&quot;ugc nofollow&quot;&gt;owntracks&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Database&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;database&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;527-1531&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit3&quot; id=&quot;sources&quot;&gt;Sources&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
See also: &lt;a href=&quot;https://wiki.qlyoung.net/autoarchaeology&quot; class=&quot;wikilink1&quot; title=&quot;autoarchaeology&quot; data-wiki-id=&quot;autoarchaeology&quot;&gt;autoarchaeology&lt;/a&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://support.strava.com/hc/en-us/articles/216918437-Exporting-your-Data-and-Bulk-Export&quot; class=&quot;urlextern&quot; title=&quot;https://support.strava.com/hc/en-us/articles/216918437-Exporting-your-Data-and-Bulk-Export&quot; rel=&quot;ugc nofollow&quot;&gt;Strava&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.garmin.com/en-US/account/datamanagement/exportdata&quot; class=&quot;urlextern&quot; title=&quot;https://www.garmin.com/en-US/account/datamanagement/exportdata&quot; rel=&quot;ugc nofollow&quot;&gt;Garmin&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://github.com/qlyoung/comma-gps-extractor/&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/qlyoung/comma-gps-extractor/&quot; rel=&quot;ugc nofollow&quot;&gt;Comma&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://support.google.com/maps/answer/6258979&quot; class=&quot;urlextern&quot; title=&quot;https://support.google.com/maps/answer/6258979&quot; rel=&quot;ugc nofollow&quot;&gt;Google Timeline&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Photo geolocation tags&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/humanity?do=showtag&amp;amp;tag=humanity&quot; class=&quot;wikilink1&quot; title=&quot;tag:humanity&quot; rel=&quot;tag&quot;&gt;humanity&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/technology?do=showtag&amp;amp;tag=technology&quot; class=&quot;wikilink1&quot; title=&quot;tag:technology&quot; rel=&quot;tag&quot;&gt;technology&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/geospatial?do=showtag&amp;amp;tag=geospatial&quot; class=&quot;wikilink1&quot; title=&quot;tag:geospatial&quot; rel=&quot;tag&quot;&gt;geospatial&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Sources&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;sources&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;1532-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/music_management">
        <dc:format>text/html</dc:format>
        <dc:date>2025-11-04T03:19:02+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>music_management</title>
        <link>https://wiki.qlyoung.net/music_management</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;music_management&quot;&gt;music management&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
I&amp;#039;m a big music enjoyer. What I&amp;#039;m not a big enjoyer of is streaming services. Maybe I&amp;#039;ll write another article about that, but the short of it is that instead of paying a company to rent temporary access to their music library,  which they pay to license from a label who in turn pays only some tiny fraction of their revenue to the artist, I prefer a more traditional model:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; you find some new music&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; you listen to it a few times to see if you like it&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; if you like it, you pay the artist for a copy&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; you own that copy&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
That way the artist gets money for making art and you pay once to own a copy of it instead of renting temporary access.
&lt;/p&gt;

&lt;p&gt;
Streaming is good for the first two steps. Finding new music is hard, and you have to listen to it a few times at different times, since it might only hit in a certain mood. Starting from music you already like and letting the recommender algorithm drive discovery is a good way to find new music. After that though, I prefer to pay the artist for their work instead of paying rent, and prefer to download the data once rather than download it every time I want to listen to music. Bandwidth is a limited resource!
&lt;/p&gt;

&lt;p&gt;
One of the benefits of owning music is that you can put it on whatever device you want and use whatever program you prefer to play it. However, if you have multiple devices then getting your music collection available on all of them becomes an exercise in file management. This article roughly depicts how I solve it.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;music management&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;music_management&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-1520&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit2&quot; id=&quot;methods&quot;&gt;Methods&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Here&amp;#039;s a chart that shows a rough outline of data flow:
&lt;/p&gt;

&lt;p&gt;
&lt;div id=&quot;mermaidContainer0&quot; style=&quot;position: relative; width:auto; height:auto&quot;&gt;&lt;span class=&quot;mermaid&quot; id=mermaidContent0 style=&quot;width:auto; height:auto&quot;&gt;
flowchart TD
    Source -.-&gt; |download| staging
    W[&quot;Windows VM&quot;]
    subgraph server[&quot;&amp;lt;em class=&#039;u&#039;&amp;gt;server&quot;]
        staging --&gt; |beet import| library
        library --&gt; W
    end
    subgraph devices[&quot;devices&amp;lt;/em&amp;gt;___&quot;]
        library -.-&gt; |syncthing| desktop((&quot;fa:fa-computer desktop&quot;)) &amp; laptop((&quot;fa:fa-laptop laptop&quot;))
        W -.-&gt; |tunefusion| phone((&quot;fa:fa-mobile-phone phone&quot;))
    end
    click phone &quot;&amp;lt;a href=&#039;https://www.dbpoweramp.com/tunefusion.htm&#039; class=&#039;urlextern&#039; title=&#039;https://www.dbpoweramp.com/tunefusion.htm&#039; rel=&#039;ugc nofollow&#039;&amp;gt;https://www.dbpoweramp.com/tunefusion.htm&amp;lt;/a&amp;gt;&quot;
&lt;/span&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;
In short:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Acquire the music from somewhere; for me, I usually buy it on &lt;a href=&quot;https://bandcamp.com/&quot; class=&quot;urlextern&quot; title=&quot;https://bandcamp.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Bandcamp&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Download it to a “staging” directory on my &lt;a href=&quot;https://wiki.qlyoung.net/personal_infrastructure&quot; class=&quot;wikilink1&quot; title=&quot;personal_infrastructure&quot; data-wiki-id=&quot;personal_infrastructure&quot;&gt;home server&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Log into the server and run &lt;code&gt;beet import&lt;/code&gt;, pointing it at the staging directory. This adds the music to my library database, cleans up tags, pulls album art, properly names the files and copies them into my “Artist/Album/&amp;lt;files&amp;gt;” library directory structure, applying my chosen file naming scheme. &lt;a href=&quot;https://beets.io/&quot; class=&quot;urlextern&quot; title=&quot;https://beets.io/&quot; rel=&quot;ugc nofollow&quot;&gt;beets slaps&lt;/a&gt;.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
The rest of the diagram depicts how the 3 devices I play my library on end up with access to my music.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Methods&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;methods&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;1521-2707&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit3&quot; id=&quot;desktop_laptop&quot;&gt;Desktop &amp;amp; Laptop&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
My server, desktop and laptop all run &lt;a href=&quot;https://syncthing.net/&quot; class=&quot;urlextern&quot; title=&quot;https://syncthing.net/&quot; rel=&quot;ugc nofollow&quot;&gt;Syncthing&lt;/a&gt;. The &lt;code&gt;music&lt;/code&gt; directory is synchronized to all devices using that.
&lt;/p&gt;

&lt;p&gt;
In the past, both of these devices mounted the directory containing music via NFS. Since they&amp;#039;re all on a &lt;a href=&quot;https://tailscale.com/kb/1136/tailnet&quot; class=&quot;urlextern&quot; title=&quot;https://tailscale.com/kb/1136/tailnet&quot; rel=&quot;ugc nofollow&quot;&gt;Tailnet&lt;/a&gt; together this worked as long as both the server and client were both online, regardless of the routing environment. This worked pretty well - latency was never an issue, and in any case I rarely needed to play from my laptop since I usually use my phone when I&amp;#039;m outside my home network. However, I didn&amp;#039;t like the network requirement and realized disk space is plentiful while bandwidth is a scarce commodity. Since I generally frown upon streaming when it isn&amp;#039;t necessary I put my money where my mouth was and switched to file synchronization.
&lt;/p&gt;

&lt;p&gt;
For playback on the computer, I prefer &lt;a href=&quot;https://tauonmusicbox.rocks/&quot; class=&quot;urlextern&quot; title=&quot;https://tauonmusicbox.rocks/&quot; rel=&quot;ugc nofollow&quot;&gt;tauon music box&lt;/a&gt;.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Desktop &amp;amp; Laptop&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;desktop_laptop&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;2708-3672&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit4&quot; id=&quot;phone&quot;&gt;Phone&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
The phone is a little trickier. Ideally I would use the same approach as for my desktop and laptop - run a sync program to keep the music library up to date on my iPhone disk. Unfortunately, that&amp;#039;s not how things work on iOS, for two reasons:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; background daemons cannot really exist on iOS&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; the concept of a filesystem that is shared between apps does not exist on iOS&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
Regarding backgrounding, in general iOS is very strict about apps performing any work in the background. Since sync programs are designed around running in the background, this more or less precludes the file synchronization strategy. It&amp;#039;s worth noting that this could be worked around using iCloud, but since I don&amp;#039;t use iCloud, that doesn&amp;#039;t work for me.
&lt;/p&gt;

&lt;p&gt;
Even if a sync program was viable on iOS, we would hit another blocker. iOS does not have the concept of a shared filesystem. Apps are only able to write to their own sandboxed filesystems. Consequently any files downloaded by a sync app would not be accessible by a music player app.
&lt;/p&gt;

&lt;p&gt;
The upshot is that to solve this problem you need a music playback app that also has its own syncing service built in. Obviously this is &lt;a href=&quot;https://en.wikipedia.org/wiki/Unix_philosophy&quot; class=&quot;urlextern&quot; title=&quot;https://en.wikipedia.org/wiki/Unix_philosophy&quot; rel=&quot;ugc nofollow&quot;&gt;heretical&lt;/a&gt;. A music player should focus solely on playing music, while a syncing application should handle data sync. The result of that being impossible is the app store has a bunch of terrible apps with names like “&lt;a href=&quot;https://apps.apple.com/bf/app/music-player-tube-mp3-player/id1572190372&quot; class=&quot;urlextern&quot; title=&quot;https://apps.apple.com/bf/app/music-player-tube-mp3-player/id1572190372&quot; rel=&quot;ugc nofollow&quot;&gt;Network music player ULTIMATE&lt;/a&gt;” that have varying levels of support for playback and/or sync to/from various data sources - Samba, WebDAV, whatever else you can think of. I&amp;#039;ve tried most of them and they all suck.
&lt;/p&gt;

&lt;p&gt;
I can already hear you saying, “why don&amp;#039;t you &lt;a href=&quot;https://wiki.qlyoung.net/just&quot; class=&quot;wikilink1&quot; title=&quot;just&quot; data-wiki-id=&quot;just&quot;&gt;just&lt;/a&gt; play back your music over the network using a network player”? In addition to the available apps sucking, network conditions on mobile are variable enough that streaming from a home server results in a generally poor experience. Industrial streaming services like Spotify have to go to &lt;a href=&quot;https://engineering.atspotify.com/2020/02/how-spotify-aligned-cdn-services-for-a-lightning-fast-streaming-experience/&quot; class=&quot;urlextern&quot; title=&quot;https://engineering.atspotify.com/2020/02/how-spotify-aligned-cdn-services-for-a-lightning-fast-streaming-experience/&quot; rel=&quot;ugc nofollow&quot;&gt;extreme lengths&lt;/a&gt; to paper over the network enough to deliver a good experience. “Pinning” - where you stream but select specific items to keep locally on disk - doesn&amp;#039;t really work for me because I don&amp;#039;t want to choose what music to listen each time I anticipate a no-network scenario.
&lt;/p&gt;

&lt;p&gt;
Anyway, as luck would have it, the &lt;a href=&quot;https://apps.apple.com/us/app/foobar2000/id1072807669&quot; class=&quot;urlextern&quot; title=&quot;https://apps.apple.com/us/app/foobar2000/id1072807669&quot; rel=&quot;ugc nofollow&quot;&gt;best music player on iOS&lt;/a&gt; also has &lt;a href=&quot;https://www.dbpoweramp.com/tunefusion.htm&quot; class=&quot;urlextern&quot; title=&quot;https://www.dbpoweramp.com/tunefusion.htm&quot; rel=&quot;ugc nofollow&quot;&gt;the best sync solution&lt;/a&gt; I&amp;#039;ve seen. The downside is that the companion program that runs on your server
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; is Windows only&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; is closed source&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; costs money&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
However, it is surprisingly full featured and works very well. Since it&amp;#039;s Windows only, I run it inside a windows VM on my hypervisor (which also hosts the file server with my music library). The VM has the music directory mounted from the file server via Samba.
&lt;/p&gt;

&lt;p&gt;
The end result is that every time I open the foobar2000 app on iOS, any new music in my library downloads to my device. After that it&amp;#039;s available for local playback. Since my phone is also on Tailscale, this works anywhere.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Phone&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;phone&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:4,&amp;quot;range&amp;quot;:&amp;quot;3673-6975&amp;quot;} --&gt;
&lt;h1 class=&quot;sectionedit5&quot; id=&quot;historical_methods&quot;&gt;Historical methods&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
I used to do it a much more complicated way than depicted above because I had an Android phone with not enough storage to store my lossless music collection. Android meant I could run background file syncing utilities and not enough storage meant that I had to transcode my collection to something lossy in order to crunch it down small enough to fit on my phone. Buying an iPhone with 512gb of storage meant that 1) I lost the ability to run any kind of background syncing software because iOS doesn&amp;#039;t really allow daemons to exist (unless Apple made them) and 2) I no longer needed to transcode as my music collection is only ~115gb which fits on my phone&amp;#039;s internal storage. Thus all of the automatic cron jobs to do periodic transcoding and sync via syncthing etc are no more.
&lt;/p&gt;

&lt;p&gt;
These are the unintelligible notes I took about how I used to do it, replete with ascii diagrams from a time before I caved a little bit on my static site elitism and just used &lt;a href=&quot;https://mermaid.js.org/&quot; class=&quot;urlextern&quot; title=&quot;https://mermaid.js.org/&quot; rel=&quot;ugc nofollow&quot;&gt;mermaid&lt;/a&gt;:
&lt;/p&gt;

&lt;p&gt;
1. Tx from source to local staging directory
2. `beet import` from staging directory into mounted remote share[0]
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;                 { source }
                     |
                     .
                    ---   (net) &amp;lt;1&amp;gt;
                     .
                     |
                     v
              [ local:music ]
                     |
                     .
            beet    ---   (net) &amp;lt;2&amp;gt;
           import    .
                     |
                     v
      [ remote:music =&amp;gt; local:remote/music]&lt;/pre&gt;

&lt;p&gt;
3. cron job on remote periodically copies new files into a sync directory,
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt; transcoding any lossless files to `-q6` ogg vorbis to reduce size[1]&lt;/pre&gt;
&lt;pre class=&quot;code&quot;&gt;             [ remote:music ]
                     |
                     |
    music-sync.sh    |    @ 2hr &amp;lt;3&amp;gt;
                     |
                     v
           [ remote:music-sync ]&lt;/pre&gt;

&lt;p&gt;
4. sync directory shared to all devices via syncthing[2]
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;           [ remote:music-sync ]
                     |
                     |
                     .
        syncthing   ---   (net) &amp;lt;4&amp;gt;
                     .
                    ...
                   . . .
                  .  .  .
                 .   .   .
                /    |    \
               v     v     v
            phone  laptop  idk&lt;/pre&gt;

&lt;p&gt;
[0] &amp;lt;&lt;a href=&quot;https://beets.io/&quot; class=&quot;urlextern&quot; title=&quot;https://beets.io/&quot; rel=&quot;ugc nofollow&quot;&gt;https://beets.io/&lt;/a&gt;&amp;gt;
&lt;/p&gt;

&lt;p&gt;
[2] &amp;lt;&lt;a href=&quot;https://syncthing.net/&quot; class=&quot;urlextern&quot; title=&quot;https://syncthing.net/&quot; rel=&quot;ugc nofollow&quot;&gt;https://syncthing.net/&lt;/a&gt;&amp;gt;
&lt;/p&gt;
&lt;pre class=&quot;code shell&quot;&gt;#!/usr/bin/fish
set MUSICDIR &amp;quot;./music/&amp;quot;
set SYNCDIR  &amp;quot;./music-sync&amp;quot;
&amp;nbsp;
for dir in (find &amp;quot;$MUSICDIR&amp;quot; -type d | cut -d&#039;/&#039; -f3-)
    mkdir -p &amp;quot;$SYNCDIR/$dir&amp;quot;
end
&amp;nbsp;
for file in (find &amp;quot;$MUSICDIR&amp;quot; -type f -name &#039;*.flac&#039; -o -name &#039;*.mp3&#039; -o -name &#039;*.ogg&#039; | cut -d&#039;/&#039; -f3-)
    set ifile (echo &amp;quot;$MUSICDIR/$file&amp;quot;)
    switch $file
    case &amp;quot;*.flac&amp;quot;
        set ofile (echo &amp;quot;$SYNCDIR/&amp;quot;(echo &amp;quot;$file&amp;quot; | sed &amp;quot;s/flac/ogg/&amp;quot;))
        if test -e &amp;quot;$ofile&amp;quot;
            echo &amp;quot;$ofile exists; skipping&amp;quot;
            continue
        end
        echo &amp;quot;&amp;gt;&amp;gt; Transcoding &#039;$ifile&#039; to &#039;$ofile&#039;&amp;quot;
        oggenc -q6 -o &amp;quot;$ofile&amp;quot; &amp;quot;$ifile&amp;quot;
    case &amp;quot;*&amp;quot;
        set ofile (echo &amp;quot;$SYNCDIR/$file&amp;quot;)
        if test -e &amp;quot;$ofile&amp;quot;
            echo &amp;quot;$ofile exists; skipping&amp;quot;
            continue
        end
        echo &amp;quot;&amp;gt;&amp;gt; Copying &#039;$ifile&#039; to $ofile&amp;quot;
        cp &amp;quot;$ifile&amp;quot; &amp;quot;$ofile&amp;quot;
    end
end&lt;/pre&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/from_blog?do=showtag&amp;amp;tag=from_blog&quot; class=&quot;wikilink1&quot; title=&quot;tag:from_blog&quot; rel=&quot;tag&quot;&gt;from blog&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/technology?do=showtag&amp;amp;tag=technology&quot; class=&quot;wikilink1&quot; title=&quot;tag:technology&quot; rel=&quot;tag&quot;&gt;technology&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/self-hosting?do=showtag&amp;amp;tag=self-hosting&quot; class=&quot;wikilink1&quot; title=&quot;tag:self-hosting&quot; rel=&quot;tag&quot;&gt;self-hosting&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Historical methods&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;historical_methods&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:5,&amp;quot;range&amp;quot;:&amp;quot;6976-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/my_web">
        <dc:format>text/html</dc:format>
        <dc:date>2025-02-14T18:50:38+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>my_web</title>
        <link>https://wiki.qlyoung.net/my_web</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;my_web&quot;&gt;my web&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
In recent years I&amp;#039;ve become disillusioned with 3rd party hosted web services. I don&amp;#039;t like:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; tracking &amp;amp; advertisements&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; lack of control over where my data is physically stored&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; lack of control over what is served to people who visit my pages&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; privacy policies&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; data breaches&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; nebulous copyrights&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.notion.so&quot; class=&quot;urlextern&quot; title=&quot;https://www.notion.so&quot; rel=&quot;ugc nofollow&quot;&gt;getting logged out constantly&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; constantly changing UIs&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; bait and switch (free to paid)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; lock-in&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://ourincrediblejourney.tumblr.com/&quot; class=&quot;urlextern&quot; title=&quot;https://ourincrediblejourney.tumblr.com/&quot; rel=&quot;ugc nofollow&quot;&gt;incredible journeys&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; google&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Consequently I&amp;#039;ve been steadily migrating to hosting everything I need myself. At this point the only things I don&amp;#039;t self host are email and Github, which is just a portfolio piece these days.
&lt;/p&gt;

&lt;p&gt;
My choices in software lean heavily towards stability, sometimes at the expense of my other technological values of elegance and minimalism. Fun as it is, I have other stuff to do than play sysadmin in my limited free time. For the same reason everything is dockerized as a hard requirement, so I prefer software with good support for that already.
&lt;/p&gt;

&lt;p&gt;
I wrote a technical overview of &lt;a href=&quot;https://wiki.qlyoung.net/personal_infrastructure&quot; class=&quot;wikilink1&quot; title=&quot;personal_infrastructure&quot; data-wiki-id=&quot;personal_infrastructure&quot;&gt;how I run all this crap&lt;/a&gt; if it interests you.
&lt;/p&gt;

&lt;p&gt;
An overview of what I self host to follow.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;my web&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;my_web&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-1247&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit2&quot; id=&quot;private&quot;&gt;private&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
This stuff is only accessible on my personal intranet.
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://archivebox.io/&quot; class=&quot;urlextern&quot; title=&quot;https://archivebox.io/&quot; rel=&quot;ugc nofollow&quot;&gt;archivebox&lt;/a&gt; - personal &lt;a href=&quot;https://archive.org/web/&quot; class=&quot;urlextern&quot; title=&quot;https://archive.org/web/&quot; rel=&quot;ugc nofollow&quot;&gt;Wayback Machine&lt;/a&gt;. Kinda like bookmarks for data hoarders.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://about.gitea.com/&quot; class=&quot;urlextern&quot; title=&quot;https://about.gitea.com/&quot; rel=&quot;ugc nofollow&quot;&gt;gitea&lt;/a&gt; - personal git server. Like a lightweight Gitlab.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://healthchecks.io/&quot; class=&quot;urlextern&quot; title=&quot;https://healthchecks.io/&quot; rel=&quot;ugc nofollow&quot;&gt;healthchecks&lt;/a&gt; - cron monitoring. I use this primarily to monitor backups on my devices, so I get emailed if backups start failing.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://invidious.io/&quot; class=&quot;urlextern&quot; title=&quot;https://invidious.io/&quot; rel=&quot;ugc nofollow&quot;&gt;invidious&lt;/a&gt; - youtube frontend. Video browsing free of engineered distraction. Combined with a compatible mobile client (such as Yattee) it also achieves ad-free youtube on iOS without resorting to jailbreaks.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://miniflux.app/&quot; class=&quot;urlextern&quot; title=&quot;https://miniflux.app/&quot; rel=&quot;ugc nofollow&quot;&gt;miniflux&lt;/a&gt; - rss reader. I probably need a better solution for RSS - probably a mobile app - as I rarely check it.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://nextcloud.com/&quot; class=&quot;urlextern&quot; title=&quot;https://nextcloud.com/&quot; rel=&quot;ugc nofollow&quot;&gt;nextcloud&lt;/a&gt;* - calDAV, cardDAV, &lt;a href=&quot;https://apps.nextcloud.com/apps/tasks&quot; class=&quot;urlextern&quot; title=&quot;https://apps.nextcloud.com/apps/tasks&quot; rel=&quot;ugc nofollow&quot;&gt;task tracking&lt;/a&gt;. The kitchen sink of self hosting, but most of the apps it has aren&amp;#039;t great compared to dedicated software. I use it primarily for calDAV and cardDAV. Heavier than what I need for those applications but it&amp;#039;s nice to be able to try out nextcloud apps when I hear about them&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.photoprism.app/&quot; class=&quot;urlextern&quot; title=&quot;https://www.photoprism.app/&quot; rel=&quot;ugc nofollow&quot;&gt;photoprism&lt;/a&gt; - personal photo server. The GOAT. Photoprism has it all - CV content tagging &amp;amp; face recognition, manual tagging, RAW rendering, sidecar handling, location &amp;amp; map views, calendar views, albums, comprehensive search, pleasant design &amp;amp; obsessive core devs.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://quassel-irc.org/&quot; class=&quot;urlextern&quot; title=&quot;https://quassel-irc.org/&quot; rel=&quot;ugc nofollow&quot;&gt;quassel core&lt;/a&gt;* - &lt;abbr title=&quot;Internet Relay Chat&quot;&gt;IRC&lt;/abbr&gt; bouncer&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://docs.tandoor.dev/&quot; class=&quot;urlextern&quot; title=&quot;https://docs.tandoor.dev/&quot; rel=&quot;ugc nofollow&quot;&gt;tandoor&lt;/a&gt; - recipe book; capable of extracting recipes from web urls, similar functionality to &lt;a href=&quot;https://www.justtherecipe.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.justtherecipe.com/&quot; rel=&quot;ugc nofollow&quot;&gt;justtherecipe&lt;/a&gt; et al&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://github.com/oobabooga/text-generation-webui&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/oobabooga/text-generation-webui&quot; rel=&quot;ugc nofollow&quot;&gt;text-generation-webui&lt;/a&gt; - locally hosted LLMs. Super convenient, just plug in the model name on huggingface and it will download it and serve a chatbot-style web UI&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.monicahq.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.monicahq.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Monica&lt;/a&gt; - personal CRM. Lets me track things about people I know - birthdays, pets, kids, s/o&amp;#039;s, significant life events, things like that. Can send email reminders of things as well. I&amp;#039;m a very forgetful guy and this helps me keep up my relationships with others. Probably will replace Nextcloud cardDAV for me at some point&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;private&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;private&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;1248-3611&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit3&quot; id=&quot;public&quot;&gt;public&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
All the stuff you can get to on the web.
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://akkoma.social/&quot; class=&quot;urlextern&quot; title=&quot;https://akkoma.social/&quot; rel=&quot;ugc nofollow&quot;&gt;akkoma&lt;/a&gt; - fedi microblogging. That means twitter for you &lt;del&gt;normies&lt;/del&gt; mainstream folks. &lt;a href=&quot;https://social.qlyoung.net/&quot; class=&quot;urlextern&quot; title=&quot;https://social.qlyoung.net/&quot; rel=&quot;ugc nofollow&quot;&gt;https://social.qlyoung.net/&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.dokuwiki.org/dokuwiki&quot; class=&quot;urlextern&quot; title=&quot;https://www.dokuwiki.org/dokuwiki&quot; rel=&quot;ugc nofollow&quot;&gt;dokuwiki&lt;/a&gt; - wiki. You&amp;#039;re on it.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://github.com/krateng/maloja&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/krateng/maloja&quot; rel=&quot;ugc nofollow&quot;&gt;maloja&lt;/a&gt; - scrobbles. It shows you what music I&amp;#039;ve been listening to, when I listen to it on clients that support reporting that information. &lt;a href=&quot;https://maloja.qlyoung.net/&quot; class=&quot;urlextern&quot; title=&quot;https://maloja.qlyoung.net/&quot; rel=&quot;ugc nofollow&quot;&gt;https://maloja.qlyoung.net/&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://joinpeertube.org/en_US&quot; class=&quot;urlextern&quot; title=&quot;https://joinpeertube.org/en_US&quot; rel=&quot;ugc nofollow&quot;&gt;peertube&lt;/a&gt; - video hosting. Fediverse native via ActivityPub. Supports remote transcoding &amp;amp; S3 compatible storage for videos; consequently can be a very cost efficient platform for self hosting video (think &amp;lt; $20/mo). &lt;a href=&quot;https://qtube.qlyoung.net/&quot; class=&quot;urlextern&quot; title=&quot;https://qtube.qlyoung.net/&quot; rel=&quot;ugc nofollow&quot;&gt;https://qtube.qlyoung.net/&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://piwigo.org/&quot; class=&quot;urlextern&quot; title=&quot;https://piwigo.org/&quot; rel=&quot;ugc nofollow&quot;&gt;piwigo&lt;/a&gt; - photo gallery. There are lighter options, but I like the album management interface and built in support for plugins and themes. Very stable. &lt;a href=&quot;https://photos.qlyoung.net/&quot; class=&quot;urlextern&quot; title=&quot;https://photos.qlyoung.net/&quot; rel=&quot;ugc nofollow&quot;&gt;https://photos.qlyoung.net/&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://matrix.org/&quot; class=&quot;urlextern&quot; title=&quot;https://matrix.org/&quot; rel=&quot;ugc nofollow&quot;&gt;matrix&lt;/a&gt;* - chat server. Kind of a huge pain in the ass to self host, but once &lt;a href=&quot;https://github.com/spantaleev/matrix-docker-ansible-deploy&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/spantaleev/matrix-docker-ansible-deploy&quot; rel=&quot;ugc nofollow&quot;&gt;set up&lt;/a&gt;, you can plug almost every other chat program in the world into it - even stuff like facebook messenger and whatsapp. Largely eliminates chat platform conversations. &lt;pre class=&quot;code&quot;&gt;@qlyoung:qlyoung.net&lt;/pre&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;p&gt;
*Observant readers will note that not all of this stuff is/need be web based
&lt;/p&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/technology?do=showtag&amp;amp;tag=technology&quot; class=&quot;wikilink1&quot; title=&quot;tag:technology&quot; rel=&quot;tag&quot;&gt;technology&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/self-hosting?do=showtag&amp;amp;tag=self-hosting&quot; class=&quot;wikilink1&quot; title=&quot;tag:self-hosting&quot; rel=&quot;tag&quot;&gt;self-hosting&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;public&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;public&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;3612-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/network_simulation_with_k8s-topo_on_raspi_cluster">
        <dc:format>text/html</dc:format>
        <dc:date>2024-01-06T05:24:25+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>network_simulation_with_k8s-topo_on_raspi_cluster</title>
        <link>https://wiki.qlyoung.net/network_simulation_with_k8s-topo_on_raspi_cluster</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;network_simulation_with_k8s-topo_on_raspberry_pi_3b_cluster&quot;&gt;network simulation with k8s-topo on raspberry pi 3b+ cluster&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
This post covers how I set up &lt;a href=&quot;https://github.com/networkop/k8s-topo&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/networkop/k8s-topo&quot; rel=&quot;ugc nofollow&quot;&gt;k8s-topo&lt;/a&gt; on a Raspberry Pi cluster to perform network simulations. &lt;code&gt;k8s-topo&lt;/code&gt; is a sweet project that lets you spin up arbitrary network topologies on Kubernetes clusters. The router nodes can be cEOS, Quagga, or FRR. However, on ARM, the only supported router image is FRR since it&amp;#039;s the only one that currently has ARM images available.
Here&amp;#039;s a picture of the cluster:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/picluster.jpg?id=network_simulation_with_k8s-topo_on_raspi_cluster&quot; class=&quot;media&quot; title=&quot;picluster.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/picluster.jpg?w=600&amp;amp;tok=f2f2b6&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;Raspberry Pi 3B+ cluster&quot; alt=&quot;Raspberry Pi 3B+ cluster&quot; width=&quot;600&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
If you&amp;#039;re curious, the backplane is a &lt;a href=&quot;https://bitscope.com/product/blade/?p=about&quot; class=&quot;urlextern&quot; title=&quot;https://bitscope.com/product/blade/?p=about&quot; rel=&quot;ugc nofollow&quot;&gt;Bitscope Blade&lt;/a&gt;. I&amp;#039;d like to write another post on how to set up and provision such a cluster when I find the time.
You can run some pretty big topologies even on such a small cluster, such as
this 40-node random topology:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/randomtopo.png?id=network_simulation_with_k8s-topo_on_raspi_cluster&quot; class=&quot;media&quot; title=&quot;randomtopo.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/randomtopo.png?w=600&amp;amp;tok=6e0e01&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;Undirected graph depicting example network topology&quot; alt=&quot;Undirected graph depicting example network topology&quot; width=&quot;600&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Overall it&amp;#039;s a capable, compact setup for experimenting with software routers. In my role as a maintainer for FRR it serves particularly well as a testbed for that project, especially for development related to our &lt;a href=&quot;https://hub.docker.com&quot; class=&quot;urlextern&quot; title=&quot;https://hub.docker.com&quot; rel=&quot;ugc nofollow&quot;&gt;Docker images&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
Fair warning: this involves a &lt;em&gt;lot&lt;/em&gt; of source builds. The software industry - and this is especially true of the k8s / Docker communities - is largely an amd64 monoculture these days, and our target platform is ARM. Furthermore, Kubernetes is notoriously complicated and somewhat underdocumented and takes full advantage of semantic versioning to regularly break its APIs between versions. Similarly, the Go ecosystem is still a moving target and language features relied upon by tooling often require recent versions. Finally, anyone who&amp;#039;s worked with Kubernetes before knows that there&amp;#039;s really no such thing as Kubernetes; there&amp;#039;s only Kubernetes implementations, each of which have their own little quirks that like to block you. k3s seems to be one of the better ones in this regard but there are still quirks.
&lt;/p&gt;

&lt;p&gt;
Because of these factors, and because of the painfully slow build time of large software on Raspis, this project is a rather involved and will probably take at least a full day. Nevertheless, with perseverance, we shall prevail.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;network simulation with k8s-topo on raspberry pi 3b+ cluster&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;network_simulation_with_k8s-topo_on_raspberry_pi_3b_cluster&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-2265&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit2&quot; id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; A k3s cluster of Raspberry Pis, or similar armv7l devices &lt;em&gt;with hard float support&lt;/em&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Raspbian 10&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;code&gt;bash&lt;/code&gt; - all of my command lines are specific to &lt;code&gt;bash&lt;/code&gt; but could be adapted to other shells. Also, they&amp;#039;re all implied to be root shells.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Docker, on the master node:&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;code bash&quot;&gt;apt &lt;span class=&quot;kw2&quot;&gt;install&lt;/span&gt; docker.io&lt;/pre&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Patience&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
I cannot emphasize hard float enough. Unfortunately nobody cares about soft float devices, especially Python. If you are on soft float do not waste your time. If you do attempt this project on soft float and succeed, I&amp;#039;d love to hear about it.
&lt;/p&gt;

&lt;p&gt;
Note that while my target device, Pi 3B+, is a 64-bit platform, I have tried to avoid taking the easy way out and downloading prebuilt arm64 binaries. That is what got us into this monoculture mess to begin with. I do liberally download 32 bit armv5 binaries though, because some of the tools used can take days to build on a Raspberry Pi (looking at you protobuf). These should be largely compatible across most ARM devices.
&lt;/p&gt;

&lt;p&gt;
Also, before we go any further, add this line to the end of your &lt;code&gt;.bashrc&lt;/code&gt;:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;kw3&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;re2&quot;&gt;KUBECONFIG&lt;/span&gt;=&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;rancher&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;k3s&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;k3s.yaml&lt;/pre&gt;

&lt;p&gt;
A lot of tools look at this environment variable to know where the k8s global config is.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Prerequisites&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;prerequisites&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;2266-3541&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit3&quot; id=&quot;guide&quot;&gt;Guide&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Best to begin from the top.
&lt;/p&gt;

&lt;p&gt;
End goal: Get &lt;code&gt;k8s-topo&lt;/code&gt; running.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Guide&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;guide&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;3542-3627&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit4&quot; id=&quot;step_1install_dependencies&quot;&gt;Step 1: Install Dependencies&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
The main thing &lt;code&gt;k8s-topo&lt;/code&gt; needs is a way to configure appropriate network devices in a Kubernetes-sanctioned way; something to sit on top of the “native” Kubernetes networking sludge and provide a playground to allow us to create interfaces accessible to our containers and wire them up. &lt;code&gt;k8s-topo&lt;/code&gt; supports two methods of configuring the network resources it needs.
&lt;/p&gt;

&lt;p&gt;
The first is via a “CNI plugin” (Container Network Interface). When k8s is told that it needs to create some network resources, it first looks at a predetermined location on the node - typically &lt;code&gt;/etc/cni/net.d/&lt;/code&gt; - for a config file(s). This config file tells it which CNI plugins are available and which one to call. In particular, the config file specifies the path of the plugin binary, some general k8s-related options, and some binary-specific options to pass. K8s then calls this binary, passes it the requested state of the cluster network in environment variables, and expects the binary to make it so. The binary itself typically interacts with the k8s &lt;abbr title=&quot;Application Programming Interface&quot;&gt;API&lt;/abbr&gt; server to perform its actions, although it may do so indirectly by way of other tools, or even with other CNI plugins.
&lt;/p&gt;

&lt;p&gt;
The author of &lt;code&gt;k8s-topo&lt;/code&gt;, &lt;a href=&quot;https://github.com/networkop&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/networkop&quot; rel=&quot;ugc nofollow&quot;&gt;@networkop&lt;/a&gt;, has built a CNI plugin called &lt;a href=&quot;https://github.com/networkop/meshnet-cni&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/networkop/meshnet-cni&quot; rel=&quot;ugc nofollow&quot;&gt;meshnet-ci&lt;/a&gt;. There is a nice (but slightly outdated) writeup on it &lt;a href=&quot;https://networkop.co.uk/post/2018-11-k8s-topo-p1/&quot; class=&quot;urlextern&quot; title=&quot;https://networkop.co.uk/post/2018-11-k8s-topo-p1/&quot; rel=&quot;ugc nofollow&quot;&gt;here&lt;/a&gt;. Tl;dr it creates &lt;code&gt;veth&lt;/code&gt; &amp;#039;s for connectivity between pods on the same node and runs a daemon on all the nodes to coordinate creation of VXLAN p2p links for cross-node pod connectivity. When I started on this project, it didn&amp;#039;t have support for k8s 1.18, which is what I was using. After opening a GitHub issue asking dumb questions in which I mentioned this fact, he updated it to support 1.18 (thank you!). By that time I had already attempted to use the other supported method, &lt;a href=&quot;https://networkservicemesh.io/&quot; class=&quot;urlextern&quot; title=&quot;https://networkservicemesh.io/&quot; rel=&quot;ugc nofollow&quot;&gt;NetworkServiceMesh&lt;/a&gt;. I got pretty far, but ultimately ran into some behavior that appears to be either a bug in NSM or a misconfiguration by me that I was not able to solve. For completeness, I&amp;#039;ll cover my attempts on that front in case you want to try this method out. Maybe someone reading this will tell me what I did wrong.
&lt;/p&gt;

&lt;p&gt;
&lt;code&gt;NetworkServiceMesh&lt;/code&gt; does sort of the same thing as &lt;code&gt;meshnet-cni&lt;/code&gt;, but in a different way. Since my end goal was really just to get my network sims going I didn&amp;#039;t spend too much time researching what a “Service Mesh” is, but in NSM&amp;#039;s case it seems to mean sidecar containers that deploy alongside your pods and provide network connectivity things.
&lt;/p&gt;

&lt;p&gt;
I&amp;#039;ve detailed my efforts towards setting up NetworkService mesh below, but it&amp;#039;s collapsed to save a bit of space.
&lt;/p&gt;

&lt;p&gt;
&amp;lt;details&amp;gt;
&lt;/p&gt;

&lt;p&gt;
&amp;lt;summary markdown=“span”&amp;gt;
&amp;lt;b&amp;gt;Setting Up NetworkServiceMesh&amp;lt;/b&amp;gt;
&amp;lt;/summary&amp;gt;
&lt;/p&gt;

&lt;p&gt;
&lt;em&gt;I was not able to get this working. I feel like I got pretty close, so if you&amp;#039;re adventurous perhaps you can figure out the last piece of the puzzle, I would love to know about it.&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
NSM is deployed with Helm, so we have to set up Helm first. Helm is like a package manager for k8s.
&lt;/p&gt;

&lt;p&gt;
Download a Helm binary release from the GitHub releases page. NSM only supports Helm 2, so we&amp;#039;ll need to install that first. At time of writing, 2.16.7 is the latest version, so that&amp;#039;s what I used. Make sure to download the &lt;code&gt;arm&lt;/code&gt; archive.
&lt;/p&gt;

&lt;p&gt;
(Actually shortly after I wrote this, NSM gained support for Helm 3. I didn&amp;#039;t try it since Helm 2 works.)
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;kw2&quot;&gt;wget&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;https://get.helm.sh/helm-v2.16.7-linux-arm.tar.gz&amp;quot;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;
Extract the archive and “install” Helm:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;kw2&quot;&gt;tar&lt;/span&gt; xvzf helm-v2.16.7-linux-arm.tar.gz
&lt;span class=&quot;kw2&quot;&gt;cp&lt;/span&gt; linux-arm&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;helm &lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;usr&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;local&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;bin&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;helm&lt;/pre&gt;

&lt;p&gt;
The Helm client is now installed. Next step is to install the backend, called &lt;code&gt;tiller&lt;/code&gt;.  k3s is a bit particular regarding permissions, so we need to give &lt;code&gt;tiller&lt;/code&gt; its own k8s service account to do stuff with.
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;kubectl &lt;span class=&quot;re5&quot;&gt;-n&lt;/span&gt; kube-system create serviceaccount tiller
&amp;nbsp;
kubectl create clusterrolebinding tiller \
  &lt;span class=&quot;re5&quot;&gt;--clusterrole&lt;/span&gt;=cluster-admin \
  &lt;span class=&quot;re5&quot;&gt;--serviceaccount&lt;/span&gt;=kube-system:tiller&lt;/pre&gt;

&lt;p&gt;
References:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account&quot; class=&quot;urlextern&quot; title=&quot;https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account&quot; rel=&quot;ugc nofollow&quot;&gt;https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://rancher.com/docs/rancher/v2.x/en/installation/options/helm2/helm-init&quot; class=&quot;urlextern&quot; title=&quot;https://rancher.com/docs/rancher/v2.x/en/installation/options/helm2/helm-init&quot; rel=&quot;ugc nofollow&quot;&gt;https://rancher.com/docs/rancher/v2.x/en/installation/options/helm2/helm-init&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Now we start getting to the x86 monoculture stuff.
&lt;/p&gt;

&lt;p&gt;
The backend for Helm, &lt;code&gt;tiller&lt;/code&gt;, runs in a container in the k8s cluster itself. Despite the Rancher docs claiming you can simply &lt;code&gt;helm init&lt;/code&gt;, in reality, if you do this Helm downloads and deploys an amd64 &lt;code&gt;tiller&lt;/code&gt; image. Obviously on ARM this won&amp;#039;t work; it will just crash with &lt;code&gt;exec format error&lt;/code&gt;. We need an ARM image. As it turns out, Helm doesn&amp;#039;t actually build these, because real computers are all amd64, right?
&lt;/p&gt;

&lt;p&gt;
Fortunately, &lt;a href=&quot;https://hub.docker.com/u/jessestuart&quot; class=&quot;urlextern&quot; title=&quot;https://hub.docker.com/u/jessestuart&quot; rel=&quot;ugc nofollow&quot;&gt;someone&lt;/a&gt; looking out for us aliens builds ARM images of &lt;code&gt;tiller&lt;/code&gt; and publishes them on his personal DockerHub registry. I could write another post railing against binary-only dependencies downloaded from random people on the internet, maybe another time. Hopefully his registry is still available when you&amp;#039;re reading this document. If not you&amp;#039;ll have to find out how to build &lt;code&gt;tiller&lt;/code&gt; for ARM yourself.
&lt;/p&gt;

&lt;p&gt;
To initialize Helm with an appropriate backend image:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;helm init &lt;span class=&quot;re5&quot;&gt;--service-account&lt;/span&gt; tiller &lt;span class=&quot;re5&quot;&gt;--tiller-image&lt;/span&gt;=jessestuart&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;tiller:v2.16.7&lt;/pre&gt;

&lt;p&gt;
&lt;em&gt;Note the tag. If you downloaded a later version of Helm 2 earlier, you&amp;#039;ll need to change the tag version to match. The backend version must match the client version (you can see what you installed with &lt;code&gt;helm version&lt;/code&gt;).&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
Next we&amp;#039;ll have to build NSM ourselves, because they don&amp;#039;t provide images for anything except amd64.
&lt;/p&gt;

&lt;p&gt;
Much of NSM is written in Go, so we need the appropriate Go version. Since Go is rapidly changing, you might have to tweak the version listed here; at time of writing, NSM requires Go 1.13. Other things require Go, and 1.13 worked for everything else I built, so this seems to be a good version.
&lt;/p&gt;

&lt;p&gt;
Install Go 1.13:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;kw2&quot;&gt;wget&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;https://dl.google.com/go/go1.13.11.linux-armv6l.tar.gz&amp;quot;&lt;/span&gt;
&lt;span class=&quot;kw2&quot;&gt;tar&lt;/span&gt; &lt;span class=&quot;re5&quot;&gt;-C&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;usr&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;kw3&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;re5&quot;&gt;-xzf&lt;/span&gt; .&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;go1.13.11.linux-armv6l.tar.gz
&lt;span class=&quot;kw3&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;re2&quot;&gt;PATH&lt;/span&gt;=&lt;span class=&quot;re1&quot;&gt;$PATH&lt;/span&gt;:&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;usr&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;local&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;go&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;bin&lt;/pre&gt;

&lt;p&gt;
Clone the NSM repo:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;kw2&quot;&gt;git clone&lt;/span&gt; https:&lt;span class=&quot;sy0&quot;&gt;//&lt;/span&gt;github.com&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;networkservicemesh&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;networkservicemesh.git
&lt;span class=&quot;kw3&quot;&gt;cd&lt;/span&gt; networkservicemesh&lt;/pre&gt;

&lt;p&gt;
NSM has two forwarding plane implementations available. One is based on VPP (the default), the other uses the kernel. The VPP image, naturally, doesn&amp;#039;t support our particular flavor of ARM:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;root&lt;span class=&quot;sy0&quot;&gt;@&lt;/span&gt;clusterpi-master:&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;home&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;pi&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;vpp-agent&lt;span class=&quot;co0&quot;&gt;# make prod-image&lt;/span&gt;
&lt;span class=&quot;re2&quot;&gt;IMAGE_TAG&lt;/span&gt;= .&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;docker&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;prod&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;build.sh
Current architecture &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;armv7l&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; is not supported.&lt;/pre&gt;

&lt;p&gt;
Fortunately the kernel forwarder does support ARM, so we&amp;#039;ll use that one instead.
&lt;/p&gt;

&lt;p&gt;
Not having found anything in the NSM docs about how to properly build this thing for ARM, very hacky procedures follow.
&lt;/p&gt;

&lt;p&gt;
By default, the recommended &lt;code&gt;make&lt;/code&gt; invocation in the NSM docs builds test images, one of which uses the VPP forwarder, so we need to turn that off. Also turn the forwarder itself off.
&lt;/p&gt;

&lt;p&gt;
&lt;code&gt;cd&lt;/code&gt; into your NSM clone and apply the following patches:
&lt;/p&gt;

&lt;p&gt;
Patch 1:
&lt;/p&gt;
&lt;pre class=&quot;code diff&quot;&gt;diff --git a/forwarder/build.mk b/forwarder/build.mk
index &lt;span class=&quot;re0&quot;&gt;0d24&lt;/span&gt;b89f..&lt;span class=&quot;re0&quot;&gt;23c1&lt;/span&gt;cac4 &lt;span class=&quot;nu0&quot;&gt;100644&lt;/span&gt;
&lt;span class=&quot;re3&quot;&gt;--- a/forwarder/build.mk&lt;/span&gt;
&lt;span class=&quot;re4&quot;&gt;+++ b/forwarder/build.mk&lt;/span&gt;
&lt;span class=&quot;re6&quot;&gt;@@ -12,7 +12,7 @@&lt;/span&gt;
 # See the License for the specific language governing permissions and
 # limitations under the License.
&amp;nbsp;
&lt;span class=&quot;re7&quot;&gt;-forwarder_images = vppagent-forwarder kernel-forwarder&lt;/span&gt;
&lt;span class=&quot;re8&quot;&gt;+forwarder_images = kernel-forwarder&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;
Patch 2:
&lt;/p&gt;
&lt;pre class=&quot;code diff&quot;&gt;diff --git a/test/build.mk b/test/build.mk
index 97354fbf..d1aff&lt;span class=&quot;re0&quot;&gt;0d3&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;100644&lt;/span&gt;
&lt;span class=&quot;re3&quot;&gt;--- a/test/build.mk&lt;/span&gt;
&lt;span class=&quot;re4&quot;&gt;+++ b/test/build.mk&lt;/span&gt;
&lt;span class=&quot;re6&quot;&gt;@@ -14,7 +14,7 @@&lt;/span&gt;
&amp;nbsp;
 test_apps = $&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;shell ls ./test/applications/cmd/&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
 test_targets = $&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;addsuffix -build, $&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;addprefix go-, $&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;test_apps&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;re7&quot;&gt;-test_images = test-common vpp-test-common&lt;/span&gt;
&lt;span class=&quot;re8&quot;&gt;+test_images = test-common # vpp-test-common&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;
At this point we can build the project. This takes about an hour on my Pi 3B+.
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;kw2&quot;&gt;make&lt;/span&gt; k8s-build&lt;/pre&gt;

&lt;p&gt;
In order to work around some other stuff, we&amp;#039;ll need raw tarballs of the Docker images we just built. There&amp;#039;s a &lt;code&gt;make&lt;/code&gt; target to save all those.
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;kw2&quot;&gt;make&lt;/span&gt; k8s-save&lt;/pre&gt;

&lt;p&gt;
Your built images are now in your local docker registry, &lt;strong&gt;but k3s doesn&amp;#039;t use that&lt;/strong&gt;, it has a private one. If you try to deploy the NSM Helm chart at this point, you&amp;#039;ll end up pulling the amd64 images. These images will then be cached on every single one of your nodes, and will not be pulled again due to the pull policy in NSM&amp;#039;s pod specs being &lt;code&gt;IfNotPresent&lt;/code&gt;, leading to much pain.
&lt;/p&gt;

&lt;p&gt;
In case you already did this by mistake, you&amp;#039;ll need to log into each node and run the following to delete the amd64 images from the cache:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;k3s ctr images list &lt;span class=&quot;sy0&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;grep&lt;/span&gt; networkservicemesh &lt;span class=&quot;sy0&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;re5&quot;&gt;-d&lt;/span&gt;&lt;span class=&quot;st_h&quot;&gt;&#039; &#039;&lt;/span&gt; &lt;span class=&quot;re5&quot;&gt;-f1&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;xargs&lt;/span&gt; k3s ctr images remove&lt;/pre&gt;

&lt;p&gt;
We now need a way to make our Kubernetes pods use our locally-built ARM images. There&amp;#039;s two ways of doing this, the correct way and the hacky way. The correct way is to set up a Docker registry, retag all of the images we just built and upload them to this registry, reconfigure your entire cluster to use this registry, and rewrite the Helm charts to point at it instead of the default &lt;code&gt;docker.io&lt;/code&gt;. The hacky way is to just inject our images into the image cache on each node so that the nodes believe they have the latest version of the upstream images and thus defer to our cached ARM images instead of the upstream amd64 images.
&lt;/p&gt;

&lt;p&gt;
You could also create a local registry and serve your images from there; I tried this, ran into some TLS issues, canned it.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Hacky way&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
All the images you saved in &lt;code&gt;make k8s-save&lt;/code&gt; should be in &lt;code&gt;build/images&lt;/code&gt; as &lt;code&gt;tar&lt;/code&gt; archives.
&lt;/p&gt;

&lt;p&gt;
On each cluster node:
- &lt;code&gt;scp&lt;/code&gt; the images to your node, like &lt;code&gt;/tmp/images&lt;/code&gt;
- Run this:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;in&lt;/span&gt; .&lt;span class=&quot;sy0&quot;&gt;/*&lt;/span&gt;; &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt; k3s ctr images import &lt;span class=&quot;re1&quot;&gt;$file&lt;/span&gt;; &lt;span class=&quot;kw1&quot;&gt;done&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;
Note you&amp;#039;ll have to do this for other images later on, so it&amp;#039;s probably better to just use DockerHub.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Correct Way&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
Retag your built images and push them to your personal DockerHub repository.
&lt;/p&gt;

&lt;p&gt;
Now, at last, you can deploy NSM:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;re2&quot;&gt;SPIRE_ENABLED&lt;/span&gt;=&lt;span class=&quot;kw2&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;re2&quot;&gt;INSECURE&lt;/span&gt;=&lt;span class=&quot;kw2&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;re2&quot;&gt;FORWARDING_PLANE&lt;/span&gt;=kernel &lt;span class=&quot;kw2&quot;&gt;make&lt;/span&gt; helm-install-nsm&lt;/pre&gt;

&lt;p&gt;
At this point the containers deployed successfully, but the problem I ran into was some kind of communications between two of NSM&amp;#039;s containers, similar to &lt;a href=&quot;https://github.com/networkservicemesh/networkservicemesh/issues/2160&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/networkservicemesh/networkservicemesh/issues/2160&quot; rel=&quot;ugc nofollow&quot;&gt;this bug&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
By this time &lt;code&gt;meshnet-cni&lt;/code&gt; was ready to try again so I went in that direction again.
&lt;/p&gt;

&lt;p&gt;
—
&lt;/p&gt;

&lt;p&gt;
&amp;lt;/details&amp;gt;
&lt;/p&gt;

&lt;p&gt;
Instead of NSM I ended up going with &lt;code&gt;meshnet-cni&lt;/code&gt;, and I recommend you go this route as well; it&amp;#039;s lighter, simpler and created by the author of &lt;code&gt;k8s-topo&lt;/code&gt; specifically for use with that project. Consequently, changes to one are less likely to break the other :)
&lt;/p&gt;

&lt;/div&gt;

&lt;h4 id=&quot;setting_up_meshnet-cni&quot;&gt;Setting up meshnet-cni&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Just like with NetworkServiceMesh, we have to make a lot of adjustments, build custom images, etc. I&amp;#039;ve already done these and put them in a fork of &lt;code&gt;meshnet-cni&lt;/code&gt;, so rather than walk through the necessary changes you can just clone my branch:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;kw2&quot;&gt;git clone&lt;/span&gt; &lt;span class=&quot;re5&quot;&gt;--single-branch&lt;/span&gt; &lt;span class=&quot;re5&quot;&gt;--branch&lt;/span&gt; k3s-arm https:&lt;span class=&quot;sy0&quot;&gt;//&lt;/span&gt;github.com&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;qlyoung&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;meshnet-cni.git&lt;/pre&gt;

&lt;p&gt;
Brief summary of changes made:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Change all binary downloads to fetch ARM versions&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Modify CNI config files for k3s Flannel&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Remove node selectors that restrict to amd64 nodes&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Change docker images to point at ARM-compatible builds with above changes&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Modify CNI config paths to place them in the custom k3s locations&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
The last one does require some manual patching. k3s does not use the standard &lt;code&gt;/etc/cni/net.d&lt;/code&gt; location for CNI configs. It will only look at that directory if you disable the built-in CNI, Flannel. However, &lt;code&gt;meshnet-cni&lt;/code&gt; works by layering additional network resources *on top* of resources created by a lower-level CNI referred to as the “delegate”; in this case Flannel. If we disable Flannel and replace it wholesale with &lt;code&gt;meshnet-cni&lt;/code&gt;, things will not work, but if we leave Flannel enabled, then &lt;code&gt;/etc/cni/net.d&lt;/code&gt; is completely ignored. This is an unfortunate design choice by k3s that also precludes use of other CNIs that follow the “delegate” pattern, such as Multus. However, we can workaround this by simply installing our CNI to k3s&amp;#039;s custom location, which is &lt;code&gt;/var/lib/rancher/k3s/agent/etc/cni/net.d&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
But it gets more complicated. That path is where the CNI configs go, but the CNI binaries go somewhere else, again not the default k8s location. And in the case of the binaries, the k3s equivalent path includes a cluster-specific GUID &lt;img src=&quot;https://wiki.qlyoung.net/lib/images/smileys/smile.svg&quot; class=&quot;icon smiley&quot; alt=&quot;:-)&quot; /&gt;. We need to find this GUID and tweak &lt;code&gt;meshnet-cni&lt;/code&gt; before installing to know it. Fortunately this customization is only relevant for the k8s manifests and doesn&amp;#039;t need to be hardcoded in the Docker images, so you can use the k3s + ARM images I&amp;#039;ve already built instead of having to build your own.
&lt;/p&gt;

&lt;p&gt;
To find the GUID:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;kw2&quot;&gt;ls&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;var&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;lib&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;rancher&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;k3s&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;data&lt;/pre&gt;

&lt;p&gt;
There should be a single directory there whose name is a long hash-looking string, in my case:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;co0&quot;&gt;#  ls /var/lib/rancher/k3s/data&lt;/span&gt;
ec54df8c1938fe49660230d16334b4c7e83888a93e6f037fd8552893e2f67383&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;
To make sure &lt;code&gt;meshnet-cni&lt;/code&gt; binaries make it into this location, &lt;code&gt;cd&lt;/code&gt; into your &lt;code&gt;meshnet-cni&lt;/code&gt; repo and run this, replacing &lt;code&gt;YOUR_GUID&lt;/code&gt; with the GUID you just found:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;kw3&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;re2&quot;&gt;K3S_CNI_GUID&lt;/span&gt;=&lt;span class=&quot;sy0&quot;&gt;&amp;lt;&lt;/span&gt;YOUR_GUID&lt;span class=&quot;sy0&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;kw2&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;re5&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;re5&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;s/YOUR_K3S_GUID/&lt;span class=&quot;es2&quot;&gt;$K3S_CNI_GUID&lt;/span&gt;/g&amp;quot;&lt;/span&gt; manifests&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;base&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;meshnet.yml&lt;/pre&gt;

&lt;p&gt;
Now you should be ready to deploy &lt;code&gt;meshnet-cni&lt;/code&gt;. To do this:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;apt &lt;span class=&quot;kw2&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;re5&quot;&gt;-yqq&lt;/span&gt; build-essential
&lt;span class=&quot;kw2&quot;&gt;make&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;install&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;
If the installation was successful you should now have a &lt;code&gt;meshnet-cni&lt;/code&gt; agent pod running on each node. Verify this:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;co0&quot;&gt;# kubectl -n meshnet get all&lt;/span&gt;
NAME                READY   STATUS    RESTARTS   AGE
pod&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;meshnet-p7mmq   &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;     Running   &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;          15d
pod&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;meshnet-b66vp   &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;     Running   &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;          15d
pod&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;meshnet-wqlvz   &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;     Running   &lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;          43s
pod&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;meshnet-6jk7g   &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;     Running   &lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;          43s
&amp;nbsp;
NAME                     DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
daemonset.apps&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;meshnet   &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;         &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;         &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;       &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;            &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;           &lt;span class=&quot;sy0&quot;&gt;&amp;lt;&lt;/span&gt;none&lt;span class=&quot;sy0&quot;&gt;&amp;gt;&lt;/span&gt;          15d&lt;/pre&gt;

&lt;p&gt;
With luck you should be good to go, and &lt;code&gt;meshnet-cni&lt;/code&gt; is now ready to create the necessary VXLAN devices as directed by &lt;code&gt;k8s-topo&lt;/code&gt;.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4 id=&quot;step_2_-_disable_load_balancer&quot;&gt;Step 2 - Disable load balancer&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Now that we have our overlay (&lt;code&gt;meshnet-cni&lt;/code&gt;) deployed, we&amp;#039;re ready to deploy &lt;code&gt;k8s-topo&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
Not!
&lt;/p&gt;

&lt;p&gt;
By default k3s runs a load balancer, which binds ports 80 and 443 on every
node. &lt;code&gt;k8s-topo&lt;/code&gt; wants those, so if you try to deploy &lt;code&gt;k8s-topo&lt;/code&gt; now, your pods
will remain in &lt;code&gt;Pending&lt;/code&gt; because port &lt;code&gt;80&lt;/code&gt; is in use. Don&amp;#039;t bother trying to
&lt;code&gt;netstat -tln&lt;/code&gt;, port 80 won&amp;#039;t show up. But it&amp;#039;s still in use. Kubernetes logic.
&lt;/p&gt;

&lt;p&gt;
Anyway, we need to turn that off:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://rancher.com/docs/k3s/latest/en/networking/#traefik-ingress-controller&quot; class=&quot;urlextern&quot; title=&quot;https://rancher.com/docs/k3s/latest/en/networking/#traefik-ingress-controller&quot; rel=&quot;ugc nofollow&quot;&gt;https://rancher.com/docs/k3s/latest/en/networking/#traefik-ingress-controller&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://github.com/rancher/k3s/issues/1160#issuecomment-561572618&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/rancher/k3s/issues/1160#issuecomment-561572618&quot; rel=&quot;ugc nofollow&quot;&gt;https://github.com/rancher/k3s/issues/1160#issuecomment-561572618&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Copied here for posterity:
&lt;/p&gt;
&lt;blockquote&gt;&lt;div class=&quot;no&quot;&gt;
 1. Remove traefik helm chart resource:&lt;br/&gt;
    &lt;pre class=&quot;code&quot;&gt;&amp;gt;    kubectl -n kube-system delete helmcharts.helm.cattle.io traefik
&amp;gt;    &lt;/pre&gt;

&lt;p&gt;
&lt;br/&gt;
 2. Stop the k3s service:&lt;br/&gt;
    
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;&amp;gt;    sudo service k3s stop
&amp;gt;    &lt;/pre&gt;

&lt;p&gt;
&lt;br/&gt;
 3. Edit service file &lt;code&gt;/etc/systemd/system/k3s.service&lt;/code&gt; and add this line to&lt;br/&gt;
    &lt;code&gt;ExecStart&lt;/code&gt;:&lt;br/&gt;
    
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;&amp;gt;    --no-deploy traefik \
&amp;gt;    &lt;/pre&gt;

&lt;p&gt;
&lt;br/&gt;
 4. Reload the service file:&lt;br/&gt;
    
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;&amp;gt;    sudo systemctl daemon-reload
&amp;gt;    &lt;/pre&gt;

&lt;p&gt;
&lt;br/&gt;
 5. Remove the manifest file from auto-deploy folder:&lt;br/&gt;
    
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;&amp;gt;    sudo rm /var/lib/rancher/k3s/server/manifests/traefik.yaml
&amp;gt;    &lt;/pre&gt;

&lt;p&gt;
&lt;br/&gt;
 6. Start the k3s service:&lt;br/&gt;
    
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;&amp;gt;    sudo service k3s start
&amp;gt;    &lt;/pre&gt;
&lt;/div&gt;&lt;/blockquote&gt;

&lt;p&gt;
Probably a good idea to reboot your master node here for good measure.
&lt;/p&gt;

&lt;p&gt;
And finally, we can deploy &lt;code&gt;k8s-topo&lt;/code&gt;.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4 id=&quot;step_3_-_install_k8s-topo&quot;&gt;Step 3 - Install k8s-topo&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Now that we have our overlay (&lt;code&gt;meshnet-cni&lt;/code&gt;) deployed, we&amp;#039;re ready to deploy &lt;code&gt;k8s-topo&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
As with &lt;code&gt;meshnet-cni&lt;/code&gt;, &lt;code&gt;k8s-topo&lt;/code&gt; requires patches to work on ARM. These patches are much less extensive than the ones to &lt;code&gt;meshnet-cni&lt;/code&gt; since nothing needs to be changed to accommodate k3s; the changes just point the images to the ARM-compatible ones I&amp;#039;ve built and published in my own DockerHub registry.
&lt;/p&gt;

&lt;p&gt;
Pull my ARM-compatible fork of &lt;code&gt;k8s-topo&lt;/code&gt;:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;kw2&quot;&gt;git clone&lt;/span&gt; &lt;span class=&quot;re5&quot;&gt;--single-branch&lt;/span&gt; &lt;span class=&quot;re5&quot;&gt;--branch&lt;/span&gt; k3s-arm https:&lt;span class=&quot;sy0&quot;&gt;//&lt;/span&gt;github.com&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;qlyoung&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;k8s-topo.git&lt;/pre&gt;

&lt;p&gt;
One notable change is that I&amp;#039;ve also added support for &lt;a href=&quot;https://github.com/frrouting/frr&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/frrouting/frr&quot; rel=&quot;ugc nofollow&quot;&gt;FRR&lt;/a&gt;, which is a significantly upgraded fork of Quagga. The rest of the images will still pull amd64 versions and so at this time the only image you can choose for your topology simulations is the FRR image.
&lt;/p&gt;

&lt;p&gt;
Now you can deploy &lt;code&gt;k8s-topo&lt;/code&gt; onto your cluster:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;kw3&quot;&gt;cd&lt;/span&gt; k8s-topo
kubectl apply &lt;span class=&quot;re5&quot;&gt;-f&lt;/span&gt; manifest.yml&lt;/pre&gt;

&lt;p&gt;
Voila.
&lt;/p&gt;

&lt;p&gt;
Verify that the &lt;code&gt;k8s-topo&lt;/code&gt; control pod and associated services have been created:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;root&lt;span class=&quot;sy0&quot;&gt;@&lt;/span&gt;clusterpi-&lt;span class=&quot;nu0&quot;&gt;69&lt;/span&gt; &lt;span class=&quot;co0&quot;&gt;# kubectl get all&lt;/span&gt;
NAME                            READY   STATUS        RESTARTS   AGE
pod&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;k8s-topo-86cbbdbddb-xrwlm   &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;     Running       &lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;          55s
&amp;nbsp;
NAME                       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;S&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;        AGE
service&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;kubernetes         ClusterIP   10.43.0.1       &lt;span class=&quot;sy0&quot;&gt;&amp;lt;&lt;/span&gt;none&lt;span class=&quot;sy0&quot;&gt;&amp;gt;&lt;/span&gt;        &lt;span class=&quot;nu0&quot;&gt;443&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;TCP        22d
service&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;k8s-topo-service   NodePort    10.43.111.199   &lt;span class=&quot;sy0&quot;&gt;&amp;lt;&lt;/span&gt;none&lt;span class=&quot;sy0&quot;&gt;&amp;gt;&lt;/span&gt;        &lt;span class=&quot;nu0&quot;&gt;80&lt;/span&gt;:&lt;span class=&quot;nu0&quot;&gt;30000&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;TCP   56s
&amp;nbsp;
NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;k8s-topo   &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;     &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;            &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;           56s
&amp;nbsp;
NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;k8s-topo-86cbbdbddb   &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;         &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;         &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;       55s&lt;/pre&gt;

&lt;p&gt;
That&amp;#039;s it, we&amp;#039;re done!
&lt;/p&gt;

&lt;/div&gt;

&lt;h4 id=&quot;step_4_-_profit&quot;&gt;Step 4 - profit&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Now you can log into the &lt;code&gt;k8s-topo&lt;/code&gt; control pod and use the CLI tools there.
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;root&lt;span class=&quot;sy0&quot;&gt;@&lt;/span&gt;clusterpi-&lt;span class=&quot;nu0&quot;&gt;69&lt;/span&gt; &lt;span class=&quot;co0&quot;&gt;# kubectl exec -it deployment/k8s-topo -- sh&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;k8s-topo &lt;span class=&quot;co0&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;k8s-topo &lt;span class=&quot;co0&quot;&gt;# k8s-topo -h&lt;/span&gt;
usage: k8s-topo &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;-h&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;-d&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;--create &lt;span class=&quot;sy0&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;re5&quot;&gt;--destroy&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;re5&quot;&gt;--show&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;re5&quot;&gt;--eif&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;re5&quot;&gt;--lldp&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;|&lt;/span&gt; --graph&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; topology
&amp;nbsp;
Tool to create network topologies inside k8s
&amp;nbsp;
positional arguments:
  topology     Topology &lt;span class=&quot;kw2&quot;&gt;file&lt;/span&gt;
&amp;nbsp;
optional arguments:
  -h, &lt;span class=&quot;re5&quot;&gt;--help&lt;/span&gt;   show this &lt;span class=&quot;kw3&quot;&gt;help&lt;/span&gt; message and &lt;span class=&quot;kw3&quot;&gt;exit&lt;/span&gt;
  -d, &lt;span class=&quot;re5&quot;&gt;--debug&lt;/span&gt;  Enable Debug
&amp;nbsp;
Actions:
  Create or destroy topology
&amp;nbsp;
  &lt;span class=&quot;re5&quot;&gt;--create&lt;/span&gt;     Create topology
  &lt;span class=&quot;re5&quot;&gt;--destroy&lt;/span&gt;    Destroy topology
  &lt;span class=&quot;re5&quot;&gt;--show&lt;/span&gt;       Show running topology
  &lt;span class=&quot;re5&quot;&gt;--eif&lt;/span&gt;        Enable &lt;span class=&quot;kw2&quot;&gt;ip&lt;/span&gt; forwarding &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; cEOS devices
  &lt;span class=&quot;re5&quot;&gt;--lldp&lt;/span&gt;       Enable LLDP forwarding &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; vrnetlab devices
  &lt;span class=&quot;re5&quot;&gt;--graph&lt;/span&gt;      Generate a D3 graph
&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;k8s-topo &lt;span class=&quot;co0&quot;&gt;#&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4 id=&quot;usage_examples&quot;&gt;Usage Examples&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Create a random FRR topology:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;k8s-topo &lt;span class=&quot;co0&quot;&gt;# cd examples/builder/&lt;/span&gt;
&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;k8s-topo&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;examples&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;builder &lt;span class=&quot;co0&quot;&gt;# ./builder 10 3&lt;/span&gt;
Total number of links generated: &lt;span class=&quot;nu0&quot;&gt;12&lt;/span&gt;
&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;k8s-topo&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;examples&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;builder &lt;span class=&quot;co0&quot;&gt;# sed -i -e &#039;s/qrtr/frr/g&#039; ./random.yml&lt;/span&gt;
&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;k8s-topo&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;examples&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;builder &lt;span class=&quot;co0&quot;&gt;# cat random.yml&lt;/span&gt;&lt;/pre&gt;
&lt;pre class=&quot;code&quot;&gt;yml
conf_dir: /k8s-topo/examples/builder/config-random
etcd_port: 32379
links:
- endpoints:
  - frr-192-0-2-2:eth1:10.0.0.2/30
  - frr-192-0-2-5:eth1:10.0.0.1/30
- endpoints:
  - frr-192-0-2-2:eth2:10.0.0.5/30
  - frr-192-0-2-4:eth1:10.0.0.6/30
- endpoints:
  - frr-192-0-2-0:eth1:10.0.0.10/30
  - frr-192-0-2-4:eth2:10.0.0.9/30
- endpoints:
  - frr-192-0-2-0:eth2:10.0.0.13/30
  - frr-192-0-2-9:eth1:10.0.0.14/30
- endpoints:
  - frr-192-0-2-3:eth1:10.0.0.18/30
  - frr-192-0-2-9:eth2:10.0.0.17/30
- endpoints:
  - frr-192-0-2-3:eth2:10.0.0.21/30
  - frr-192-0-2-7:eth1:10.0.0.22/30
- endpoints:
  - frr-192-0-2-0:eth3:10.0.0.25/30
  - frr-192-0-2-6:eth1:10.0.0.26/30
- endpoints:
  - frr-192-0-2-4:eth3:10.0.0.29/30
  - frr-192-0-2-8:eth1:10.0.0.30/30
- endpoints:
  - frr-192-0-2-1:eth1:10.0.0.34/30
  - frr-192-0-2-4:eth4:10.0.0.33/30
- endpoints:
  - frr-192-0-2-1:eth2:10.0.0.37/30
  - frr-192-0-2-8:eth2:10.0.0.38/30
- endpoints:
  - frr-192-0-2-6:eth2:10.0.0.42/30
  - frr-192-0-2-8:eth3:10.0.0.41/30
- endpoints:
  - frr-192-0-2-0:eth4:10.0.0.45/30
  - frr-192-0-2-5:eth2:10.0.0.46/30
publish_base:
  22: 30001&lt;/pre&gt;

&lt;p&gt;
Apply the topology:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;k8s-topo&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;examples&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;builder &lt;span class=&quot;co0&quot;&gt;# cd /k8s-topo/&lt;/span&gt;
&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;k8s-topo &lt;span class=&quot;co0&quot;&gt;# k8s-topo --create examples/builder/random.yml&lt;/span&gt;
INFO:__main__:All topology data has been uploaded
INFO:__main__:All pods have been created successfully
INFO:__main__:
 &lt;span class=&quot;kw3&quot;&gt;alias&lt;/span&gt; frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;=&lt;span class=&quot;st_h&quot;&gt;&#039;kubectl exec -it frr-192-0-2-2 sh&#039;&lt;/span&gt;
 &lt;span class=&quot;kw3&quot;&gt;alias&lt;/span&gt; frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;5&lt;/span&gt;=&lt;span class=&quot;st_h&quot;&gt;&#039;kubectl exec -it frr-192-0-2-5 sh&#039;&lt;/span&gt;
 &lt;span class=&quot;kw3&quot;&gt;alias&lt;/span&gt; frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;=&lt;span class=&quot;st_h&quot;&gt;&#039;kubectl exec -it frr-192-0-2-4 sh&#039;&lt;/span&gt;
 &lt;span class=&quot;kw3&quot;&gt;alias&lt;/span&gt; frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;=&lt;span class=&quot;st_h&quot;&gt;&#039;kubectl exec -it frr-192-0-2-0 sh&#039;&lt;/span&gt;
 &lt;span class=&quot;kw3&quot;&gt;alias&lt;/span&gt; frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;9&lt;/span&gt;=&lt;span class=&quot;st_h&quot;&gt;&#039;kubectl exec -it frr-192-0-2-9 sh&#039;&lt;/span&gt;
 &lt;span class=&quot;kw3&quot;&gt;alias&lt;/span&gt; frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;=&lt;span class=&quot;st_h&quot;&gt;&#039;kubectl exec -it frr-192-0-2-3 sh&#039;&lt;/span&gt;
 &lt;span class=&quot;kw3&quot;&gt;alias&lt;/span&gt; frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;7&lt;/span&gt;=&lt;span class=&quot;st_h&quot;&gt;&#039;kubectl exec -it frr-192-0-2-7 sh&#039;&lt;/span&gt;
 &lt;span class=&quot;kw3&quot;&gt;alias&lt;/span&gt; frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;6&lt;/span&gt;=&lt;span class=&quot;st_h&quot;&gt;&#039;kubectl exec -it frr-192-0-2-6 sh&#039;&lt;/span&gt;
 &lt;span class=&quot;kw3&quot;&gt;alias&lt;/span&gt; frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;8&lt;/span&gt;=&lt;span class=&quot;st_h&quot;&gt;&#039;kubectl exec -it frr-192-0-2-8 sh&#039;&lt;/span&gt;
 &lt;span class=&quot;kw3&quot;&gt;alias&lt;/span&gt; frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;=&lt;span class=&quot;st_h&quot;&gt;&#039;kubectl exec -it frr-192-0-2-1 sh&#039;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;
Log out of the container and verify that the pods have been created:
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;root&lt;span class=&quot;sy0&quot;&gt;@&lt;/span&gt;clusterpi-&lt;span class=&quot;nu0&quot;&gt;69&lt;/span&gt; &lt;span class=&quot;co0&quot;&gt;# kubectl get all&lt;/span&gt;
NAME                            READY   STATUS        RESTARTS   AGE
pod&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;k8s-topo-86cbbdbddb-xrwlm   &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;     Running       &lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;          6m18s
pod&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;               &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;     Running       &lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;          35s
pod&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;5&lt;/span&gt;               &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;     Running       &lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;          35s
pod&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;7&lt;/span&gt;               &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;     Running       &lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;          34s
pod&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;9&lt;/span&gt;               &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;     Running       &lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;          35s
pod&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;               &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;     Running       &lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;          35s
pod&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;               &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;     Running       &lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;          34s
pod&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;               &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;     Running       &lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;          33s
pod&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;               &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;     Running       &lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;          35s
pod&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;6&lt;/span&gt;               &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;     Running       &lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;          34s
pod&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;frr-&lt;span class=&quot;nu0&quot;&gt;192&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;-&lt;span class=&quot;nu0&quot;&gt;8&lt;/span&gt;               &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;     Running       &lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;          34s&lt;/pre&gt;

&lt;p&gt;
Log into one of the pods and have a look around:
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;root@clusterpi-69 # kubectl exec -it pod/frr-192-0-2-2 -- sh
# ip addr
1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
3: eth0@if31: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc noqueue state UP group default
    link/ether ca:c9:8b:1f:d6:69 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.42.2.116/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::c8c9:8bff:fe1f:d669/64 scope link
       valid_lft forever preferred_lft forever
40: eth2@if41: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc noqueue state UP group default
    link/ether e2:72:df:0f:93:55 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet 10.0.0.5/30 brd 10.0.0.7 scope global eth2
       valid_lft forever preferred_lft forever
    inet6 fe80::e072:dfff:fe0f:9355/64 scope link
       valid_lft forever preferred_lft forever
42: eth1@if43: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc noqueue state UP group default
    link/ether 2e:af:91:d4:c1:b0 brd ff:ff:ff:ff:ff:ff link-netnsid 2
    inet 10.0.0.2/30 brd 10.0.0.3 scope global eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::2caf:91ff:fed4:c1b0/64 scope link
       valid_lft forever preferred_lft forever
# vtysh
% Can&amp;#039;t open configuration file /etc/frr/vtysh.conf due to &amp;#039;No such file or directory&amp;#039;.

Hello, this is FRRouting (version 7.5-dev_git587162029617).
Copyright 1996-2005 Kunihiro Ishiguro, et al.

frr-192-0-2-2#
frr-192-0-2-2# sh run
Building configuration...

Current configuration:
!
frr version 7.5-dev_git587162029617
frr defaults traditional
hostname frr-192-0-2-2
no ipv6 forwarding
!
line vty
!
end
frr-192-0-2-2#&lt;/pre&gt;

&lt;p&gt;
Welcome to your simulation.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4 id=&quot;notes&quot;&gt;Notes&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
I&amp;#039;ve extensively replaced various components of this setup with my own forks to which I&amp;#039;ve added ARM support. I plan to merge the ARM changes back into their respective upstream repos, but this is going to take some time, as the changes I&amp;#039;ve done currently are very quick-and-dirty. They need to be rewritten not just to work on ARM, but to generalize amd64-specific build options to work on any target architecture.
&lt;/p&gt;

&lt;p&gt;
I very much dislike making this setup depending on my personal GitHub forks and DockerHub registry, but doing so was the only way I could finish this project in a reasonable time frame.
&lt;/p&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/from_blog?do=showtag&amp;amp;tag=from_blog&quot; class=&quot;wikilink1&quot; title=&quot;tag:from_blog&quot; rel=&quot;tag&quot;&gt;from blog&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/technology?do=showtag&amp;amp;tag=technology&quot; class=&quot;wikilink1&quot; title=&quot;tag:technology&quot; rel=&quot;tag&quot;&gt;technology&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/networking?do=showtag&amp;amp;tag=networking&quot; class=&quot;wikilink1&quot; title=&quot;tag:networking&quot; rel=&quot;tag&quot;&gt;networking&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Step 1: Install Dependencies&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;step_1install_dependencies&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:4,&amp;quot;range&amp;quot;:&amp;quot;3628-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/note_taking_programs">
        <dc:format>text/html</dc:format>
        <dc:date>2025-09-29T18:37:05+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>note_taking_programs</title>
        <link>https://wiki.qlyoung.net/note_taking_programs</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;note_taking_programs&quot;&gt;note taking programs&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
I have a relatively busy life with many areas of concern and a poor memory, so I rely heavily on notes to document and remember things.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Current choice&lt;/strong&gt;: &lt;a href=&quot;https://obsidian.md/&quot; class=&quot;urlextern&quot; title=&quot;https://obsidian.md/&quot; rel=&quot;ugc nofollow&quot;&gt;Obsidian&lt;/a&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Offline: Yes&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Editor implementation: Web/Electron&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Mobile exp: ★★☆☆☆&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Sync: Yes&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Org: Hierarchical&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
Obsidian is a markdown editor that operates over a directory of markdown files and supports crosslinking between them. It has a plugin system that extends this base to add whatever other capabilities you want.
&lt;/p&gt;

&lt;p&gt;
The editor itself is pretty nice. If you&amp;#039;ve used electron apps before you know that sometimes they feel native (e.g. Discord) and sometimes they feel like a webpage running in Electron (e.g. Logseq, most others). Obsidian&amp;#039;s editor feels very native.
&lt;/p&gt;

&lt;p&gt;
Markdown as a content format is both a blessing and a curse. On the one hand, all user content is plain text. That has many advantages - easy to get at your content, easy to exit the ecosystem (in theory), syncing is fast, on disk size is light.
&lt;/p&gt;

&lt;p&gt;
On the other hand, Obsidian wants to have its cake and eat it too. It wants to get all the advantages of Markdown but also offer high level functionality such as task tracking, tabular data storage, query languages etc. To make these work well, the underlying data really wants to be structured. But there is no structured data store in Obsidian, the data format is “just Markdown”. How to solve this conundrum? Most of the value-added features do so by storing structured data &lt;strong&gt;in Markdown&lt;/strong&gt;. This is such an obviously bad idea and the results are as bad as you might expect. You end up with a bunch of shitty home grown databases whose disk format is an insane extension of Markdown. Now it&amp;#039;s no longer easy to exit the ecosystem, you cannot get at your data without the Obsidian code necessary to render it, etc.
&lt;/p&gt;

&lt;p&gt;
Edit: I originally wrote the above paragraph in 2024. It&amp;#039;s now late 2025 and I have been vindicated. &lt;a href=&quot;https://help.obsidian.md/bases/syntax&quot; class=&quot;urlextern&quot; title=&quot;https://help.obsidian.md/bases/syntax&quot; rel=&quot;ugc nofollow&quot;&gt;https://help.obsidian.md/bases/syntax&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Consequently I don&amp;#039;t use any of the plugins in Obsidian that do this and so for me, most of the advanced workflows are lost on me and it ends up as a glorified Markdown editor. However, the editor itself is quite nice so it&amp;#039;s still worthwhile to use it vs e.g. vim on a directory tree. The sync service (which costs money) works well and does a good job syncing apps between my various computers and phone. The mobile editor is not great but as it is an App it&amp;#039;s more functional than alternatives. Writing Markdown is fine when you have a full keyboard but it&amp;#039;s not a good experience on mobile.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;note taking programs&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;note_taking_programs&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-2618&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit2&quot; id=&quot;previously&quot;&gt;Previously&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Previously&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;previously&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;2619-2642&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit3&quot; id=&quot;notion&quot;&gt;Notion&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Offline: No&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Editor implementation: Web/Electron&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Mobile exp: ★★☆☆☆&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Sync: N/A&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Org: Hierarchical&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
I used to use &lt;a href=&quot;https://www.notion.so&quot; class=&quot;urlextern&quot; title=&quot;https://www.notion.so&quot; rel=&quot;ugc nofollow&quot;&gt;Notion&lt;/a&gt;. Notion is organized around the idea of content blocks. Each block has a type; text, image, database etc.
&lt;/p&gt;

&lt;p&gt;
Databases are probably the most distinguishing feature of Notion. They&amp;#039;re an in-app implementation of a hybrid between a spreadsheet and a relational database. The nice thing about databases is that you can choose their display formats; for example, a database with a date column can be displayed as a calendar.
&lt;/p&gt;

&lt;p&gt;
Notion&amp;#039;s biggest strengths are databases and the ability to embed any type of content inline - images, pdfs, maps, calendars etc. It&amp;#039;s got a very rich presentation and you can make some truly beautiful and functional notes with it. Databases are very powerful IF you are the type of person that is willing to spend large amounts of time curating structure for your notes.
&lt;/p&gt;

&lt;p&gt;
My biggest issues with Notion are, in order:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Online only - you can&amp;#039;t access your notes without internet&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Clients are webpage-in-a-box type Electron apps, feels like a website both on desktop and mobile&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; The mobile app logs you out every few weeks. I cannot fucking stand mobile apps that log you out regularly. I reported this to Notion and they never fixed it, that was the final straw for me.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Notion&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;notion&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;2643-4031&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit4&quot; id=&quot;logseq&quot;&gt;logseq&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Offline: Yes&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Editor implementation: Web/Electron&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Mobile exp: N/A&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Sync: Yes&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Org: Sequential&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
Logseq is interesting. It&amp;#039;s an outliner with a unique model for note taking. It&amp;#039;s organized around the idea of a sequence of logs. The happy path for logseq is that each day you open up today&amp;#039;s daily note, write whatever it is you are working on or thinking of, and attach identifiers to blocks of content. Later on, if you want to extend the notes you took on that content, you&amp;#039;re supposed to synthesize a new note in today&amp;#039;s journal page by transcluding the content you already wrote via its identifier. Then there&amp;#039;s some hand wavey idea that “later” you&amp;#039;ll pull all of those blocks into their own topic pages.
&lt;/p&gt;

&lt;p&gt;
This probably works for some people but it&amp;#039;s way too much effort writing notes to be useful for me.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;logseq&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;logseq&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:4,&amp;quot;range&amp;quot;:&amp;quot;4032-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/oxygen_rebreather">
        <dc:format>text/html</dc:format>
        <dc:date>2024-05-31T20:45:11+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>oxygen_rebreather</title>
        <link>https://wiki.qlyoung.net/oxygen_rebreather</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;oxygen_rebreather&quot;&gt;oxygen rebreather&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
In late 2022 I purchased an oxygen diving rebreather. The specific unit is a &lt;a href=&quot;https://en.salamanderccr.com/&quot; class=&quot;urlextern&quot; title=&quot;https://en.salamanderccr.com/&quot; rel=&quot;ugc nofollow&quot;&gt;salamander&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
A rebreather is a device that allows users to breathe underwater. Unlike &lt;a href=&quot;https://en.wikipedia.org/wiki/Scuba_set#Open-circuit&quot; class=&quot;urlextern&quot; title=&quot;https://en.wikipedia.org/wiki/Scuba_set#Open-circuit&quot; rel=&quot;ugc nofollow&quot;&gt;open circuit&lt;/a&gt; systems, where gas exhaled by the user is lost as bubbles into the water, a rebreather recycles that gas. This is accomplished by routing exhaled gas into a bag (called a “counterlung”). Along the way the gas is forced through a chemical filter to remove CO2 and the oxygen consumed by the body is replaced from a supply tank. The user then inhales from the bag on their next breath.
&lt;/p&gt;

&lt;p&gt;
Because gas is recycled rebreathers typically have a much longer duration for a given gas volume than open circuit systems. Roughly speaking this means you can use much smaller cylinders to achieve the same dive, ignoring backup gas carried in case of a rebreather malfunction (“bailout”). The inhaled gas is also warm and humid since it was just inside the diver&amp;#039;s body, in contrast to open circuit where the gas is supplied directly from a pressurized cylinder and is &lt;a href=&quot;https://en.wikipedia.org/wiki/ideal gas law#Combined_gas_law&quot; class=&quot;interwiki iw_wp&quot; title=&quot;https://en.wikipedia.org/wiki/ideal gas law#Combined_gas_law&quot;&gt;consequently cold and dry&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
An oxygen rebreather is a special case of rebreather where the gas composition breathed by the user is nominally 100% oxygen. This is in contrast to a “mixed gas” rebreather which delivers a gas mixture such as nitrox (oxygen &amp;amp; nitrogen) or trimix (oxygen, nitrogen &amp;amp; helium). Those units require the use of a diluent gas in addition to an oxygen supply - the diluent and oxygen are mixed by the unit to produce the desired gas. This makes them very capable units, but much more complicated, large, and prone to failure. An oxygen rebreather is comparatively simple as it only has two jobs to do - replace the consumed oxygen and remove the CO2.
&lt;/p&gt;

&lt;p&gt;
Here is mine:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/salamander_predive.jpg?id=oxygen_rebreather&quot; class=&quot;media&quot; title=&quot;salamander_predive.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/salamander_predive.jpg?w=400&amp;amp;tok=301922&quot; class=&quot;medialeft&quot; align=&quot;left&quot; loading=&quot;lazy&quot; title=&quot;salamander oxygen ccr, wet, ready to dive&quot; alt=&quot;salamander oxygen ccr, wet, ready to dive&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/salamander_predive_2.jpg?id=oxygen_rebreather&quot; class=&quot;media&quot; title=&quot;salamander_predive_2.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/salamander_predive_2.jpg?w=400&amp;amp;tok=e38a3b&quot; class=&quot;medialeft&quot; align=&quot;left&quot; loading=&quot;lazy&quot; title=&quot;salamander oxygen ccr, wet, ready to dive, alternate photo&quot; alt=&quot;salamander oxygen ccr, wet, ready to dive, alternate photo&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;oxygen rebreather&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;oxygen_rebreather&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-2053&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit2&quot; id=&quot;why&quot;&gt;why?&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
This question is most often asked by other divers. The funny thing is that the answer is usually obvious to non divers - it lets you breathe underwater! But when divers ask it, they mean something different. What they mean is “why would you buy a device with such limited utility?” I&amp;#039;ll explain what they mean.
&lt;/p&gt;

&lt;p&gt;
The thing about oxygen is that it&amp;#039;s a highly reactive gas. This property is &lt;a href=&quot;https://en.wikipedia.org/wiki/Cellular respiration&quot; class=&quot;interwiki iw_wp&quot; title=&quot;https://en.wikipedia.org/wiki/Cellular respiration&quot;&gt;critical to human chemistry&lt;/a&gt;. However, in excess concentrations, &lt;a href=&quot;https://en.wikipedia.org/wiki/oxygen_toxicity&quot; class=&quot;interwiki iw_wp&quot; title=&quot;https://en.wikipedia.org/wiki/oxygen_toxicity&quot;&gt;oxygen becomes toxic&lt;/a&gt;. Therefore in a breathing gas you have to keep the oxygen concentration below a certain level in order to avoid poisoning the user. In diving, we do this by limiting the &lt;a href=&quot;https://en.wikipedia.org/wiki/partial_pressure&quot; class=&quot;interwiki iw_wp&quot; title=&quot;https://en.wikipedia.org/wiki/partial_pressure&quot;&gt;partial pressure&lt;/a&gt; of oxygen (ppO2). The “maximum” ppO2 generally accepted to be “safe” is 1.6 ATA. Oxygen partial pressure increases with depth. For pure oxygen, at the surface (where pressure is 1 ATA) your breathing gas is already at 1.0 ppO2. Every 33ft of freshwater adds another atmosphere of pressure, so for pure oxygen a ppO2 of 1.6 is reached at about 20ft. In short, you cannot safely dive an oxygen rebreather below 20ft. In some respects this is quite limiting; most dive sites are deeper than that.
&lt;/p&gt;

&lt;p&gt;
So why would you want one of these things? Well, for starters, not *everything* worth diving is deeper than 20ft. There are some sites, like &lt;a href=&quot;https://diveatlas.org/index.php/Blue_Heron_Bridge&quot; class=&quot;urlextern&quot; title=&quot;https://diveatlas.org/index.php/Blue_Heron_Bridge&quot; rel=&quot;ugc nofollow&quot;&gt;Blue Heron Bridge&lt;/a&gt;, that are considered world class and have plenty to see in an oxygen rebreather&amp;#039;s depth range. Also, not every dive has to have an objective. You can have a very enjoyable dive and never go below 20ft! For dives like that, the oxygen rebreather is awesome, better than open circuit. Because exhaled gas is captured within the system instead of being exhaled as bubbles, bubble noise is eliminated and the unit is almost completely silent (you can still hear the sound of oxygen pushing through the breathing hoses, but it&amp;#039;s very quiet). Consequently aquatic life is much less scared of you.
&lt;/p&gt;

&lt;p&gt;
The unit is also very lightweight. Fully assembled and ready to dive it weighs 6kg (~13lb) including an oxygen bottle (and not counting ballast). Compared to a typical OC scuba system, where the most basic equipment configuration exceeds 40 pounds (not counting ballast), it is much easier to transport. It&amp;#039;s also much smaller, both in storage and in the water. In the water it&amp;#039;s as close as you can get to wearing nothing at all. This makes it very enjoyable to dive.
&lt;/p&gt;

&lt;p&gt;
It&amp;#039;s also cool. Oxygen rebreathers are very uncommon in sport diving (meaning non-military). Every time I&amp;#039;ve dived it people are curious about it because they&amp;#039;ve never seen one before.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;why?&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;why&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;2054-4762&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit3&quot; id=&quot;i_want_to_see&quot;&gt;I want to see&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
&lt;a href=&quot;https://qtube.qlyoung.net/w/pVz795iyz4iahQ4aBTfsdA&quot; class=&quot;urlextern&quot; title=&quot;https://qtube.qlyoung.net/w/pVz795iyz4iahQ4aBTfsdA&quot; rel=&quot;ugc nofollow&quot;&gt;https://qtube.qlyoung.net/w/pVz795iyz4iahQ4aBTfsdA&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://qtube.qlyoung.net/w/48Vxq2jsP2FByVuk5nzHzE&quot; class=&quot;urlextern&quot; title=&quot;https://qtube.qlyoung.net/w/48Vxq2jsP2FByVuk5nzHzE&quot; rel=&quot;ugc nofollow&quot;&gt;https://qtube.qlyoung.net/w/48Vxq2jsP2FByVuk5nzHzE&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://www.youtube.com/watch?v=iJJ0QnZ_42E&quot; class=&quot;urlextern&quot; title=&quot;https://www.youtube.com/watch?v=iJJ0QnZ_42E&quot; rel=&quot;ugc nofollow&quot;&gt;https://www.youtube.com/watch?v=iJJ0QnZ_42E&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;I want to see&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;i_want_to_see&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;4763-4938&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit4&quot; id=&quot;i_need_more&quot;&gt;I need more&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Check out the &lt;a href=&quot;http://www.therebreathersite.nl/Zuurstofrebreathers/database_oxygen_rebreathersNL.htm&quot; class=&quot;urlextern&quot; title=&quot;http://www.therebreathersite.nl/Zuurstofrebreathers/database_oxygen_rebreathersNL.htm&quot; rel=&quot;ugc nofollow&quot;&gt;oxygen rebreather page on therebreathersite.nl&lt;/a&gt;.
&lt;/p&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/diving?do=showtag&amp;amp;tag=diving&quot; class=&quot;wikilink1&quot; title=&quot;tag:diving&quot; rel=&quot;tag&quot;&gt;diving&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;I need more&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;i_need_more&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:4,&amp;quot;range&amp;quot;:&amp;quot;4939-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/password_management">
        <dc:format>text/html</dc:format>
        <dc:date>2025-10-06T03:07:52+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>password_management</title>
        <link>https://wiki.qlyoung.net/password_management</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;password_management&quot;&gt;password management&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Credential management is a critical part of any personal infrastructure.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;password management&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;password_management&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-107&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit2&quot; id=&quot;current_setup&quot;&gt;Current setup&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
I used to use &lt;a href=&quot;https://www.passwordstore.org/&quot; class=&quot;urlextern&quot; title=&quot;https://www.passwordstore.org/&quot; rel=&quot;ugc nofollow&quot;&gt;pass&lt;/a&gt;. However, with the advent of &lt;a href=&quot;https://en.wikipedia.org/wiki/WebAuthn&quot; class=&quot;interwiki iw_wp&quot; title=&quot;https://en.wikipedia.org/wiki/WebAuthn&quot;&gt;passkeys&lt;/a&gt; this approach was becoming increasingly difficult.
&lt;/p&gt;

&lt;p&gt;
For the record, I absolutely believe in PKC authentication but am highly skeptical of passkeys, to put it mildly. Perhaps another wiki page about that at some point.
&lt;/p&gt;

&lt;p&gt;
Anyhow, in 2025 I switched to &lt;a href=&quot;https://keepassxc.org/&quot; class=&quot;urlextern&quot; title=&quot;https://keepassxc.org/&quot; rel=&quot;ugc nofollow&quot;&gt;KeepassXC&lt;/a&gt; for credential management. In tandem with a &lt;a href=&quot;https://syncthing.net/&quot; class=&quot;urlextern&quot; title=&quot;https://syncthing.net/&quot; rel=&quot;ugc nofollow&quot;&gt;sync solution&lt;/a&gt; to sync the credential database to all devices it works well enough. It supports passkeys, although not all websites (relying parties) will “allow” the particular “flavor” of passkeys (synced credentials) it “supports” (this is all security theater).
&lt;/p&gt;

&lt;p&gt;
KeepassXC works on every computer I own and I use &lt;a href=&quot;https://keepassium.com/&quot; class=&quot;urlextern&quot; title=&quot;https://keepassium.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Keepassium&lt;/a&gt; on my phone.
&lt;/p&gt;

&lt;p&gt;
I do wish it had the ability to use a GPG key as a primary key; instead the database is encrypted with a passphrase. Security keys and PGP challenge-response can be used, but only as secondary or tertiary factors to the passphrase.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Current setup&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;current_setup&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;108-1199&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit3&quot; id=&quot;old_setup&quot;&gt;Old setup&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/untitled_diagram.drawio_1_.svg?id=password_management&quot; class=&quot;media&quot; title=&quot;untitled_diagram.drawio_1_.svg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/untitled_diagram.drawio_1_.svg?w=500&amp;amp;tok=821e91&quot; class=&quot;mediaright&quot; align=&quot;right&quot; loading=&quot;lazy&quot; title=&quot; &quot; alt=&quot; &quot; width=&quot;500&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
I use &lt;a href=&quot;https://www.passwordstore.org/&quot; class=&quot;urlextern&quot; title=&quot;https://www.passwordstore.org/&quot; rel=&quot;ugc nofollow&quot;&gt;pass&lt;/a&gt;. This is a shell script that provides porcelain over a directory of plain text files encrypted with a gpg keypair.
&lt;/p&gt;

&lt;p&gt;
The on disk format looks like this:
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;qlyoung@host ~&amp;gt; tree .password-store/
.password-store/
├── airlines
│   ├── volantis.gpg
│   ├── aerlingus.gpg
│   ├── aircanada.gpg
│   └── united.gpg
├── web
│   ├── twitch.tv.gpg
│   ├── github.gpg
│   ├── bandcamp.gpg
...&lt;/pre&gt;

&lt;p&gt;
Copying a password to the clipboard looks like this:
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;qlyoung@host ~&amp;gt; pass -c web/github
&amp;lt;unlock gpg keypair&amp;gt;
Copied web/github to clipboard. Will clear in 45 seconds.&lt;/pre&gt;

&lt;p&gt;
The “unlock gpg keypair” bit is some UX that asks for the key to unlock my GPG keypair. I use a &lt;a href=&quot;https://en.wikipedia.org/wiki/OpenPGP card&quot; class=&quot;interwiki iw_wp&quot; title=&quot;https://en.wikipedia.org/wiki/OpenPGP card&quot;&gt;OpenPGP card&lt;/a&gt; so this is a prompt to insert that and unlock it.
&lt;/p&gt;

&lt;p&gt;
This directory is version controlled with git. Cross-device sync is accomplished with git push/pull.
&lt;/p&gt;

&lt;p&gt;
It is a very simple and secure setup.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Old setup&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;old_setup&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;1200-2279&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit4&quot; id=&quot;key_management&quot;&gt;key management&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/img_1295.jpg?id=password_management&quot; class=&quot;media&quot; title=&quot;img_1295.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/img_1295.jpg?w=400&amp;amp;tok=ffc764&quot; class=&quot;mediaright&quot; align=&quot;right&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
This setup requires my GPG keypair to unlock the store. This means that the keypair has to be accessible to every device that needs access to passwords.
&lt;/p&gt;

&lt;p&gt;
A GPG keypair is for practical purposes two files, one containing the public key and the other containing the private key. It&amp;#039;s very important to the security of any system involving GPG that the private key file not be made public. For this reason it&amp;#039;s usually encrypted with a symmetric password. If you use the conventional setup, this symmetric password effectively becomes the “master” password for the password store.
&lt;/p&gt;

&lt;p&gt;
However, even though the keypair is itself encrypted, having it stored on disk isn&amp;#039;t ideal from a security perspective - particularly on mobile devices, which are very exposed and historically very insecure. It&amp;#039;s better if we don&amp;#039;t store the key on disk at all. To accomplish this I store my keypair on a &lt;a href=&quot;https://www.yubico.com/product/yubikey-5c-nfc/&quot; class=&quot;urlextern&quot; title=&quot;https://www.yubico.com/product/yubikey-5c-nfc/&quot; rel=&quot;ugc nofollow&quot;&gt;yubikey&lt;/a&gt;, which implements the &lt;a href=&quot;https://en.wikipedia.org/wiki/OpenPGP card&quot; class=&quot;interwiki iw_wp&quot; title=&quot;https://en.wikipedia.org/wiki/OpenPGP card&quot;&gt;OpenPGP card&lt;/a&gt; standard. This small, unobtrusive device lives on my keychain along with my car keys.
&lt;/p&gt;

&lt;p&gt;
On my desktop and laptop, whenever I need a password, I&amp;#039;m prompted to plug in and unlock my yubikey with a 6 digit pin. The encrypted file containing the password is then sent, over USB, to the embedded processor on the yubikey. The yubikey decrypts it using the GPG private key and sends back the cleartext contents. The flow is the same on my phone, except that communication occurs over NFC so I am prompted to tap my yubikey on the device.
&lt;/p&gt;

&lt;p&gt;
Using a smartcard means the GPG keypair itself is never on disk and thus cannot be stolen. Even if the yubikey is stolen, the key cannot be extracted from it, and it is still locked with a pin so it cannot be used to perform any operations.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;key management&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;key_management&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:4,&amp;quot;range&amp;quot;:&amp;quot;2280-4089&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit5&quot; id=&quot;porcelain&quot;&gt;porcelain&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;

&lt;h4 id=&quot;desktop&quot;&gt;desktop&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
It would be inconvenient on desktop to open a terminal and type &lt;code&gt;pass -c &amp;lt;password&amp;gt;&lt;/code&gt; every time I want a password. Fortunately there are various porcelain programs that wrap &lt;code&gt;pass&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
There are plugins that provide browser autofill which I used to use.
&lt;/p&gt;

&lt;p&gt;
Currently I have a global system shortcut (&lt;code&gt;^-alt-p&lt;/code&gt;) which launches &lt;a href=&quot;https://github.com/carnager/rofi-pass&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/carnager/rofi-pass&quot; rel=&quot;ugc nofollow&quot;&gt;rofi-pass&lt;/a&gt;. This is a floating launcher prompt, similar to Spotlight on a Mac. It allows me to type a few characters of the name of the password I want until it matches the correct one. Then I unlock the password store as previously described. Instead of copying the password to the clipboard, it then types it out using a virtual keyboard device. This is nice because it works everywhere - in the browser, in the terminal, and so on.
&lt;/p&gt;

&lt;p&gt;
For git synchronization, pass automatically creates a new git commit whenever a password file is changed, and wraps git operations so that &lt;code&gt;pass git push&lt;/code&gt; and &lt;code&gt;pass git pull&lt;/code&gt; do the obvious things.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4 id=&quot;mobile&quot;&gt;mobile&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
On my iPhone, I use &lt;a href=&quot;https://apps.apple.com/us/app/pass-password-store/id1205820573&quot; class=&quot;urlextern&quot; title=&quot;https://apps.apple.com/us/app/pass-password-store/id1205820573&quot; rel=&quot;ugc nofollow&quot;&gt;this app&lt;/a&gt;, which handles the git operations and integrates with the iOS password management UX.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;porcelain&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;porcelain&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:5,&amp;quot;range&amp;quot;:&amp;quot;4090-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/peak_sets">
        <dc:format>text/html</dc:format>
        <dc:date>2025-08-20T06:28:37+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>peak_sets</title>
        <link>https://wiki.qlyoung.net/peak_sets</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;peak_sets&quot;&gt;peak sets&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
these are not necessarily the most creative sets or the best by any measure of objective quality but just the ones i think SLAM
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=-eT4runfu9U&amp;amp;pp=ygUOaW8ga2l0Y2hlbiBzZXQ%3D&quot; class=&quot;urlextern&quot; title=&quot;https://www.youtube.com/watch?v=-eT4runfu9U&amp;amp;pp=ygUOaW8ga2l0Y2hlbiBzZXQ%3D&quot; rel=&quot;ugc nofollow&quot;&gt;i_o kitchen set&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=rvf0iOF_Vt8&quot; class=&quot;urlextern&quot; title=&quot;https://www.youtube.com/watch?v=rvf0iOF_Vt8&quot; rel=&quot;ugc nofollow&quot;&gt;sarah bonito hardcore mix 2024&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=mcsvKbXey9o&quot; class=&quot;urlextern&quot; title=&quot;https://www.youtube.com/watch?v=mcsvKbXey9o&quot; rel=&quot;ugc nofollow&quot;&gt;taffa summer break 2021&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=b-1t8hvJOBw&quot; class=&quot;urlextern&quot; title=&quot;https://www.youtube.com/watch?v=b-1t8hvJOBw&quot; rel=&quot;ugc nofollow&quot;&gt;taffa millennium strike live 2022&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=NPINX3nTfTg&amp;amp;pp=ygUZbW9kZXJuIGFtYmllbnQganVuZ2xlIG1peA%3D%3D&quot; class=&quot;urlextern&quot; title=&quot;https://www.youtube.com/watch?v=NPINX3nTfTg&amp;amp;pp=ygUZbW9kZXJuIGFtYmllbnQganVuZ2xlIG1peA%3D%3D&quot; rel=&quot;ugc nofollow&quot;&gt;whatever this is&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=4KGsgpFiswQ&quot; class=&quot;urlextern&quot; title=&quot;https://www.youtube.com/watch?v=4KGsgpFiswQ&quot; rel=&quot;ugc nofollow&quot;&gt;porter robinson secret sky 2020&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=43CG4M5vOos&quot; class=&quot;urlextern&quot; title=&quot;https://www.youtube.com/watch?v=43CG4M5vOos&quot; rel=&quot;ugc nofollow&quot;&gt;sewerslvt rawsewage mix&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=7US21orGO3w&quot; class=&quot;urlextern&quot; title=&quot;https://www.youtube.com/watch?v=7US21orGO3w&quot; rel=&quot;ugc nofollow&quot;&gt;sewerslvt sewagebipolar lvl 2/2&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=kY0zOOQ1-uc&quot; class=&quot;urlextern&quot; title=&quot;https://www.youtube.com/watch?v=kY0zOOQ1-uc&quot; rel=&quot;ugc nofollow&quot;&gt;voxkai - ms avalanche 2021&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://wiki.qlyoung.net/that_one_with_the_airplanes&quot; class=&quot;wikilink2&quot; title=&quot;that_one_with_the_airplanes&quot; rel=&quot;nofollow&quot; data-wiki-id=&quot;that_one_with_the_airplanes&quot;&gt;that one with the airplanes&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/peak_tributes">
        <dc:format>text/html</dc:format>
        <dc:date>2025-07-17T20:56:15+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>peak_tributes</title>
        <link>https://wiki.qlyoung.net/peak_tributes</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;peak_tributes&quot;&gt;peak tributes&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
my favorite tributes
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://youtu.be/ES02mWIJ2Tk?feature=shared&quot; class=&quot;urlextern&quot; title=&quot;https://youtu.be/ES02mWIJ2Tk?feature=shared&quot; rel=&quot;ugc nofollow&quot;&gt;felix baumgartner stratosphere jump&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=6YsNRnZRgg8&quot; class=&quot;urlextern&quot; title=&quot;https://www.youtube.com/watch?v=6YsNRnZRgg8&quot; rel=&quot;ugc nofollow&quot;&gt;anomalocaris&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=6PMufhcqLZ8&quot; class=&quot;urlextern&quot; title=&quot;https://www.youtube.com/watch?v=6PMufhcqLZ8&quot; rel=&quot;ugc nofollow&quot;&gt;sky king&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/personal_infrastructure">
        <dc:format>text/html</dc:format>
        <dc:date>2025-10-06T03:38:40+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>personal_infrastructure</title>
        <link>https://wiki.qlyoung.net/personal_infrastructure</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;personal_infrastructure&quot;&gt;personal infrastructure&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
&lt;em&gt;tldr - docker compose, isolated servers&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
Here I describe how my personal computing infrastructure is set up. These aren&amp;#039;t rules or a framework or anything - just a description of how things have ended up.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;personal infrastructure&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;personal_infrastructure&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-250&amp;quot;} --&gt;
&lt;h1 class=&quot;sectionedit2&quot; id=&quot;security&quot;&gt;Security&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
Seems like an odd place to start, but security considerations have dictated the biggest architectural decisions. My view of security contextualizes the rest of the setup.
&lt;/p&gt;

&lt;p&gt;
I have some basic assumptions about software security, main ones relevant to my infra include:
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Security&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;security&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;251-542&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit3&quot; id=&quot;every_application_is_vulnerable&quot;&gt;Every application is vulnerable&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Every time you stand up a service that can be reached from the public internet you&amp;#039;re adding a huge amount of area to your attack surface. Even a single modern web application relies on so much software that it&amp;#039;s reasonable to assume there&amp;#039;s a security flaw in there somewhere. Stand up 10 of those and your surface area is quite large. For that reason I assume everything I run is vulnerable and can be hacked.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Every application is vulnerable&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;every_application_is_vulnerable&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;543-1000&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit4&quot; id=&quot;docker_based_isolation_isn_t_reliable&quot;&gt;Docker based isolation isn&amp;#039;t reliable&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
I use docker for everything, mostly for its ability to isolate software and the filesystem from the host. That said, I assume that the container sandbox can be broken out of. I choose to make the assumption that isolation begins at the VM level. VM sandbox escapes aren&amp;#039;t in my threat model.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Docker based isolation isn&amp;#039;t reliable&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;docker_based_isolation_isn_t_reliable&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:4,&amp;quot;range&amp;quot;:&amp;quot;1001-1344&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit5&quot; id=&quot;can_t_break_what_you_can_t_touch&quot;&gt;Can&amp;#039;t break what you can&amp;#039;t touch&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
&lt;em&gt;Or, network isolation is a strong security measure.&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
All my compute infrastructure is organized into two separate spheres, public and private. The idea is that if my public infrastructure gets completely owned and everything in that sphere is exfiltrated or wiped, that would be annoying but would not be a disaster. I also make an effort to ensure that the public sphere is properly isolated, meaning you can&amp;#039;t get to the private sphere from the public sphere.
&lt;/p&gt;

&lt;p&gt;
Whenever I&amp;#039;m setting up a new piece of infrastructure, the first thing I think about is what sphere it goes in.
&lt;/p&gt;

&lt;p&gt;
&lt;div id=&quot;mermaidContainer1&quot; style=&quot;position: relative; width:auto; height:auto&quot;&gt;&lt;span class=&quot;mermaid&quot; id=mermaidContent1 style=&quot;width:auto; height:auto&quot;&gt;
flowchart TD
  pub((public))
  priv((private))
  data{stores sensitive data?}
  onlyme{used only by me?}
  data --&gt; |yes| priv
  data --&gt; |no| onlyme
  onlyme --&gt; |yes| priv
  onlyme --&gt; |no| pub
&lt;/span&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;
There are other concerns but that&amp;#039;s the general thought process.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Can&amp;#039;t break what you can&amp;#039;t touch&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;can_t_break_what_you_can_t_touch&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:5,&amp;quot;range&amp;quot;:&amp;quot;1345-2254&amp;quot;} --&gt;
&lt;h1 class=&quot;sectionedit6&quot; id=&quot;machines&quot;&gt;Machines&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Machines&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;machines&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:6,&amp;quot;range&amp;quot;:&amp;quot;2255-2278&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit7&quot; id=&quot;public&quot;&gt;Public&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
For the public sphere, I use cloud-based Linux VMs from one of the affordable providers. I run most of my stuff on a single shared CPU VM with 4 CPU cores, 4gb of RAM and 50gb of disk space (storage is a later section).
&lt;/p&gt;

&lt;p&gt;
For things that need to be exposed in the internet I think cloud is the best choice. From a network isolation perspective serving things from your home means untrusted traffic will be flowing within your home network. Apart from security concerns there are other problems:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Serving things on a residential connection is sometimes outside of the TOS of your service provider&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; If your connection is asymmetric performance can be low, especially when serving large files&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Bandwidth always inferior to cloud&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Dynamic IP addresses makes access annoying&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1 node&quot;&gt;&lt;div class=&quot;li&quot;&gt; Advertising your home IP address is a mild security concern&lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; can be mitigated if you rent an ultra cheap VPS and use it as a gateway; I did this initially but realized I can run everything on a cheap vps to begin with&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Weather / residential power outages impact uptime&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
The main advantages of hosting at home are that it&amp;#039;s cheaper over the long run, you aren&amp;#039;t subject to cloud provider TOS and pricing changes, and are unaffected by cloud outages. The overall balance is in cloud&amp;#039;s favor for services that need to be served to the public internet.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Public&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;public&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:7,&amp;quot;range&amp;quot;:&amp;quot;2279-3642&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit8&quot; id=&quot;private&quot;&gt;Private&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
For my private sphere, I have a home server. This a normal mATX desktop computer although the board is a server board and supports ECC memory, which I use.
&lt;/p&gt;

&lt;p&gt;
Build details:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Case: Fractal Node 804 &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Motherboard: &lt;a href=&quot;https://www.asrockrack.com/general/productdetail.asp?Model=X570D4U#Specifications&quot; class=&quot;urlextern&quot; title=&quot;https://www.asrockrack.com/general/productdetail.asp?Model=X570D4U#Specifications&quot; rel=&quot;ugc nofollow&quot;&gt;Asrock RACK X570D4U&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; RAM: 128gb ECC memory (4x &lt;a href=&quot;https://www.kingston.com/dataSheets/KSM32ED8_32ME.pdf&quot; class=&quot;urlextern&quot; title=&quot;https://www.kingston.com/dataSheets/KSM32ED8_32ME.pdf&quot; rel=&quot;ugc nofollow&quot;&gt;KSM32ED8/32ME&lt;/a&gt;)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1 node&quot;&gt;&lt;div class=&quot;li&quot;&gt; Storage:&lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; 4x WD Blue 3.5“ 4tb HDD&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; 4x Hitachi 3.5” 4tb HDD&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; 1tb WD Black NVME&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; CPU: AMD Ryzen 7 3800X&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; GPU: RTX 3070&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Cooling: That noctua air cooler everyone likes&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Comments:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; The system is headless, the graphics card is for GPGPU compute&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Very pleased with the Asrock RACK board; good IPMI and the section display for status codes is extremely useful&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Fractal Node build quality lives up to its rep, it&amp;#039;s a great server case&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/homeserver.jpg?id=personal_infrastructure&quot; class=&quot;media&quot; title=&quot;homeserver.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/homeserver.jpg?w=400&amp;amp;tok=af53df&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;photo of home server&quot; alt=&quot;photo of home server&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
8oz Red Bull for scale.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Private&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;private&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:8,&amp;quot;range&amp;quot;:&amp;quot;3643-4626&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit9&quot; id=&quot;os&quot;&gt;OS&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
On my home server, I run Proxmox. I don&amp;#039;t have many VMs on it - in fact, like the public sphere, most of my services run on a single VM. Some of the reasons I use a hypervisor:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1 node&quot;&gt;&lt;div class=&quot;li&quot;&gt; Much better control and monitoring of hardware resources&lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; Full suite of hardware monitoring tools built into a clean web interface; disk diagnostics, memory / CPU / network dashboards, etc.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; Ability to easily partition storage resources among VMs&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; Ability to set fine grained memory and CPU resource limits&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;level1 node&quot;&gt;&lt;div class=&quot;li&quot;&gt; Full snapshots for tenant VMs&lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; Ability to snapshot the entire machine (VM) at the “hardware” level before making major changes is very valuable; makes risky operations like &lt;abbr title=&quot;Operating System&quot;&gt;OS&lt;/abbr&gt; upgrades risk free&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;level1 node&quot;&gt;&lt;div class=&quot;li&quot;&gt; Ability to create new machines whenever I want to try something&lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; E.g. I have a Windows VM that runs some Windows-only server software&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; I can spin up a VM with resources of my choosing to try out an operating system or run some experiment without worrying about damaging any infrastructure I rely on&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Of course, you don&amp;#039;t really need a dedicated hypervisor to do any of this; you can do it all with KVM on a traditional bare metal server &lt;abbr title=&quot;Operating System&quot;&gt;OS&lt;/abbr&gt;. But it&amp;#039;s much easier and more convenient in a hypervisor and in practice, there&amp;#039;s very few downsides to the hypervisor.
&lt;/p&gt;

&lt;p&gt;
The main downside is that hardware passthrough can be tricky. I pass through the RTX 3070 to the VM, and then into docker containers, in order to get accelerated encoding for media related services. KVM GPU passthrough is annoying.
&lt;/p&gt;

&lt;p&gt;
The &lt;abbr title=&quot;Operating System&quot;&gt;OS&lt;/abbr&gt; that applications run on (ignoring Docker) is always a recent Ubuntu Server LTS with automatic security upgrades enabled. I find the Debian-based platform familiar, comfortable and stable. Ubuntu Server also has a very wide install base which means most problems can be resolved with a web search. This helps keep the maintenance overhead as low as possible. I enjoy building my infrastructure, but I do rely on it and dislike being forced to fix it, so choices in system software tend to be on the less exotic side.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;OS&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;os&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:9,&amp;quot;range&amp;quot;:&amp;quot;4627-6684&amp;quot;} --&gt;
&lt;h1 class=&quot;sectionedit10&quot; id=&quot;storage&quot;&gt;Storage&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
For public infrastructure I use cloud storage as detailed in the next section.
&lt;/p&gt;

&lt;p&gt;
For private infrastructure I have an 8x4tb HDD array.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/homeserver-drives.jpg?id=personal_infrastructure&quot; class=&quot;media&quot; title=&quot;homeserver-drives.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/homeserver-drives.jpg?w=400&amp;amp;tok=0bad7a&quot; class=&quot;media&quot; loading=&quot;lazy&quot; title=&quot;photo of home server drive racks&quot; alt=&quot;photo of home server drive racks&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
These drives form a ZFS pool-of-mirrors with two hot swappable spares:
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  pool: tank
config:
	NAME                        STATE     READ WRITE CKSUM
	tank                        ONLINE       0     0     0
	  mirror-0                  ONLINE       0     0     0
	    wwn-0x50014ee266927a7d  ONLINE       0     0     0
	    wwn-0x5000cca22bca7cbb  ONLINE       0     0     0
	  mirror-1                  ONLINE       0     0     0
	    wwn-0x50014ee2113d2fa4  ONLINE       0     0     0
	    wwn-0x5000cca22be46d4b  ONLINE       0     0     0
	  mirror-2                  ONLINE       0     0     0
	    wwn-0x50014ee2bbe84236  ONLINE       0     0     0
	    wwn-0x5000cca23de171c4  ONLINE       0     0     0
	spares
	  wwn-0x50014ee2bf43a534    AVAIL
	  wwn-0x50014ee2bbe843cf    AVAIL&lt;/pre&gt;

&lt;p&gt;
Total usable storage space is 12tb. One disk in each mirror pair can fail with no data loss.
&lt;/p&gt;

&lt;p&gt;
I carve out virtual disks from this pool and attach them to VMs as needed.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Storage&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;storage&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:10,&amp;quot;range&amp;quot;:&amp;quot;6685-7880&amp;quot;} --&gt;
&lt;h1 class=&quot;sectionedit11&quot; id=&quot;applications&quot;&gt;Applications&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
Over time I&amp;#039;ve gravitated towards
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; web applications&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; running in Docker containers&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; served by nginx reverse proxy&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;div id=&quot;mermaidContainer2&quot; style=&quot;position: relative; width:auto; height:auto&quot;&gt;&lt;span class=&quot;mermaid&quot; id=mermaidContent2 style=&quot;width:auto; height:auto&quot;&gt;
flowchart TD
  subgraph VM
    nginx(nginx)
    subgraph Docker
      direction LR
      app1(piwigo)
      app2(akkoma)
      app3(peertube)
      appel(. . .)
    end
  end
  inet((network)) --&gt; nginx --&gt; app1 &amp; app2 &amp; app3 &amp; appel
&lt;/span&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;
Most of the self-hostable software I use is web based. Containers offer a way to run applications without contaminating the host, with (usually) easy upgrades and good lifecycle management and without reliance on the &lt;abbr title=&quot;Operating System&quot;&gt;OS&lt;/abbr&gt; packaging system, which makes them platform agnostic. Serving everything via nginx reverse proxy simplifies web server configuration and simplifies TLS. nginx has great performance and security and since nginx reverse proxy to docker is a popular choice now, many applications now provide example nginx reverse proxy configurations, making configuration even easier.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Applications&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;applications&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:1,&amp;quot;secid&amp;quot;:11,&amp;quot;range&amp;quot;:&amp;quot;7881-8876&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit12&quot; id=&quot;docker_compose&quot;&gt;Docker Compose&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Every single application I run is a docker compose project. Docker compose makes it very easy to manage complicated multipart services as a unit. Many of the applications I run are complex web applications with an application component, a separate database (eg mysql or postgres), and sometimes additional components. Putting everything in docker compose means that bringing up the simplest to the most complicated of these applications is done the exact same way:
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;docker compose up&lt;/pre&gt;

&lt;p&gt;
All dependencies, environment variables, mounts, port bindings, application settings etc. are all completely specified either in the &lt;code&gt;docker-compose.yml&lt;/code&gt; file, the &lt;code&gt;.env&lt;/code&gt; file, and the persistent data files on disk, and all of these files are contained in a single directory.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Docker Compose&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;docker_compose&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:1,&amp;quot;secid&amp;quot;:12,&amp;quot;range&amp;quot;:&amp;quot;8877-9685&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit13&quot; id=&quot;disk_layout&quot;&gt;Disk layout&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
On my machines, I have a directory called &lt;code&gt;apps&lt;/code&gt; in my home directory. Within this &lt;code&gt;apps&lt;/code&gt; directory, there is a directory for each application I run, and within that directory there is a &lt;code&gt;docker-compose.yml&lt;/code&gt;, &lt;code&gt;.env&lt;/code&gt; and sometimes data files. 
&lt;/p&gt;

&lt;p&gt;
The entire state of any application is contained within that application&amp;#039;s directory. As long as I have that directory, I can bring the application back up with its previous state even if the host is completely destroyed.
&lt;/p&gt;

&lt;p&gt;
On disk, the situation looks like this:
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;qlyoung@private ~&amp;gt; tree --prune -P &amp;quot;docker-compose.yml&amp;quot; -L 3 apps
apps
├── calibre-web
│   └── docker-compose.yml
├── gitea
│   └── docker-compose.yml
├── healthchecks
│   └── docker-compose.yml
├── invidious
│   └── docker-compose.yml
├── jellyfin
│   └── docker-compose.yml
├── linkding
│   └── docker-compose.yml
├── miniflux
│   └── docker-compose.yml
├── nextcloud
│   └── docker-compose.yml
├── onlyoffice
│   └── docker-compose.yml
├── photoprism
│   └── docker-compose.yml
├── quassel-core
│   └── docker-compose.yml
├── tandoor
│   └── docker-compose.yml
...&lt;/pre&gt;

&lt;p&gt;
Data files and &lt;code&gt;.env&lt;/code&gt; are omitted here but live alongside each &lt;code&gt;docker-compose.yml&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
I set things up so that I only need a given application&amp;#039;s directory in order to completely restore its state. Consequently, I don&amp;#039;t use docker volumes since these are stored in &lt;code&gt;/var/lib/docker&lt;/code&gt;. I only use bind mounts. I think it might be possible to have &lt;code&gt;local&lt;/code&gt; driver volumes be located in a custom directory, but since bind mounts have always worked well for me, I haven&amp;#039;t tried it.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Disk layout&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;disk_layout&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:2,&amp;quot;secid&amp;quot;:13,&amp;quot;range&amp;quot;:&amp;quot;9686-11483&amp;quot;} --&gt;
&lt;h1 class=&quot;sectionedit14&quot; id=&quot;application_storage&quot;&gt;Application Storage&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Application Storage&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;application_storage&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:3,&amp;quot;secid&amp;quot;:14,&amp;quot;range&amp;quot;:&amp;quot;11484-11518&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit15&quot; id=&quot;private1&quot;&gt;Private&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Storage on my private infrastructure is easy. I have a 32tb ZFS pool in my home server; if my VM runs low on storage space, I just make the disk bigger. If 32tb is not enough, I will buy bigger disks.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Private&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;private1&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:3,&amp;quot;secid&amp;quot;:15,&amp;quot;range&amp;quot;:&amp;quot;11519-11741&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit16&quot; id=&quot;public1&quot;&gt;Public&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
On my public infrastructure, disk space is constrained. My primary VM has 50gb of disk space. I used to have storage volumes provisioned in my cloud provder that were attached to the VM, with data files stored by applications symlinked to a location on the volume. However, while you can get as much storage as you want this way without needing to upgrade the VM instance size, cloud providers know that and thus tend to charge quite a lot for storage.
&lt;/p&gt;

&lt;p&gt;
I use Backblaze B2 object storage, which is affordable and provides an S3 compatible &lt;abbr title=&quot;Application Programming Interface&quot;&gt;API&lt;/abbr&gt;. When selecting applications to fill some infrastructure need that will need to run on my public infra, I only select applications that either have minimal disk requirements or support the S3 &lt;abbr title=&quot;Application Programming Interface&quot;&gt;API&lt;/abbr&gt;. Each service has its own B2 bucket and its own access key that grants access only to that bucket.
&lt;/p&gt;

&lt;p&gt;
&lt;div id=&quot;mermaidContainer3&quot; style=&quot;position: relative; width:auto; height:auto&quot;&gt;&lt;span class=&quot;mermaid&quot; id=mermaidContent3 style=&quot;width:auto; height:auto&quot;&gt;
flowchart TD
subgraph VM
  akkoma
  piwigo
  peertube
  appel(. . .)
end
subgraph B2
  qlyoung-pleroma(qlyoung-pleroma 4.2gb)
  qlyoung-piwigo(qlyoung-piwigo 6.9gb)
  qlyoung-peertube(qlyoung-peertube 32gb)
  qlyoung-foobar(. . .)
end
akkoma -- S3 API + akkoma key --&gt; qlyoung-pleroma
piwigo -- S3 API + piwigo key --&gt; qlyoung-piwigo
peertube -- S3 API + peertube key --&gt; qlyoung-peertube
appel -- S3 API + ... key --&gt; qlyoung-foobar
&lt;/span&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;
If possible, I also configure applications so that users are served files directly from B2 rather than having them proxied through my VM, which improves performance both in terms of load time and bandwidth usage. When you watch a video on my Peertube server, your browser is downloading the video directly from B2, instead of &lt;code&gt;B2 → VM → You&lt;/code&gt;.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Public&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;public1&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:3,&amp;quot;secid&amp;quot;:16,&amp;quot;range&amp;quot;:&amp;quot;11742-13406&amp;quot;} --&gt;
&lt;h1 class=&quot;sectionedit17&quot; id=&quot;dns_http_tls&quot;&gt;DNS &amp;amp; HTTP &amp;amp; TLS&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
This is identical on both private and public. Grand overview:
&lt;/p&gt;

&lt;p&gt;
&lt;div id=&quot;mermaidContainer4&quot; style=&quot;position: relative; width:auto; height:auto&quot;&gt;&lt;span class=&quot;mermaid&quot; id=mermaidContent4 style=&quot;width:auto; height:auto&quot;&gt;
flowchart TD
  subgraph VM
    nginx(nginx 0.0.0.0:443)
    subgraph Docker
      direction LR
      piwigo(piwigo 127.0.0.1:8000)
      akkoma(akkoma 127.0.0.1:8001)
      peertube(peertube 127.0.0.1:8002)
      appel(. . .)
    end
  end
  photos.qlyoung.net &amp; social.qlyoung.net &amp; qtube.qlyoung.net &amp; el(. . .) -- CNAME --&gt; qlyoung.net -- A --&gt; VM
  nginx -- photos.qlyoung.net --&gt; piwigo
  nginx -- social.qlyoung.net --&gt; akkoma
  nginx -- qtube.qlyoung.net --&gt; peertube
  nginx -- ... --&gt; appel
&lt;/span&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;DNS &amp;amp; HTTP &amp;amp; TLS&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;dns_http_tls&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:3,&amp;quot;secid&amp;quot;:17,&amp;quot;range&amp;quot;:&amp;quot;13407-14023&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit18&quot; id=&quot;dns&quot;&gt;DNS&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Each service is on its own subdomain. All subdomains &lt;code&gt;CNAME&lt;/code&gt; to the main domain name (&lt;code&gt;qlyoung.net&lt;/code&gt;) which has an &lt;code&gt;A&lt;/code&gt; pointing to the public IP of the VM. In my private infrastructure, the &lt;code&gt;A&lt;/code&gt; record instead points to a Tailscale IP - but more on that in the networking section.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;DNS&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;dns&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:3,&amp;quot;secid&amp;quot;:18,&amp;quot;range&amp;quot;:&amp;quot;14024-14328&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit19&quot; id=&quot;http&quot;&gt;HTTP&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
nginx runs on the host and binds host ports 80 and 443. All docker containers bind to &lt;code&gt;(127.0.0.1, P)&lt;/code&gt; where &lt;code&gt;P&lt;/code&gt; is a host port number of my choosing. Each service has its own subdomain and a corresponding nginx configuration:
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;root@public /e/n/sites-available# ls | sort
birdcam.conf
maloja.conf
piwigo.conf
pleroma.conf
qlyoung.conf
qtube.conf
wiki.conf
...&lt;/pre&gt;

&lt;p&gt;
Each of these looks more or less the same, and contains configuration to reverse proxy to the corresponding Docker container. For example, here is &lt;code&gt;piwigo.conf&lt;/code&gt;. It reverse proxies requests to &lt;code&gt;photos.qlyoung.net&lt;/code&gt; to &lt;code&gt;127.0.0.1:8090&lt;/code&gt;, which is where the container running Piwigo is bound:
&lt;/p&gt;
&lt;pre class=&quot;code nginx&quot;&gt;&lt;a href=&quot;http://wiki.nginx.org/NginxHttpCoreModule#server&quot;&gt;&lt;span class=&quot;kw3&quot;&gt;server&lt;/span&gt;&lt;/a&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;a href=&quot;http://wiki.nginx.org/NginxHttpCoreModule#server_name&quot;&gt;&lt;span class=&quot;kw3&quot;&gt;server_name&lt;/span&gt;&lt;/a&gt; photos.qlyoung.net&lt;span class=&quot;sy0&quot;&gt;;&lt;/span&gt;
&amp;nbsp;
    &lt;a href=&quot;http://wiki.nginx.org/NginxHttpCoreModule#location&quot;&gt;&lt;span class=&quot;kw3&quot;&gt;location&lt;/span&gt;&lt;/a&gt; / &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
         &lt;a href=&quot;http://wiki.nginx.org/NginxHttpProxyModule#proxy_pass&quot;&gt;&lt;span class=&quot;kw23&quot;&gt;proxy_pass&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;http://wiki.nginx.org/NginxHttpCoreModule#http&quot;&gt;&lt;span class=&quot;kw3&quot;&gt;http&lt;/span&gt;&lt;/a&gt;://127.0.0.1:8090&lt;span class=&quot;sy0&quot;&gt;;&lt;/span&gt;
         &lt;a href=&quot;http://wiki.nginx.org/NginxHttpProxyModule#proxy_set_header&quot;&gt;&lt;span class=&quot;kw23&quot;&gt;proxy_set_header&lt;/span&gt;&lt;/a&gt; X-Forwarded-Host &lt;span class=&quot;re0&quot;&gt;$server_name&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;;&lt;/span&gt;
         &lt;a href=&quot;http://wiki.nginx.org/NginxHttpProxyModule#proxy_set_header&quot;&gt;&lt;span class=&quot;kw23&quot;&gt;proxy_set_header&lt;/span&gt;&lt;/a&gt; X-Forwarded-Proto https&lt;span class=&quot;sy0&quot;&gt;;&lt;/span&gt;
         &lt;a href=&quot;http://wiki.nginx.org/NginxHttpProxyModule#proxy_set_header&quot;&gt;&lt;span class=&quot;kw23&quot;&gt;proxy_set_header&lt;/span&gt;&lt;/a&gt; X-Forwarded-For &lt;span class=&quot;re0&quot;&gt;$remote_addr&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
    &lt;a href=&quot;http://wiki.nginx.org/NginxHttpCoreModule#listen&quot;&gt;&lt;span class=&quot;kw3&quot;&gt;listen&lt;/span&gt;&lt;/a&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;::&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;:443 &lt;a href=&quot;http://wiki.nginx.org/NginxHttpSslModule#ssl&quot;&gt;&lt;span class=&quot;kw33&quot;&gt;ssl&lt;/span&gt;&lt;/a&gt; ipv6only&lt;span class=&quot;sy0&quot;&gt;=&lt;/span&gt;on&lt;span class=&quot;sy0&quot;&gt;;&lt;/span&gt;
    &lt;a href=&quot;http://wiki.nginx.org/NginxHttpCoreModule#listen&quot;&gt;&lt;span class=&quot;kw3&quot;&gt;listen&lt;/span&gt;&lt;/a&gt; 443 &lt;a href=&quot;http://wiki.nginx.org/NginxHttpSslModule#ssl&quot;&gt;&lt;span class=&quot;kw33&quot;&gt;ssl&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;sy0&quot;&gt;;&lt;/span&gt;
&amp;nbsp;
    &amp;lt;... tls details, misc settings omitted ...&amp;gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;
Note that Piwigo itself is bound to port 80 within the container, which is mapped to 8090 on the host:
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;qlyoung@public ~/a/piwigo&amp;gt; docker-compose ps
     Name                    Command               State                Ports
------------------------------------------------------------------------------------------
piwigo_mysql_1    docker-entrypoint.sh --def ...   Up      3306/tcp, 33060/tcp
piwigo_piwigo_1   /init                            Up      443/tcp, 127.0.0.1:8090-&amp;gt;80/tcp&lt;/pre&gt;

&lt;p&gt;
I find this setup to be clean and convenient. Service specific settings such as request body size are set in the nginx configuration and only need an nginx reload to be applied. Sites can be made accessible or not by enabling or disabling their nginx configurations without touching the application itself.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;HTTP&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;http&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:3,&amp;quot;secid&amp;quot;:19,&amp;quot;range&amp;quot;:&amp;quot;14329-16242&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit20&quot; id=&quot;tls&quot;&gt;TLS&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
TLS is terminated by nginx. This makes TLS management very simple, since certificates can be issued and configured in nginx using Certbot with the nginx plugin. No container specific configuration is necessary. I think this is the correct separation of concerns - TLS configuration is a detail relevant to how services are exposed, but not to the services themselves. Having TLS configuration live in the web server with services unaware of it makes sense.
&lt;/p&gt;

&lt;p&gt;
Certbot issues certificates using Let&amp;#039;s Encrypt. Certbot automatically renews certificates so there is no maintenance overhead.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;TLS&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;tls&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:6,&amp;quot;secid&amp;quot;:20,&amp;quot;range&amp;quot;:&amp;quot;16243-16847&amp;quot;} --&gt;
&lt;h1 class=&quot;sectionedit21&quot; id=&quot;networking&quot;&gt;Networking&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Networking&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;networking&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:6,&amp;quot;secid&amp;quot;:21,&amp;quot;range&amp;quot;:&amp;quot;16848-16873&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit22&quot; id=&quot;public2&quot;&gt;Public&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Everything is served on public IP addresses. &lt;abbr title=&quot;Domain Name System&quot;&gt;DNS&lt;/abbr&gt; is managed through my &lt;abbr title=&quot;Domain Name System&quot;&gt;DNS&lt;/abbr&gt; provider (Namecheap).
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Public&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;public2&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:6,&amp;quot;secid&amp;quot;:22,&amp;quot;range&amp;quot;:&amp;quot;16874-16991&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit23&quot; id=&quot;private2&quot;&gt;Private&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
My home server and all my personal computers are part of a single Tailscale network (tailnet). The network topology is full mesh.
&lt;/p&gt;

&lt;p&gt;
&lt;div id=&quot;mermaidContainer5&quot; style=&quot;position: relative; width:auto; height:auto&quot;&gt;&lt;span class=&quot;mermaid&quot; id=mermaidContent5 style=&quot;width:auto; height:auto&quot;&gt;
flowchart LR
 subgraph Tailnet
   laptop
   desktop
   server
   pihole(odroid )
   el(. . .)
   laptop --- desktop &amp; server &amp; pihole &amp; el
   desktop --- server &amp; pihole &amp; el
   server --- pihole &amp; el
   pihole --- el
 end
&lt;/span&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;
For &lt;abbr title=&quot;Domain Name System&quot;&gt;DNS&lt;/abbr&gt;, I have a small SBC &lt;a href=&quot;https://www.hardkernel.com/shop/odroid-xu4-special-price/&quot; class=&quot;urlextern&quot; title=&quot;https://www.hardkernel.com/shop/odroid-xu4-special-price/&quot; rel=&quot;ugc nofollow&quot;&gt;odroid-xu4&lt;/a&gt; running Pihole which is also part of the tailnet (&lt;code&gt;odroid&lt;/code&gt; in the diagram above). The tailscale daemon on each device configures the device to use that &lt;abbr title=&quot;Domain Name System&quot;&gt;DNS&lt;/abbr&gt; server when resolving &lt;code&gt;*.qlyoung.net&lt;/code&gt; domains. Otherwise &lt;abbr title=&quot;Domain Name System&quot;&gt;DNS&lt;/abbr&gt; is the same as previously described, except that the &lt;code&gt;A&lt;/code&gt; record for the primary VM points to a Tailscale address.
&lt;/p&gt;

&lt;p&gt;
&lt;div id=&quot;mermaidContainer6&quot; style=&quot;position: relative; width:auto; height:auto&quot;&gt;&lt;span class=&quot;mermaid&quot; id=mermaidContent6 style=&quot;width:auto; height:auto&quot;&gt;
flowchart TD
  subgraph VM
    tip(Tailscale IP) === nginx(nginx)
  end
  git.qlyoung.net &amp; healthchecks.qlyoung.net &amp; jf.qlyoung.net &amp; links.qlyoung.net &amp; el(. . .) -- CNAME --&gt; private.qlyoung.net -- A --&gt; tip
&lt;/span&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;
On the VM nginx is configured to only bind to the Tailscale address. This ensures applications are only accessible by devices explicitly joined to the tailnet, and not by e.g. a guest who has joined my home &lt;abbr title=&quot;Local Area Network&quot;&gt;LAN&lt;/abbr&gt;.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Private&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;private2&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:6,&amp;quot;secid&amp;quot;:23,&amp;quot;range&amp;quot;:&amp;quot;16992-18276&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit24&quot; id=&quot;typical_deployment&quot;&gt;Typical Deployment&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
1. Identify a need, e.g., “I want a personal recipe book and meal planner”
&lt;/p&gt;

&lt;p&gt;
2. Search for Web-based projects that fill that need, select one based on presence of Docker support, features &amp;amp; community activity, e.g. &lt;a href=&quot;https://tandoor.dev/&quot; class=&quot;urlextern&quot; title=&quot;https://tandoor.dev/&quot; rel=&quot;ugc nofollow&quot;&gt;Tandoor&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
3. Log into server, clone, configure, and start application (pseudocode)
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;   $ &lt;span class=&quot;kw2&quot;&gt;ssh&lt;/span&gt; server
   &lt;span class=&quot;co0&quot;&gt;# set up directories&lt;/span&gt;
   $ &lt;span class=&quot;kw2&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;re5&quot;&gt;-p&lt;/span&gt; apps&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;tandoor &lt;span class=&quot;sy0&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;kw3&quot;&gt;cd&lt;/span&gt; apps&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;tandoor
   $ &lt;span class=&quot;kw2&quot;&gt;wget&lt;/span&gt; docker-compose.yml .env
   &lt;span class=&quot;co0&quot;&gt;# make any changes; set port bindings, env variables to set credentials, etc&lt;/span&gt;
   $ &lt;span class=&quot;kw2&quot;&gt;vim&lt;/span&gt; docker-compose.yml .env
   &lt;span class=&quot;co0&quot;&gt;# start service&lt;/span&gt;
   $ docker compose up &lt;span class=&quot;re5&quot;&gt;-d&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;
4. Set up new &lt;code&gt;CNAME&lt;/code&gt; record; &lt;code&gt;CNAME recipes.qlyoung.net → qlyoung.net&lt;/code&gt;
5. Configure nginx, request and install TLS certificate
&lt;/p&gt;
&lt;pre class=&quot;code bash&quot;&gt;   &lt;span class=&quot;co0&quot;&gt;# create nginx configuration, usually by copying another one and tweaking it&lt;/span&gt;
   $ &lt;span class=&quot;kw2&quot;&gt;vim&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;sites-available&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;tandoor.conf
   &lt;span class=&quot;co0&quot;&gt;# enable nginx with chosen &lt;/span&gt;
   $ &lt;span class=&quot;kw2&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;re5&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;sites-available&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;tandoor.conf &lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;sites-enabled&lt;span class=&quot;sy0&quot;&gt;/&lt;/span&gt;tandoor.conf
   &lt;span class=&quot;co0&quot;&gt;# Log into namecheap, create new CNAME record (recipes.qlyoung.net)&lt;/span&gt;
   &lt;span class=&quot;co0&quot;&gt;# Set up TLS&lt;/span&gt;
   $ certbot &lt;span class=&quot;re5&quot;&gt;--nginx&lt;/span&gt; &lt;span class=&quot;re5&quot;&gt;-d&lt;/span&gt; recipes.qlyoung.net&lt;/pre&gt;

&lt;p&gt;
6. ???
&lt;/p&gt;

&lt;p&gt;
7. Profit
&lt;/p&gt;

&lt;p&gt;
The deployment process is identical for both internal and external services.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Typical Deployment&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;typical_deployment&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:6,&amp;quot;secid&amp;quot;:24,&amp;quot;range&amp;quot;:&amp;quot;18277-19561&amp;quot;} --&gt;
&lt;h1 class=&quot;sectionedit25&quot; id=&quot;backups&quot;&gt;Backups&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
Everything, private and public, is backed up with &lt;a href=&quot;https://restic.net/&quot; class=&quot;urlextern&quot; title=&quot;https://restic.net/&quot; rel=&quot;ugc nofollow&quot;&gt;restic&lt;/a&gt; to offsite locations. It runs daily on a &lt;code&gt;cron&lt;/code&gt; job.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Backups&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;backups&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:8,&amp;quot;secid&amp;quot;:25,&amp;quot;range&amp;quot;:&amp;quot;19562-19721&amp;quot;} --&gt;
&lt;h1 class=&quot;sectionedit26&quot; id=&quot;cost&quot;&gt;Cost&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
Private runs for the cost of electricity.
&lt;/p&gt;

&lt;p&gt;
Public bill:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Compute: $24/mo&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; B2 Storage (~200gb): ~$2/mo&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Total: ~$26/mo&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/from_blog?do=showtag&amp;amp;tag=from_blog&quot; class=&quot;wikilink1&quot; title=&quot;tag:from_blog&quot; rel=&quot;tag&quot;&gt;from blog&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/technology?do=showtag&amp;amp;tag=technology&quot; class=&quot;wikilink1&quot; title=&quot;tag:technology&quot; rel=&quot;tag&quot;&gt;technology&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Cost&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;cost&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:8,&amp;quot;secid&amp;quot;:26,&amp;quot;range&amp;quot;:&amp;quot;19722-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/photo_management">
        <dc:format>text/html</dc:format>
        <dc:date>2025-11-04T03:19:37+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>photo_management</title>
        <link>https://wiki.qlyoung.net/photo_management</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;photo_management&quot;&gt;photo management&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Oh yeah. This is gonna be a big one. Stay tuned.
&lt;/p&gt;

&lt;p&gt;
Rough outline for now:
&lt;/p&gt;

&lt;p&gt;
&lt;div id=&quot;mermaidContainer0&quot; style=&quot;position: relative; width:auto; height:auto&quot;&gt;&lt;span class=&quot;mermaid&quot; id=mermaidContent0 style=&quot;width:auto; height:auto&quot;&gt;
flowchart TD
    subgraph personal server
        I[import] --&gt; |phockup| L[Library]
    end
    subgraph web
        S(social)
    end
    subgraph desktop
        D --&gt; |baked jpg| S
        D --&gt; |raw + xmp| I
    end
    A[fa:fa-camera GoPro] --&gt; D(Darktable)
    C[fa:fa-paper-plane Drone] --&gt; D
    B[fa:fa-camera iPhone] --&gt; |HEIC + MOV| I
&lt;/span&gt;&lt;/div&gt;
&lt;/p&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/technology?do=showtag&amp;amp;tag=technology&quot; class=&quot;wikilink1&quot; title=&quot;tag:technology&quot; rel=&quot;tag&quot;&gt;technology&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/photography?do=showtag&amp;amp;tag=photography&quot; class=&quot;wikilink1&quot; title=&quot;tag:photography&quot; rel=&quot;tag&quot;&gt;photography&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/self-hosting?do=showtag&amp;amp;tag=self-hosting&quot; class=&quot;wikilink1&quot; title=&quot;tag:self-hosting&quot; rel=&quot;tag&quot;&gt;self-hosting&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/possession_minimalism">
        <dc:format>text/html</dc:format>
        <dc:date>2024-07-02T21:58:15+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>possession_minimalism</title>
        <link>https://wiki.qlyoung.net/possession_minimalism</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;possession_minimalism&quot;&gt;possession minimalism&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Earlier in my life, I conceptualized the ideal amount of possessions as the amount that I could load into a car. The idea was that this conferred maximal flexibility and mobility. If I didn&amp;#039;t like where I was there were no barriers to picking up and leaving. This was pretty easy to do early on since I had no money and couldn&amp;#039;t afford anything more than a plastic tub of clothes and a few small trinkets anyway.
&lt;/p&gt;

&lt;p&gt;
Eventually though I did get some money, and after a few years of living in the same place and not using the mobility capability, I realized this self imposed prohibition was preventing me from doing things in life I wanted to do. For example, I couldn&amp;#039;t scuba dive as much as I wanted to because while I had training, I wouldn&amp;#039;t allow myself to buy gear - scuba gear, especially tanks, is bulky. Owning dive gear isn&amp;#039;t amenable to maintaining mobility.
&lt;/p&gt;

&lt;p&gt;
Put another way, maintaining mobility was, at that point, significantly limiting my personal growth. I decided it was no longer worth it. Starting with dive gear, I began allowing myself to accrue possessions in order to do what I was interested in.
&lt;/p&gt;

&lt;p&gt;
Now I find myself approaching a big move and surrounded by many possessions. These are almost exclusively related to hobbies and sports - snowboards, skateboards, rackmount servers, firearms, diving equipment, climbing equipment, etc. I have quite a lot of things, and thinking about my past viewpoints, I&amp;#039;ve tentatively decided that moderate minimalism is probably the best policy for possessions. That is, keep around the minimum amount of stuff that allows you to do what you want to do, and no more.
&lt;/p&gt;

&lt;p&gt;
I don&amp;#039;t shoot much anymore so I&amp;#039;m selling my guns. I have an electronic keyboard, but while I would like to pretend I&amp;#039;m going to play it soon, it hasn&amp;#039;t happened for a few years so I&amp;#039;m selling it. Anything I haven&amp;#039;t used for a year is on the chopping block. I&amp;#039;ve found that unless I perform regular, aggressive purges eventually I build up so much crap that takes up space and weighs me down both physically and mentally. It&amp;#039;s an axiom that moving is easier the less stuff you have, but for me, having less stuff also makes me tangibly happier. It&amp;#039;s less to manage and maintain. I especially like selling or donating things because I&amp;#039;m not throwing them into a landfill, but making sure they end up with someone who will actually put them to use.
&lt;/p&gt;

&lt;p&gt;
Another thought for this disorganized list: many people have a viewpoint that they shouldn&amp;#039;t buy something expensive unless they know they&amp;#039;re going to use it. It sounds right on the surface, but what I&amp;#039;ve found is that in many cases, items keep their resale value so well that it actually costs much less than you think to own expensive things that you end up not using. For example, in 2021 I got into flight simulation and was doing it for long enough that I decided to purchase a very nice HOTAS system costing around $500. I waited months to buy this, living with a $30 logitech joystick, because I wanted to be sure I was going to stick with the hobby before spending so much on equipment. As often happens, some months after getting the HOTAS I stopped flight simming and the system went back in its box for over a year. Finally in 2023 I admitted I wasn&amp;#039;t getting back into it and sold the throttle - for $426. Owning an extremely premium throttle for 1 year 8 months cost me $75. Well worth it since it allowed me to explore flight simming completely without wondering if better equipment would enhance my experience! And now someone else has a like-new unit, at a discount, who is probably putting it to good use. Everyone wins.
&lt;/p&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/humanity?do=showtag&amp;amp;tag=humanity&quot; class=&quot;wikilink1&quot; title=&quot;tag:humanity&quot; rel=&quot;tag&quot;&gt;humanity&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/philosophy?do=showtag&amp;amp;tag=philosophy&quot; class=&quot;wikilink1&quot; title=&quot;tag:philosophy&quot; rel=&quot;tag&quot;&gt;philosophy&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/power_juice">
        <dc:format>text/html</dc:format>
        <dc:date>2025-12-23T18:46:28+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>power_juice</title>
        <link>https://wiki.qlyoung.net/power_juice</link>
        <description>&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; 1.5 cups orange juice, with pulp&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; 1 tablespoon creatine&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; 2 pumps liquid caffeine (appx 160mg)&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
stir to combine
&lt;/p&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/quotes">
        <dc:format>text/html</dc:format>
        <dc:date>2025-12-19T00:15:30+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>quotes</title>
        <link>https://wiki.qlyoung.net/quotes</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;quotes&quot;&gt;quotes&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Just some quotes I like. They don&amp;#039;t necessarily represent my views on anything.
&lt;/p&gt;
&lt;hr /&gt;
&lt;blockquote class=&quot;blockquote-plugin&quot;&gt;
&lt;p&gt;
Fall in love with some activity, and do it! Nobody ever figures out what life is all about, and it doesn’t matter. Explore the world. Nearly everything is really interesting if you go into it deeply enough. Work as hard and as much as you want to on the things you like to do the best. Don’t think about what you want to be, but what you want to do. Keep up some kind of a minimum with other things so that society doesn’t stop you from doing anything at all.
&lt;cite class=&quot;blockquote-plugin&quot;&gt;Richard Feynman&lt;/cite&gt;
&lt;/p&gt;

&lt;/blockquote&gt;&lt;blockquote class=&quot;blockquote-plugin&quot;&gt;
&lt;p&gt;
You can acquire infinite customers and create infinite revenue if you sell dollars for 85 cents.
&lt;cite class=&quot;blockquote-plugin&quot;&gt;Uncredited&lt;/cite&gt;
&lt;/p&gt;

&lt;/blockquote&gt;&lt;blockquote class=&quot;blockquote-plugin&quot;&gt;
&lt;p&gt;
Mood? What&amp;#039;s mood to do with it? You fight when the necessity arises, no matter the mood.
&lt;cite class=&quot;blockquote-plugin&quot;&gt;Gurney Halleck&lt;/cite&gt;
&lt;/p&gt;

&lt;/blockquote&gt;
&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/restaurants">
        <dc:format>text/html</dc:format>
        <dc:date>2025-10-10T20:20:04+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>restaurants</title>
        <link>https://wiki.qlyoung.net/restaurants</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;restaurants&quot;&gt;restaurants&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
I&amp;#039;m a bit of a foodie and enjoy finding interesting places to eat. On this page is a list of notable ones, organized by region.
&lt;/p&gt;

&lt;p&gt;
Roughly speaking, “notable” means a place I might bring up in a conversation about food in that region or about that particular cuisine.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;restaurants&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;restaurants&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-293&amp;quot;} --&gt;
&lt;h2 class=&quot;sectionedit2&quot; id=&quot;usa&quot;&gt;USA&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;USA&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;usa&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;294-310&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit3&quot; id=&quot;nc&quot;&gt;NC&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;

&lt;h4 id=&quot;raleigh&quot;&gt;Raleigh&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://brodeto.com/&quot; class=&quot;urlextern&quot; title=&quot;https://brodeto.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Brodeto&lt;/a&gt; - Adriatic. Seafood and pasta. This one is truly special, by far the best food I have had in Raleigh and the best on the East coast outside of NYC. Interesting menu. The interior design is sleek and organic, wood and stone with white highlights.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://brewerybhavana.com/&quot; class=&quot;urlextern&quot; title=&quot;https://brewerybhavana.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Brewery Bhavana&lt;/a&gt; - dim sum and beer. Half of the restaurant is a brewery and the other half is dining, with a cafe-like space in the middle. Probably the best Asian food you can get in Raleigh and a thoughtful selection of beer. Not cheap but worth it.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://restaurantjolie.com/&quot; class=&quot;urlextern&quot; title=&quot;https://restaurantjolie.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Jolie&lt;/a&gt; - French bistro. Rooftop digs are worth advance booking, especially around sunset. Steak tartare is served &amp;#039;deconstructed&amp;#039; style and if you like that dish, definitely get it. Duck confit is also highlight. This is by the same chef as Brodeto. I went here about a year before Brodeto and thought the food was good although the menu is somewhat boring.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://ac-restaurants.com/beasleys/&quot; class=&quot;urlextern&quot; title=&quot;http://ac-restaurants.com/beasleys/&quot; rel=&quot;ugc nofollow&quot;&gt;Beasley&amp;#039;s Chicken and Honey&lt;/a&gt; - Southern. My overall favorite chicken and waffle spot, although not far ahead of Tupelo. Probably the only Ashley Christensen restaurant that I would go to. I feel like the rest of them are trying to do fine cuisine with the same attitude you should make c&amp;amp;w with. Love their hot sauce.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://ac-restaurants.com/death-taxes/&quot; class=&quot;urlextern&quot; title=&quot;https://ac-restaurants.com/death-taxes/&quot; rel=&quot;ugc nofollow&quot;&gt;Death &amp;amp; Taxes&lt;/a&gt; - upscale American? Not a fan of the food at all. Think $50 steak and pasta with the richness and palate of Carraba&amp;#039;s fettucine alfredo - very rich, salty, fatty, filling, exactly the opposite of what I want in upscale dining. The most interesting dish I could find, and had, was pork and breaded onions. Salmon tartare with the fish drowned in sauce. This is quite similar to the other “upscale” Ashley Christensen restaurant I&amp;#039;ve been to (Poole&amp;#039;s) and I think I&amp;#039;m seeing the formula. Overall did not like it, both as a matter of personal taste in the cuisine direction but also because the food was just bad. The bread was stale, parts of my dish were burnt. The service was quite good but the mediocre food made it feel like an average restaurant that watched a tv show about fine dining and copied what they saw.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.sitti-raleigh.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.sitti-raleigh.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Sitti&lt;/a&gt; - Lebanese food. Right next door to Gravy. First visit was for lunch and I did not like it at all. On a subsequent dinner visit, I found it excellent. Straightforward but delicious menu.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://cafeluna.com/&quot; class=&quot;urlextern&quot; title=&quot;https://cafeluna.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Caffe Luna&lt;/a&gt; - Italian. Well priced with a small menu. Only went once and it was good. I would absolutely go again if I wanted home cooked Italian.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://ac-restaurants.com/pooles/&quot; class=&quot;urlextern&quot; title=&quot;https://ac-restaurants.com/pooles/&quot; rel=&quot;ugc nofollow&quot;&gt;Poole&amp;#039;s&lt;/a&gt; - American. People say this is the best or one of the best around. I personally found the menu, while enjoyable, somewhat boring. Like D&amp;amp;T, the food is pretty basic fare, large portions. I kinda got the impression that the menu is optimized around Instagram compatibility.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.bigeasync.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.bigeasync.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Big Easy&lt;/a&gt; - Trash. More like the big suck, don&amp;#039;t go here.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.gravyraleigh.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.gravyraleigh.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Gravy&lt;/a&gt; - Italian. Was (may still be) semi-upscale Italian. Went there many many times, but had one bad experience where the food was terrible and didn&amp;#039;t return.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.hibernianpub.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.hibernianpub.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Hibernian&lt;/a&gt; - Irish pub. Honestly one of my favorite spots around for a casual bite. On a night with great weather the roof is one of my favorite spots to be. It&amp;#039;s a pub, so the food is not really the draw, but the food is reliable and the place you eat them in is comfortable, cozy and tucked away from the chaos outside. Also the only place to get an authentic Irish breakfast around here, which I appreciated much more after visiting Ireland.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.whiskey.kitchen/&quot; class=&quot;urlextern&quot; title=&quot;https://www.whiskey.kitchen/&quot; rel=&quot;ugc nofollow&quot;&gt;Whiskey Kitchen&lt;/a&gt; - American. Food is good and slightly overpriced. This is more of a whiskey bar than a restaurant. If you want to drink and also eat, this is a great spot.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.salsafreshgrill.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.salsafreshgrill.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Salsa Fresh&lt;/a&gt; - Mexican. This is an S tier lunch spot with simple Mexican fare and a very fresh, very delicious salsa bar.  For whatever reason the Morrisville location is way better.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.samjonesbbq.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.samjonesbbq.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Sam Jones BBQ&lt;/a&gt; - BBQ. They have bar seating and I&amp;#039;ve had an interesting conversation with a stranger next to me. My buddy hated it.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.warajijapaneserestaurant.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.warajijapaneserestaurant.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Waraji&lt;/a&gt; - Sushi. Surprisingly good sushi as NC goes, in an unexpected location. If I want good sushi in Raleigh this is my goto.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://mulinoraleigh.com/&quot; class=&quot;urlextern&quot; title=&quot;https://mulinoraleigh.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Mulino&lt;/a&gt; - Italian. Me and my friend found the food overpriced and the service somewhat slow. Drinks were good.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.tonboramen.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.tonboramen.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Tonbo Ramen&lt;/a&gt; - Ramen. I was notably unimpressed and found the food bland.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.misoramenbar.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.misoramenbar.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Miso Ramen Bar&lt;/a&gt; - Ramen. There&amp;#039;s three locations; I&amp;#039;ve been to the Raleigh location twice and liked my food each time. Solid ramen spot.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://pressccc.com/&quot; class=&quot;urlextern&quot; title=&quot;https://pressccc.com/&quot; rel=&quot;ugc nofollow&quot;&gt;PRESS Coffee+Crepes&lt;/a&gt; - Crepes and a cocktail bar. I went there for a crepe and a flat white. It was OK but I couldn&amp;#039;t stop thinking that for $25 I got a lukewarm crepe, a tiny flat white and no service when I&amp;#039;d get double the portions of both with better quality for like €8-10 anywhere in Europe. Unlikely to return.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;h4 id=&quot;cary&quot;&gt;Cary&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.noodleblvd.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.noodleblvd.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Noodle Blvd&lt;/a&gt; - Ramen. The most reliable ramen spot in the Triangle that I have been to. I like their spicy seafood kimchi ramen best.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;h4 id=&quot;durham&quot;&gt;Durham&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.saltboxseafoodjoint.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.saltboxseafoodjoint.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Saltbox Seafood Joint&lt;/a&gt; - Fresh seafood. Daily menu. Straightforward, delicious meals.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.thaiangleofdurham.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.thaiangleofdurham.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Thaiangle&lt;/a&gt; - Got green curry. It was ok, next time I want Thai I&amp;#039;ll try somewhere else. Service was *too* attentive, interruption my meal to refill water, ask about the food multiple times etc.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;NC&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;nc&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;311-6173&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit4&quot; id=&quot;seattle&quot;&gt;Seattle&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.wazseattle.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.wazseattle.com/&quot; rel=&quot;ugc nofollow&quot;&gt;wa&amp;#039;z&lt;/a&gt; - Kaiseki - this ranks among the absolute best places I have ever eaten. It may be the best place I have ever eaten. I am surprised they do not have a star. Extremely delicious. Extremely expensive - but worth it.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://sushikashiba.com/&quot; class=&quot;urlextern&quot; title=&quot;https://sushikashiba.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Sushi Kashiba&lt;/a&gt; - Sushi - Also very good. It&amp;#039;s high end sushi with matching service for about as much as you would expect.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.kamonegiseattle.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.kamonegiseattle.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Kamonegi&lt;/a&gt; - Japanese - My first experience with soba, not one to be forgotten. Can be a bit hard to get in here but worth it.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Seattle&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;seattle&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:4,&amp;quot;range&amp;quot;:&amp;quot;6174-6769&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit5&quot; id=&quot;denver&quot;&gt;Denver&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.tavernettadenver.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.tavernettadenver.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Tavernetta&lt;/a&gt; - It&amp;#039;s hard to impress me with Italian but this place is very good. I particularly enjoyed their cheese selections.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://daughterthaikitchenandbar.com/&quot; class=&quot;urlextern&quot; title=&quot;https://daughterthaikitchenandbar.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Daughter Thai&lt;/a&gt; - I had khao soi for the first time here. On a later visit I had their lamb curry. Great food on both counts.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Denver&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;denver&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:5,&amp;quot;range&amp;quot;:&amp;quot;6770-7128&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit6&quot; id=&quot;los_angeles&quot;&gt;Los Angeles&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.chiangrailb.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.chiangrailb.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Chiang Rai&lt;/a&gt; - Thai. Amazing street food.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.cantersdeli.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.cantersdeli.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Canter&amp;#039;s Deli&lt;/a&gt; - Jewish deli. 24 hours and I have never been there without appreciating that. I love this place.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.roscoeschickenandwaffles.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.roscoeschickenandwaffles.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Roscoe&amp;#039;s House of Chicken and Waffles&lt;/a&gt; - Chicken and waffles. Yep. If that&amp;#039;s what you want, go to Roscoe&amp;#039;s.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Los Angeles&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;los_angeles&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:6,&amp;quot;range&amp;quot;:&amp;quot;7129-7535&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit7&quot; id=&quot;chicago&quot;&gt;Chicago&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
It&amp;#039;s funny, I&amp;#039;ve been to nice places in Chicago but the only one I can remember off hand is
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.portillos.com/index.html&quot; class=&quot;urlextern&quot; title=&quot;https://www.portillos.com/index.html&quot; rel=&quot;ugc nofollow&quot;&gt;Portillo&amp;#039;s&lt;/a&gt; - Chicago dogs - good&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Chicago&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;chicago&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:7,&amp;quot;range&amp;quot;:&amp;quot;7536-7726&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit8&quot; id=&quot;new_york_city&quot;&gt;New York City&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;https://www.russanddaughters.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.russanddaughters.com/&quot; rel=&quot;ugc nofollow&quot;&gt;Russ &amp;amp; Daughters&lt;/a&gt; - Mmmmm fish&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/food?do=showtag&amp;amp;tag=food&quot; class=&quot;wikilink1&quot; title=&quot;tag:food&quot; rel=&quot;tag&quot;&gt;food&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;New York City&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;new_york_city&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:8,&amp;quot;range&amp;quot;:&amp;quot;7727-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/sleep">
        <dc:format>text/html</dc:format>
        <dc:date>2025-10-10T20:07:28+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>sleep</title>
        <link>https://wiki.qlyoung.net/sleep</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;sleep&quot;&gt;sleep&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
It&amp;#039;s funny to get older and finally learn lessons that have been drilled into me since I was old enough to listen; but that&amp;#039;s life.
&lt;/p&gt;

&lt;p&gt;
Sleep is very, very important. It determines baseline more than any other factor. Good sleep creates the possibility for a good day. Poor sleep almost guarantees a bad or at least difficult day.
&lt;/p&gt;

&lt;p&gt;
Bryan Johnson, the nighttime erections guy, &lt;a href=&quot;https://blueprint.bryanjohnson.com/blogs/news/how-i-fixed-my-terrible-sleep&quot; class=&quot;urlextern&quot; title=&quot;https://blueprint.bryanjohnson.com/blogs/news/how-i-fixed-my-terrible-sleep&quot; rel=&quot;ugc nofollow&quot;&gt;says&lt;/a&gt;:
&lt;/p&gt;
&lt;blockquote class=&quot;blockquote-plugin&quot;&gt;
&lt;p&gt;
Make sleep your #1 priority. Nothing influences your conscious existence more.
&lt;/p&gt;

&lt;/blockquote&gt;
&lt;p&gt;
He&amp;#039;s right. I don&amp;#039;t agree with him on everything though.
&lt;/p&gt;

&lt;p&gt;
For a long time I kept my room cold for sleeping - around 67-69 F - and that helped my sleep. Any warmer and I&amp;#039;d wake up hot. At some point I switched from a duvet filled with synthetic insulation to a weighted duvet filled with glass beads. I also moved to a newer place with more efficient cooling, keeping the temperature closer to its actual setting - so, cooler than my previous place.
&lt;/p&gt;

&lt;p&gt;
After a while of not sleeping very well, I tried bumping the temperature up a few degrees. Almost instantly my sleep quality improved:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Sleeping HRV improved overnight. On the best nights I was reading ~40ms. After increasing the temp, I am regularly reading over 60ms.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Sleep quality as measured by my sleep tracker improved by around 20%&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
I am not sure how long this has been affecting my sleep but at minimum it was 6 months.
&lt;/p&gt;

&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/start">
        <dc:format>text/html</dc:format>
        <dc:date>2026-02-12T16:53:16+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>start</title>
        <link>https://wiki.qlyoung.net/start</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;home_page&quot;&gt;home page&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
this my wiki, here you will find many things i wish to write about.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;home page&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;home_page&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-94&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit2&quot; id=&quot;pages&quot;&gt;pages&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;div class=&quot;pagequery  tableless&quot; id=&quot;top-1715690641&quot; style=&quot;&quot;&gt;

&lt;ul style=&quot;&quot;&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/just&quot; class=&quot;wikilink1&quot; title=&quot;just&quot; data-wiki-id=&quot;just&quot;&gt;just&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/bird_bar&quot; class=&quot;wikilink1&quot; title=&quot;bird_bar&quot; data-wiki-id=&quot;bird_bar&quot;&gt;bird bar&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/books&quot; class=&quot;wikilink1&quot; title=&quot;books&quot; data-wiki-id=&quot;books&quot;&gt;books&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/start&quot; class=&quot;wikilink1&quot; title=&quot;start&quot; data-wiki-id=&quot;start&quot;&gt;home page&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/power_juice&quot; class=&quot;wikilink1&quot; title=&quot;power_juice&quot; data-wiki-id=&quot;power_juice&quot;&gt;power_juice&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/quotes&quot; class=&quot;wikilink1&quot; title=&quot;quotes&quot; data-wiki-id=&quot;quotes&quot;&gt;quotes&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/syncing&quot; class=&quot;wikilink1&quot; title=&quot;syncing&quot; data-wiki-id=&quot;syncing&quot;&gt;syncing&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tiered_storage&quot; class=&quot;wikilink1&quot; title=&quot;tiered_storage&quot; data-wiki-id=&quot;tiered_storage&quot;&gt;tiered storage&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/restaurants&quot; class=&quot;wikilink1&quot; title=&quot;restaurants&quot; data-wiki-id=&quot;restaurants&quot;&gt;restaurants&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/sleep&quot; class=&quot;wikilink1&quot; title=&quot;sleep&quot; data-wiki-id=&quot;sleep&quot;&gt;sleep&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/location_history&quot; class=&quot;wikilink1&quot; title=&quot;location_history&quot; data-wiki-id=&quot;location_history&quot;&gt;location history&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/degoogling&quot; class=&quot;wikilink1&quot; title=&quot;degoogling&quot; data-wiki-id=&quot;degoogling&quot;&gt;degoogling&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/file_transfer&quot; class=&quot;wikilink1&quot; title=&quot;file_transfer&quot; data-wiki-id=&quot;file_transfer&quot;&gt;file transfer&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/insurance&quot; class=&quot;wikilink1&quot; title=&quot;insurance&quot; data-wiki-id=&quot;insurance&quot;&gt;insurance&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/password_management&quot; class=&quot;wikilink1&quot; title=&quot;password_management&quot; data-wiki-id=&quot;password_management&quot;&gt;password management&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/autoarchaeology&quot; class=&quot;wikilink1&quot; title=&quot;autoarchaeology&quot; data-wiki-id=&quot;autoarchaeology&quot;&gt;autoarchaeology&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/underwater_videography_with_gopro&quot; class=&quot;wikilink1&quot; title=&quot;underwater_videography_with_gopro&quot; data-wiki-id=&quot;underwater_videography_with_gopro&quot;&gt;underwater_videography_with_gopro&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/note_taking_programs&quot; class=&quot;wikilink1&quot; title=&quot;note_taking_programs&quot; data-wiki-id=&quot;note_taking_programs&quot;&gt;note taking programs&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/peak_sets&quot; class=&quot;wikilink1&quot; title=&quot;peak_sets&quot; data-wiki-id=&quot;peak_sets&quot;&gt;peak sets&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/peak_tributes&quot; class=&quot;wikilink1&quot; title=&quot;peak_tributes&quot; data-wiki-id=&quot;peak_tributes&quot;&gt;peak tributes&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/task_tracking&quot; class=&quot;wikilink1&quot; title=&quot;task_tracking&quot; data-wiki-id=&quot;task_tracking&quot;&gt;task tracking&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/cocktail_bars&quot; class=&quot;wikilink1&quot; title=&quot;cocktail_bars&quot; data-wiki-id=&quot;cocktail_bars&quot;&gt;cocktail bars&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/whole_foods_pre_seasoned_protein_tier_list&quot; class=&quot;wikilink1&quot; title=&quot;whole_foods_pre_seasoned_protein_tier_list&quot; data-wiki-id=&quot;whole_foods_pre_seasoned_protein_tier_list&quot;&gt;whole foods pre seasoned protein tier list&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/my_web&quot; class=&quot;wikilink1&quot; title=&quot;my_web&quot; data-wiki-id=&quot;my_web&quot;&gt;my web&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/vehicle_leases&quot; class=&quot;wikilink1&quot; title=&quot;vehicle_leases&quot; data-wiki-id=&quot;vehicle_leases&quot;&gt;vehicle leases&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/documentation&quot; class=&quot;wikilink1&quot; title=&quot;documentation&quot; data-wiki-id=&quot;documentation&quot;&gt;documentation&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/music_management&quot; class=&quot;wikilink1&quot; title=&quot;music_management&quot; data-wiki-id=&quot;music_management&quot;&gt;music management&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/iphone&quot; class=&quot;wikilink1&quot; title=&quot;iphone&quot; data-wiki-id=&quot;iphone&quot;&gt;iphone&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/oxygen_rebreather&quot; class=&quot;wikilink1&quot; title=&quot;oxygen_rebreather&quot; data-wiki-id=&quot;oxygen_rebreather&quot;&gt;oxygen rebreather&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/digital_mapping&quot; class=&quot;wikilink1&quot; title=&quot;digital_mapping&quot; data-wiki-id=&quot;digital_mapping&quot;&gt;digital mapping&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/gambling&quot; class=&quot;wikilink1&quot; title=&quot;gambling&quot; data-wiki-id=&quot;gambling&quot;&gt;gambling&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/personal_infrastructure&quot; class=&quot;wikilink1&quot; title=&quot;personal_infrastructure&quot; data-wiki-id=&quot;personal_infrastructure&quot;&gt;personal infrastructure&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/too_many_projects_too_little_time&quot; class=&quot;wikilink1&quot; title=&quot;too_many_projects_too_little_time&quot; data-wiki-id=&quot;too_many_projects_too_little_time&quot;&gt;too many projects too little time&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/dive_light_burn_times&quot; class=&quot;wikilink1&quot; title=&quot;dive_light_burn_times&quot; data-wiki-id=&quot;dive_light_burn_times&quot;&gt;dive light burn times&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/fall&quot; class=&quot;wikilink1&quot; title=&quot;fall&quot; data-wiki-id=&quot;fall&quot;&gt;fall&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/possession_minimalism&quot; class=&quot;wikilink1&quot; title=&quot;possession_minimalism&quot; data-wiki-id=&quot;possession_minimalism&quot;&gt;possession minimalism&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/photo_management&quot; class=&quot;wikilink1&quot; title=&quot;photo_management&quot; data-wiki-id=&quot;photo_management&quot;&gt;photo management&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/av1&quot; class=&quot;wikilink1&quot; title=&quot;av1&quot; data-wiki-id=&quot;av1&quot;&gt;av1&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/blog&quot; class=&quot;wikilink1&quot; title=&quot;blog&quot; data-wiki-id=&quot;blog&quot;&gt;why wiki?&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/network_simulation_with_k8s-topo_on_raspi_cluster&quot; class=&quot;wikilink1&quot; title=&quot;network_simulation_with_k8s-topo_on_raspi_cluster&quot; data-wiki-id=&quot;network_simulation_with_k8s-topo_on_raspi_cluster&quot;&gt;network simulation with k8s-topo on raspberry pi 3b+ cluster&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/consistency&quot; class=&quot;wikilink1&quot; title=&quot;consistency&quot; data-wiki-id=&quot;consistency&quot;&gt;consistency&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/car_scuba_rack&quot; class=&quot;wikilink1&quot; title=&quot;car_scuba_rack&quot; data-wiki-id=&quot;car_scuba_rack&quot;&gt;car scuba rack&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/disable_bracketed_paste_in_gnu_readline&quot; class=&quot;wikilink1&quot; title=&quot;disable_bracketed_paste_in_gnu_readline&quot; data-wiki-id=&quot;disable_bracketed_paste_in_gnu_readline&quot;&gt;disable bracketed paste in gnu readline&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/adaptive_cruise&quot; class=&quot;wikilink1&quot; title=&quot;adaptive_cruise&quot; data-wiki-id=&quot;adaptive_cruise&quot;&gt;adaptive cruise&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/diy_oxygen_analyzer&quot; class=&quot;wikilink1&quot; title=&quot;diy_oxygen_analyzer&quot; data-wiki-id=&quot;diy_oxygen_analyzer&quot;&gt;diy oxygen analyzer&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/wiki/welcome&quot; class=&quot;wikilink1&quot; title=&quot;wiki:welcome&quot; data-wiki-id=&quot;wiki:welcome&quot;&gt;Welcome to your new DokuWiki&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/wiki/syntax&quot; class=&quot;wikilink1&quot; title=&quot;wiki:syntax&quot; data-wiki-id=&quot;wiki:syntax&quot;&gt;Formatting Syntax&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/wiki/dokuwiki&quot; class=&quot;wikilink1&quot; title=&quot;wiki:dokuwiki&quot; data-wiki-id=&quot;wiki:dokuwiki&quot;&gt;DokuWiki&lt;/a&gt;
&lt;/li&gt;&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/playground/playground&quot; class=&quot;wikilink1&quot; title=&quot;playground:playground&quot; data-wiki-id=&quot;playground:playground&quot;&gt;PlayGround&lt;/a&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;a class=&quot;top&quot; href=&quot;#top-1715690641&quot;&gt;Top ↑&lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;pages&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;pages&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;95-152&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit3&quot; id=&quot;tags&quot;&gt;tags&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;table class=&quot;inline&quot;&gt;&lt;tr&gt;&lt;th class=&quot;page&quot;&gt;Tag&lt;/th&gt;&lt;th class=&quot;page&quot;&gt;Quantity&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/from_blog?do=showtag&amp;amp;tag=from_blog&quot; class=&quot;wikilink1&quot; title=&quot;tag:from_blog&quot; rel=&quot;tag&quot;&gt;from blog&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;10&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/diving?do=showtag&amp;amp;tag=diving&quot; class=&quot;wikilink1&quot; title=&quot;tag:diving&quot; rel=&quot;tag&quot;&gt;diving&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;4&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/technology?do=showtag&amp;amp;tag=technology&quot; class=&quot;wikilink1&quot; title=&quot;tag:technology&quot; rel=&quot;tag&quot;&gt;technology&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;14&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/self-hosting?do=showtag&amp;amp;tag=self-hosting&quot; class=&quot;wikilink1&quot; title=&quot;tag:self-hosting&quot; rel=&quot;tag&quot;&gt;self-hosting&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;4&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/nature?do=showtag&amp;amp;tag=nature&quot; class=&quot;wikilink1&quot; title=&quot;tag:nature&quot; rel=&quot;tag&quot;&gt;nature&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/making?do=showtag&amp;amp;tag=making&quot; class=&quot;wikilink1&quot; title=&quot;tag:making&quot; rel=&quot;tag&quot;&gt;making&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/networking?do=showtag&amp;amp;tag=networking&quot; class=&quot;wikilink1&quot; title=&quot;tag:networking&quot; rel=&quot;tag&quot;&gt;networking&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/photography?do=showtag&amp;amp;tag=photography&quot; class=&quot;wikilink1&quot; title=&quot;tag:photography&quot; rel=&quot;tag&quot;&gt;photography&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/programming?do=showtag&amp;amp;tag=programming&quot; class=&quot;wikilink1&quot; title=&quot;tag:programming&quot; rel=&quot;tag&quot;&gt;programming&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/products?do=showtag&amp;amp;tag=products&quot; class=&quot;wikilink1&quot; title=&quot;tag:products&quot; rel=&quot;tag&quot;&gt;products&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/literature?do=showtag&amp;amp;tag=literature&quot; class=&quot;wikilink1&quot; title=&quot;tag:literature&quot; rel=&quot;tag&quot;&gt;literature&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/humanity?do=showtag&amp;amp;tag=humanity&quot; class=&quot;wikilink1&quot; title=&quot;tag:humanity&quot; rel=&quot;tag&quot;&gt;humanity&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;6&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/pithy?do=showtag&amp;amp;tag=pithy&quot; class=&quot;wikilink1&quot; title=&quot;tag:pithy&quot; rel=&quot;tag&quot;&gt;pithy&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/stupid?do=showtag&amp;amp;tag=stupid&quot; class=&quot;wikilink1&quot; title=&quot;tag:stupid&quot; rel=&quot;tag&quot;&gt;stupid&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/food?do=showtag&amp;amp;tag=food&quot; class=&quot;wikilink1&quot; title=&quot;tag:food&quot; rel=&quot;tag&quot;&gt;food&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/philosophy?do=showtag&amp;amp;tag=philosophy&quot; class=&quot;wikilink1&quot; title=&quot;tag:philosophy&quot; rel=&quot;tag&quot;&gt;philosophy&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/gambling?do=showtag&amp;amp;tag=gambling&quot; class=&quot;wikilink1&quot; title=&quot;tag:gambling&quot; rel=&quot;tag&quot;&gt;gambling&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/complaining?do=showtag&amp;amp;tag=complaining&quot; class=&quot;wikilink1&quot; title=&quot;tag:complaining&quot; rel=&quot;tag&quot;&gt;complaining&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/flexing?do=showtag&amp;amp;tag=flexing&quot; class=&quot;wikilink1&quot; title=&quot;tag:flexing&quot; rel=&quot;tag&quot;&gt;flexing&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/machine_learning?do=showtag&amp;amp;tag=machine_learning&quot; class=&quot;wikilink1&quot; title=&quot;tag:machine_learning&quot; rel=&quot;tag&quot;&gt;machine learning&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;inline&quot;&gt;&lt;a href=&quot;https://wiki.qlyoung.net/tag/geospatial?do=showtag&amp;amp;tag=geospatial&quot; class=&quot;wikilink1&quot; title=&quot;tag:geospatial&quot; rel=&quot;tag&quot;&gt;geospatial&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;inline&quot;&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;tags&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;tags&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;153-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/syncing">
        <dc:format>text/html</dc:format>
        <dc:date>2025-11-04T03:20:17+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>syncing</title>
        <link>https://wiki.qlyoung.net/syncing</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;syncing&quot;&gt;syncing&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
I work across multiple computers. My desktop is Arch, my main laptop is a Macbook, and I also have a Framework 13. Normally I use my desktop at home and take the mac for traveling. In all cases, I need access to my files.
&lt;/p&gt;

&lt;p&gt;
I think about my data in terms of &lt;a href=&quot;https://wiki.qlyoung.net/tiered_storage&quot; class=&quot;wikilink1&quot; title=&quot;tiered_storage&quot; data-wiki-id=&quot;tiered_storage&quot;&gt;tiers&lt;/a&gt;. I have stuff I frequently access and stuff I rarely access. My head canon for the first tier is “working set”; that&amp;#039;s my documents, music, and project files. The working set should be available and fast. Rarely accessed stuff can live on slow storage or offline.
&lt;/p&gt;

&lt;p&gt;
So for my working set I need a way to reliably sync all that data across multiple devices. For that I use &lt;a href=&quot;https://syncthing.net&quot; class=&quot;urlextern&quot; title=&quot;https://syncthing.net&quot; rel=&quot;ugc nofollow&quot;&gt;syncthing&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
&lt;div id=&quot;mermaidContainer0&quot; style=&quot;position: relative; width:auto; height:auto&quot;&gt;&lt;span class=&quot;mermaid&quot; id=mermaidContent0 style=&quot;width:auto; height:auto&quot;&gt;
graph TB
    subgraph &quot;Syncthing Network&quot;
        L[💻 Laptop]
        D[🖥️ Desktop]
        S[🖲️ Home Server]
        L &lt;--&gt;|sync| D
        L &lt;--&gt;|sync| S
        D &lt;--&gt;|sync| S
    end
    P[📱 Phone]
    P -.-&gt;|Internet Connection| S
    style L fill:#e1f5ff
    style D fill:#e1f5ff
    style S fill:#ffe1f5
    style P fill:#fff4e1
&lt;/span&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/task_tracking">
        <dc:format>text/html</dc:format>
        <dc:date>2025-06-18T14:15:49+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>task_tracking</title>
        <link>https://wiki.qlyoung.net/task_tracking</link>
        <description>
&lt;h1 class=&quot;sectionedit1&quot; id=&quot;task_tracking&quot;&gt;task tracking&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
When I was in my teens I used to keep all my tasks in my head. Occasionally if I had something with more details than I could remember I would write it down on a notepad. Around the second half of my 20s I started getting really into personal databases and task tracking. I&amp;#039;ll write another page on notetaking solutions, but this one is focused on task tracking.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;task tracking&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;task_tracking&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-393&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit2&quot; id=&quot;section2025&quot;&gt;2025&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
As of 2025, I now do all task tracking using sticky notes on my refrigerator and computer monitor.
&lt;/p&gt;

&lt;p&gt;
The fridge system is:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Left door - long term goals, thoughts. For example, places I want to travel this year, big ideas I want to pursue.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Right door - Short term tasks&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
These are organized in rows. For the right door, on the top row are smaller items that can be quickly knocked out or time sensitive items that need to happen soon. Items on lower rows are in roughly priority order. On the left door there&amp;#039;s no real structure.
&lt;/p&gt;

&lt;p&gt;
Things that ABSOLUTELY must happen tomorrow go on a sticky note on my computer monitor.
&lt;/p&gt;

&lt;p&gt;
The problem with every other system, and especially all computerized systems, is that I have to remember to LOOK at the system. When I know I have a lot of items in there, anxiety prevents me from opening the app at all. Sticky notes on the fridge force me to look, which empirically results in execution.
&lt;/p&gt;

&lt;p&gt;
Interesting corollary is that this only became viable when I moved this year into a place where the fridge is located in the main living area (big studio-style space). In my old place the fridge was in the kitchen, which I only ever went into for food and bev. So probably the main key is that the stickies are located somewhere you can&amp;#039;t miss them.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;2025&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;section2025&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;394-1691&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit3&quot; id=&quot;notion&quot;&gt;notion&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
In 2019 I started using Notion extensively as a general notetaking tool. Notion also has a kanban implementation which I started using for task tracking. I had a single big kanban with four columns:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Todo&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Doing&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Done&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Not doing&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
After a couple years, this kanban had hundreds and hundreds of items in it, but remained surprisingly usable. The nice thing about Notion&amp;#039;s kanbans was that you could easily link them to notes elsewhere in the workspace; kanban cards with brief task descriptions could have outlinks to notes pages with context, or to databases with data related to the task, and so on.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/notion-kanban-board-5.png?id=task_tracking&quot; class=&quot;media&quot; title=&quot;notion-kanban-board-5.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/notion-kanban-board-5.png?w=800&amp;amp;tok=53e685&quot; class=&quot;media&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;800&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;notion&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;notion&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;1692-2360&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit4&quot; id=&quot;obsidian_tasks&quot;&gt;obsidian tasks&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
In mid 2023 I moved off of Notion to Obsidian for &lt;a href=&quot;https://wiki.qlyoung.net/my_web&quot; class=&quot;wikilink1&quot; title=&quot;my_web&quot; data-wiki-id=&quot;my_web&quot;&gt;various reasons&lt;/a&gt;. In the process I lost my notes-integrated kanban as well. I needed a new task-tracking solution, and having done that job in my notes app before, I resolved to try to do it in Obsidian.
&lt;/p&gt;

&lt;p&gt;
When I switch to a new platform for anything, I like to try the Happy Path. In other words, I want to use the software according to what its users and developers consider to be its strengths. For task tracking in Obsidian, that is the &lt;a href=&quot;https://publish.obsidian.md/tasks/Introduction&quot; class=&quot;urlextern&quot; title=&quot;https://publish.obsidian.md/tasks/Introduction&quot; rel=&quot;ugc nofollow&quot;&gt;Tasks&lt;/a&gt; plugin. This is fundamentally a plugin that adds a query and metadata layer on top of markdown checklists.
&lt;/p&gt;

&lt;p&gt;
I tried hard to make this work for me but it really does not. The first major issue is that once I am done with a task, I want it hidden from view. But these tasks are Markdown checklists. You check them and they get strikethrough formatting, but they stay right where you defined them. I really don&amp;#039;t like that. I get dopamine from eliminating the task and making the list closer to empty. When you tick the task and it stays there that doesn&amp;#039;t give any dopamine.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/2024-01-09_02-28.png?id=task_tracking&quot; class=&quot;media&quot; title=&quot;2024-01-09_02-28.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/2024-01-09_02-28.png?w=400&amp;amp;tok=49190f&quot; class=&quot;medialeft&quot; align=&quot;left&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Another problem with Tasks is that metadata associated with tasks - such as priority, due date, categories, etc - is added in plain text on the same line as the task description. I don&amp;#039;t like this. It looks messy and unreadable, and on mobile it&amp;#039;s completely untenable. Also, the way that you distinguish tasks from regular checkbox items is by adding a #task tag. This annoys me because it&amp;#039;s like a noisy type annotation on a markdown checklist. it just doesn&amp;#039;t feel good.
&lt;/p&gt;

&lt;p&gt;
And speaking of mobile - the phone experience of Tasks is awful. On desktop I&amp;#039;m okay shuffling todos around with my keyboard using a mouse to click those small checkboxes. On mobile, it is really bad. The checklist box is hard to hit. There&amp;#039;s a lot of friction to adding new tasks - navigate to wherever the task list is in your vault, scroll, scroll, type - on a phone keyboard - the following:
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;- [ ] task description #task&lt;/pre&gt;

&lt;p&gt;
Doesn&amp;#039;t seem too bad, right? But look at the amount of keypresses you actually need to type that:
&lt;/p&gt;

&lt;p&gt;
&lt;video class=&quot;mediacenter&quot; width=&quot;800&quot; height=&quot;240&quot; controls=&quot;controls&quot;&gt;
&lt;source src=&quot;/_media/img_0701.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_media/img_0701.mp4&quot; class=&quot;media mediafile mf_mp4&quot; title=&quot;img_0701.mp4 (3.6 MB)&quot;&gt;img_0701.mp4&lt;/a&gt;&lt;/video&gt;

&lt;/p&gt;

&lt;p&gt;
It&amp;#039;s a lot. Typing - [ ] in particular sucks.
&lt;/p&gt;

&lt;p&gt;
This is really annoying on a phone and it made me dread adding tasks. If I dread adding tasks, I either won&amp;#039;t do it or I&amp;#039;ll spend too much time doing it. The mobile experience for any task tracker is critical to me. I need to be able to jot down a task the moment I think of it, and then put my phone away. It shouldn&amp;#039;t take more than ten seconds or so.
&lt;/p&gt;

&lt;p&gt;
I looked into recreating my kanban solution in Obsidian, but the two kanban plugins I tried - &lt;a href=&quot;https://github.com/mgmeyers/obsidian-kanban&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/mgmeyers/obsidian-kanban&quot; rel=&quot;ugc nofollow&quot;&gt;this one&lt;/a&gt; and &lt;a href=&quot;https://github.com/roovo/obsidian-card-board&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/roovo/obsidian-card-board&quot; rel=&quot;ugc nofollow&quot;&gt;cardboard&lt;/a&gt;. Cardboard supposedly integrates with tasks, but unfortunately it insists on either organizing tasks by tag or by due date. I want to organize them by status (todo, doing, done etc) and it can&amp;#039;t do that. The UX is also weird and again, it doesn&amp;#039;t work well on mobile at all.
&lt;/p&gt;

&lt;p&gt;
tl;dr bad mobile experience, vault littered with completed tasks, poor metadata support and high friction make every task solution I tried in Obsidian untenable. I began looking for something else.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;obsidian tasks&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;obsidian_tasks&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:4,&amp;quot;range&amp;quot;:&amp;quot;2361-5684&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit5&quot; id=&quot;do_nextcloud_tasks_caldav&quot;&gt;2do, nextcloud tasks &amp;amp; caldav&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
I vaguely knew that calDAV has support for task synchronization. I &lt;a href=&quot;https://wiki.qlyoung.net/my_web&quot; class=&quot;wikilink1&quot; title=&quot;my_web&quot; data-wiki-id=&quot;my_web&quot;&gt;already run&lt;/a&gt; Nextcloud as a calDAV server for calendaring. Nextcloud also has a &lt;a href=&quot;https://apps.nextcloud.com/apps/tasks&quot; class=&quot;urlextern&quot; title=&quot;https://apps.nextcloud.com/apps/tasks&quot; rel=&quot;ugc nofollow&quot;&gt;Tasks&lt;/a&gt; application that allows you to edit these tasks. Normally I find that Nextcloud applications kind of suck, but this one has 5 stars, and they are well deserved. I tried that one and was pleased to find that it supports categories, due dates, subtasks, and generally sports a pleasant user interface. Now to find a mobile solution.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/tasks-1.png?id=task_tracking&quot; class=&quot;media&quot; title=&quot;tasks-1.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/tasks-1.png?w=400&amp;amp;tok=c8d884&quot; class=&quot;mediaright&quot; align=&quot;right&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
After a little searching I found &lt;a href=&quot;https://www.2doapp.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.2doapp.com/&quot; rel=&quot;ugc nofollow&quot;&gt;2do&lt;/a&gt; (note that as of writing, the TLS certificate expired on January 8th). This thing looked a bit aged but very capable, so I decided to give it a try. Imagine my surprise when I saw it was $10 on the &lt;a href=&quot;https://apps.apple.com/us/app/2do-todo-list-tasks-notes/id303656546&quot; class=&quot;urlextern&quot; title=&quot;https://apps.apple.com/us/app/2do-todo-list-tasks-notes/id303656546&quot; rel=&quot;ugc nofollow&quot;&gt;app store&lt;/a&gt;. I think this is the most I have ever paid for a mobile app.
&lt;/p&gt;

&lt;p&gt;
But oh man, is it worth it. This app rocks. It is designed around making adding and managing tasks as frictionless as possible. It supports categorizing and tagging tasks, grouping related tasks together, and subtasks. It has a few different overviews for tasks that allow you to view tasks by category (e.g. “all diving-related tasks”), and by due date (“all tasks due today across all categories”). The concept of &amp;#039;smart lists&amp;#039; is also supported - set conditions on what tasks should appear in the list and they will be pulled into the list.
&lt;/p&gt;

&lt;p&gt;
&lt;video class=&quot;mediacenter&quot; width=&quot;320&quot; height=&quot;240&quot; controls=&quot;controls&quot;&gt;
&lt;source src=&quot;/_media/img_0705.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_media/img_0705.mp4&quot; class=&quot;media mediafile mf_mp4&quot; title=&quot;img_0705.mp4 (7.2 MB)&quot;&gt;img_0705.mp4&lt;/a&gt;&lt;/video&gt;

&lt;/p&gt;

&lt;p&gt;
And all of this syncs flawlessly with CalDAV. I can whip out my phone, type a task into the quick add bar, hit enter and put my phone away, and it&amp;#039;ll be there in the task viewer on Nextcloud. Furthermore, since tasks with due dates are also calendar items, they appear on my calendar so I get an overview of everything I&amp;#039;m going to do just by looking at my calendar.
&lt;/p&gt;

&lt;p&gt;
This all probably sounds very trivial, but I&amp;#039;ve never used a task tracking solution where all the pieces worked together so well. It&amp;#039;s really slick.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;2do, nextcloud tasks &amp;amp; caldav&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;do_nextcloud_tasks_caldav&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:1,&amp;quot;secid&amp;quot;:5,&amp;quot;range&amp;quot;:&amp;quot;5685-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/tiered_storage">
        <dc:format>text/html</dc:format>
        <dc:date>2025-10-22T15:33:48+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>tiered_storage</title>
        <link>https://wiki.qlyoung.net/tiered_storage</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;tiered_storage&quot;&gt;tiered storage&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
There are two schools of thought regarding storage. One prioritizes resource efficiency and consistency; data should be stored in one location, and the location made accessible to all clients. This maximizes total available storage space at the expense of availability and access speed. The other stores data where it is needed; this may be in multiple places, thus trading storage for availability and access speed, and introducing the potential for inconsistency in multihomed data.
&lt;/p&gt;

&lt;p&gt;
For a long time I was firmly in the first school. Files live in one big beautiful cluster that you can access from any device. I see the appeal. You can focus all your efforts making that cluster as big and beautiful as you want, as resilient as you want. There&amp;#039;s only one copy of data, so you never have inconsistencies. But after lots of time using a big beautiful cluster, I kept having the experience that I wanted some file and I just couldn&amp;#039;t get to it. And when I could get to it the experience of browsing around, searching, transferring files all over a network just…sucks. After you buy into the paradigm, after a while you get used to it and forget that it doesn&amp;#039;t have to be like that.
&lt;/p&gt;

&lt;p&gt;
In the past my laptop didn&amp;#039;t have enough space to store all my data. Small form factor drives just weren&amp;#039;t big enough, and then they got big enough but they were so expensive that it only made sense to put them in a workstation (desktop). In 2025, annoyed by the downsides of network storage, I started thinking about how to improve the experience. I realized I have two tiers of data.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4 id=&quot;working_set&quot;&gt;working set&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
I have a set of files I frequently access; my music, photos, documents, and projects. This is what I think of as my “working set”. It doesn&amp;#039;t include stuff that falls in the bucket of “huge binary files” - movies, 300gb DNxHD Resolve projects, that sort of thing. Right now my working set is ~4tb.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4 id=&quot;other_stuff&quot;&gt;other stuff&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Some things are just too big to store on-device. At today&amp;#039;s storage unit prices, storing 30tb of movies that you access once a year on your laptop doesn&amp;#039;t make sense yet. Storing those things on a really big cluster accessed via the network is fine.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4 id=&quot;thoughts&quot;&gt;thoughts&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
After thinking about these tiers I realized:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; My working set is ~4tb&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; m.2 NVMe drives can now accommodate that working set for a reasonable price&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; which means I can now have my working set completely stored on my laptop&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
My desktop already had 10tb of storage and my home server 12tb. Due for a laptop upgrade anyway, I got 8tb of storage on my new machine and then set about collating my working set into an organized directory tree to prepare it for &lt;a href=&quot;https://wiki.qlyoung.net/syncing&quot; class=&quot;wikilink1&quot; title=&quot;syncing&quot; data-wiki-id=&quot;syncing&quot;&gt;syncing&lt;/a&gt;. I then engaged in a massive synchronization campaign that took about a week to settle out. With careful preparation there were almost no sync conflicts and my working set is now replicated on all devices.
&lt;/p&gt;

&lt;p&gt;
At this point in time I exist in storage nirvana. I work on my desktop. Grab my laptop and catch an airplane; all the data I was working on is on my laptop and I pick up where I left off. No need to connect to airplane wifi. Get where I&amp;#039;m going, connect my laptop; it&amp;#039;s syncing everything to my home server and desktop in the background.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4 id=&quot;special_casephone&quot;&gt;special case: phone&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
I only have 1tb of storage on my phone, so it can&amp;#039;t store everything; and even if I wanted to, syncthing on iOS basically doesn&amp;#039;t work because of the lack of background services. This is completely fine though, because 1) I don&amp;#039;t really do much work on my phone and 2) my phone is always online. If I need access to something from my phone, I can download it from my home server. If my phone is completely offline and I *really* need access to my data, it&amp;#039;s on my laptop. And if my phone is offline and I don&amp;#039;t have my laptop, well, I will do something else.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;tiered storage&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;tiered_storage&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-3821&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit2&quot; id=&quot;further_thoughts&quot;&gt;Further thoughts&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
As it turns out, this is one of the few concepts in computing that I didn&amp;#039;t invent /s. From wikipedia, the free encylopedia:
&lt;/p&gt;
&lt;blockquote class=&quot;blockquote-plugin&quot;&gt;
&lt;p&gt;
HSM is a long-established concept, dating back to the beginnings of commercial data processing.
&lt;/p&gt;

&lt;/blockquote&gt;
&lt;p&gt;
&lt;a href=&quot;https://en.wikipedia.org/wiki/Hierarchical storage management&quot; class=&quot;interwiki iw_wp&quot; title=&quot;https://en.wikipedia.org/wiki/Hierarchical storage management&quot;&gt;Hierarchical storage management&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Further thoughts&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;further_thoughts&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;3822-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/too_many_projects_too_little_time">
        <dc:format>text/html</dc:format>
        <dc:date>2024-07-02T21:56:23+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>too_many_projects_too_little_time</title>
        <link>https://wiki.qlyoung.net/too_many_projects_too_little_time</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;too_many_projects_too_little_time&quot;&gt;too many projects too little time&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
There&amp;#039;s so many projects, interests and activities I want to pursue, but their number and individual complexity has reached the point where I can&amp;#039;t pursue all of them simply because I don&amp;#039;t have enough time in each day to do so. Prioritizing is difficult because I am interested in all of them.
&lt;/p&gt;

&lt;p&gt;
Lately this has lead to each day consisting of rapid switches between various things I want to pursue, making little progress on any of them. Work taking up the bulk of my time pushes project pursuits into the gutter of the day, where they then compete for my time with errands, finance management, interpersonal relationships, and personal hygiene. A younger me prioritized projects over all those things. This is evident in my Github history since most of my projects from that time were software projects. I remember that period as one of the most productive times of my life.
&lt;/p&gt;

&lt;p&gt;
Additionally I have been traveling extensively over the past year, and sinking time into projects requires staying in one place. For me anyway. I need a stable environment to create the right conditions for doing deep work. Occasionally I can find a few hours to sink into flow while away, but that is limited to computerized pursuits, such opportunities are rare, and actually leveraging them into a flow state is even rarer. The upside to traveling is that because I know I can&amp;#039;t do anything creative, that creates the perfect conditions to do reading. Normally I prioritize creating over consuming, and so I end up never reading books since my limited free time is spent on creative pursuits.
&lt;/p&gt;

&lt;p&gt;
Now I am a much more balanced and well rounded person than I was when I spent the majority of my time on computer based projects, but my creative pursuits have suffered. You can&amp;#039;t read a book and write one at the same time. Going to the gym regularly cuts out time for projects. Relationships cut out time for projects. Traveling cuts out time for projects. I love recreating but it doesn&amp;#039;t produce anything, and as a person I derive a lot of my self worth from producing things, especially things other people use.
&lt;/p&gt;

&lt;p&gt;
The obvious solution is to get so rich I don&amp;#039;t have to work anymore, and then I can spend the bulk of each day working on things I want to pursue. But this is easier said than done.
&lt;/p&gt;

&lt;p&gt;
I considered listing out the projects here in some kind of perverse flex, but that is best reserved for my personal notes.
&lt;/p&gt;
&lt;div class=&quot;tags&quot;&gt;&lt;span&gt;
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/complaining?do=showtag&amp;amp;tag=complaining&quot; class=&quot;wikilink1&quot; title=&quot;tag:complaining&quot; rel=&quot;tag&quot;&gt;complaining&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/flexing?do=showtag&amp;amp;tag=flexing&quot; class=&quot;wikilink1&quot; title=&quot;tag:flexing&quot; rel=&quot;tag&quot;&gt;flexing&lt;/a&gt;,
	&lt;a href=&quot;https://wiki.qlyoung.net/tag/humanity?do=showtag&amp;amp;tag=humanity&quot; class=&quot;wikilink1&quot; title=&quot;tag:humanity&quot; rel=&quot;tag&quot;&gt;humanity&lt;/a&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;/div&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/underwater_videography_with_gopro">
        <dc:format>text/html</dc:format>
        <dc:date>2025-09-29T19:09:53+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>underwater_videography_with_gopro</title>
        <link>https://wiki.qlyoung.net/underwater_videography_with_gopro</link>
        <description>
&lt;p&gt;
&amp;lt;!DOCTYPE markdown&amp;gt;
# underwater videography with gopro
&lt;/p&gt;

&lt;p&gt;
What I do to get decent results out of a GoPro when shooting underwater.
&lt;/p&gt;

&lt;p&gt;
# Light
&lt;/p&gt;

&lt;p&gt;
## Attenuation
&lt;/p&gt;

&lt;p&gt;
Cameras are light gathering devices and perform better the more light is available, all else being equal. How much light is available in the water?
&lt;/p&gt;

&lt;p&gt;
Attenuation of light in a medium is described by the [Beer-Lambert law](&lt;a href=&quot;https://en.wikipedia.org/wiki/Beer%E2%80%93Lambert_law&quot; class=&quot;urlextern&quot; title=&quot;https://en.wikipedia.org/wiki/Beer%E2%80%93Lambert_law&quot; rel=&quot;ugc nofollow&quot;&gt;https://en.wikipedia.org/wiki/Beer%E2%80%93Lambert_law&lt;/a&gt;). In terms of water, light intensity decreases as a function of depth per
&lt;/p&gt;

&lt;p&gt;
I(z) = I₀ ​e⁻ᵏᶻ
&lt;/p&gt;

&lt;p&gt;
where *I₀​* is the initial light intensity (at the surface), *I(z)* is the intensity at depth, *z*, and *k* is the absorption coefficient that varies depending on the medium characteristics (for water, things like clarity and particle content).
&lt;/p&gt;

&lt;p&gt;
For air *k* is around 0.0001. For seawater *k* is more like 0.05. That gives us roughly the following:
&lt;/p&gt;
&lt;div class=&quot;table sectionedit1&quot;&gt;&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; Depth (m) &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Depth (ft) &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; Light Intensity (%) &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row1&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt;————&lt;/td&gt;&lt;td class=&quot;col1&quot;&gt;————&lt;/td&gt;&lt;td class=&quot;col2&quot;&gt;———————&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row2&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; 0          &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; 0          &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; 100%                &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row3&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; 5          &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; 15         &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; 78%                 &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row4&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; 10         &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; 35         &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; 61%                 &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row5&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; 15         &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; 50         &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; 47%                 &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row6&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; 20         &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; 65         &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; 37%                 &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row7&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; 25         &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; 80         &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; 29%                 &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row8&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; 30         &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; 100        &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; 22%                 &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row9&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; 35         &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; 115        &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; 17%                 &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row10&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; 40         &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; 130        &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; 14%                 &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row11&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; 45         &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; 150        &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; 11%                 &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row12&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; 50         &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; 165        &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; 8%                  &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;table&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;table&amp;quot;,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;889-1537&amp;quot;} --&gt;
&lt;p&gt;
It&amp;#039;s much more complicated than this - absorption is wavelength dependent, seawater is not uniform, there are scattering effects, etc - but this is a decent approximation for this discussion.
&lt;/p&gt;

&lt;p&gt;
The upshot is that the diving environment is low light as far as cameras are concerned. This is why underwater photographers have kit that looks like this:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/pasted_image_20250501102135.png?id=underwater_videography_with_gopro&quot; class=&quot;media&quot; title=&quot;pasted_image_20250501102135.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/pasted_image_20250501102135.png?w=600&amp;amp;tok=7b9b11&quot; class=&quot;media&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;600&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Notice that the camera itself is the smallest part of the whole setup, most of the bulk is dedicated to lighting to add back in light absorbed by the water.
&lt;/p&gt;

&lt;p&gt;
## Sensor size
&lt;/p&gt;

&lt;p&gt;
Generally speaking the larger the sensor, the more light it can collect and the better its dynamic range. Smaller sensors struggle more in low light.
&lt;/p&gt;

&lt;p&gt;
To put in perspective how challenging it can be to use an action cam underwater:
&lt;/p&gt;

&lt;p&gt;
A full frame camera sensor is roughly 36x24mm for an area of 864mm^2. The Hero 11 sensor is 6.74&amp;times;5.05mm for a sensor area of 34.04 mm^2. That is ~25x smaller than full frame.
&lt;/p&gt;

&lt;p&gt;
If you are wondering why 6.74&amp;times;5.05mm does not correspond to the 1/1.9“ figure published by GoPro, that is because that figure is in &lt;a href=&quot;https://wiki.qlyoung.net/lib/exe/fetch.php?tok=750094&amp;amp;media=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2Foptical%20format&quot; class=&quot;media mediafile mf_org_wiki_optical_format&quot; title=&quot;https://en.wikipedia.org/wiki/optical format&quot;&gt;optical format&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
## Color
&lt;/p&gt;

&lt;p&gt;
Water is blue because it preferentially absorbs wavelengths of light other than blue. The deeper you are the further light has to travel through water, the more red light is absorbed and the bluer the scene.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/absorption_coefficient_of_water.png?id=underwater_videography_with_gopro&quot; class=&quot;media&quot; title=&quot;absorption_coefficient_of_water.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/absorption_coefficient_of_water.png?w=600&amp;amp;tok=0361b5&quot; class=&quot;media&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;600&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
The only way to truly compensate for this is to add the missing red wavelengths back in with external light.
&lt;/p&gt;

&lt;p&gt;
In camera you can adjust the white balance of the sensor to make it more sensitive to red wavelengths. Every sensor pixel has red, green and blue subpixels. The values from these pixels are combined to produce a single pixel color. Adjusting white balance biases the color information collected by subpixels to include more of the red channel. More on this in the Settings section.
&lt;/p&gt;

&lt;p&gt;
### Filters
&lt;/p&gt;

&lt;p&gt;
Red filters over a lens are stupid. Putting red plastic over a lens does not magically restore red wavelengths back into the scene. The filter is red because it absorbs blue light. As previously stated cameras perform better with more light, and most of the light we have at depth is blue. All you are doing with a red filter is deleting the precious light you do have.
&lt;/p&gt;

&lt;p&gt;
Even if filters were good, since the scene spectrum varies by depth and filters are calibrated to one spectrum your filter will almost always be suboptimal.
&lt;/p&gt;

&lt;p&gt;
You can tell when someone used a red filter because their video/picture looks like shit. Don&amp;#039;t use filters.
&lt;/p&gt;

&lt;p&gt;
## Framerate
&lt;/p&gt;

&lt;p&gt;
Slower framerate allows for longer exposure times. Longer exposure allows more light and better picture, all else being equal.
&lt;/p&gt;

&lt;p&gt;
However, not all else is equal. There is a tradeoff here. Slower shutters allow the camera to move further during each exposure. This produces motion blur. There is nothing inherently wrong with motion blur, it can even be desirable. However, if you digitally stabilize your footage, the camera motion will be cancelled but the motion blur produced by that movement will not be cancelled. The result is motion blur without apparent motion, producing a “vibrating” effect in the image. Read more here: &lt;a href=&quot;https://docs.gyroflow.xyz/app/getting-started/common-filming-tips-and-issues&quot; class=&quot;urlextern&quot; title=&quot;https://docs.gyroflow.xyz/app/getting-started/common-filming-tips-and-issues&quot; rel=&quot;ugc nofollow&quot;&gt;https://docs.gyroflow.xyz/app/getting-started/common-filming-tips-and-issues&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Diving is a slow sport, and the camera is usually not moving very fast, so there is limited motion blur. In such cases I prefer to shoot with a slower shutter speed. Personally I find it looks better. Fast, active subjects such as bait balls might benefit from 60fps with added motion blur in post. As a rule of thumb, start at 30 unless you plan to shoot something fast or want the action cam look, then consider bumping to 60 and potentially increasing ISO max to allow the camera a little more flexibility.
&lt;/p&gt;

&lt;p&gt;
## ISO
&lt;/p&gt;

&lt;p&gt;
In digital cameras ISO adjusts the electrical sensitivity of the sensor elements to incoming light. The tradeoff is that increased sensitivity results in lower SNR - i.e. higher ISO produces more noise. Fortunately denoising is very good these days, so if you are willing to denoise in post, you can get away with relatively high ISO.
&lt;/p&gt;

&lt;p&gt;
I usually limit to 1600.
&lt;/p&gt;

&lt;p&gt;
# Settings
&lt;/p&gt;

&lt;p&gt;
GoPro publishes a firmware called [Labs](&lt;a href=&quot;https://gopro.github.io/labs/&quot; class=&quot;urlextern&quot; title=&quot;https://gopro.github.io/labs/&quot; rel=&quot;ugc nofollow&quot;&gt;https://gopro.github.io/labs/&lt;/a&gt;) that unlocks a lot of power user software toggles, many of which are useful for underwater videography. Whoever is responsible for Labs does an amazing job.
&lt;/p&gt;

&lt;p&gt;
### A little diversion about GoPro color
&lt;/p&gt;

&lt;p&gt;
After first installing the Labs firmware, I spent a lot of time learning about color spaces, video grading, dynamic range etc. and subsequently shot a lot of underwater video with a custom logarithmic curve (LOGB=400) + Flat color profile for maximum dynamic range. I bought the [Leeming LUTs](&lt;a href=&quot;https://www.leeminglutpro.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.leeminglutpro.com/&quot; rel=&quot;ugc nofollow&quot;&gt;https://www.leeminglutpro.com/&lt;/a&gt;) for these settings and applied them in Resolve. I was determined to squeeze every last drop of performance out of this tiny camera.
&lt;/p&gt;

&lt;p&gt;
However, at some point I realized that the time required to grade the video I shot using these advanced settings meant that I never used that footage. The small selection of footage I did grade took an enormous amount of time and ultimately was not subjectively more pleasing to me than just using the Natural color settings. I simply do not have time to grade video properly.
&lt;/p&gt;

&lt;p&gt;
I don&amp;#039;t consider it to be a waste of time nor do I regret pursuing this. It was actually quite fun and informative, and I learned useful knowledge about color spaces, color correction, and so on. But I wouldn&amp;#039;t recommend that you go this route unless you plan to make video editing a Primary Hobby.
&lt;/p&gt;

&lt;p&gt;
## Basic settings
&lt;/p&gt;

&lt;p&gt;
- Natural color
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; `Flat` or a custom log curve in theory grant you more flexibility for color correction in post. However, after editing underwater LOG footage I realized that I do not care and that natural looks completely fine to my eye, so I don&amp;#039;t use it anymore.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
- White balance auto
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt;  Spectrum is constantly varying with depth and other things, on the balance I&amp;#039;ve found I get the best result allowing the camera to handle it&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
- Wide lens
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; “Wide” is the native lens. “Narrow” crops it, “Hyperview” and “Superview” convert 8:7 and 4:3 video respectively to 16:9 using an in-camera distortion mapping. I prefer an output with minimal in-camera corrections so I use “Wide”.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Notably, if you are using Gyroflow and want 16:9 you should shoot in 8:7 and select 16:9 as an export option in Gyroflow instead of in the camera; that way Gyroflow benefits from the additional buffer space to perform corrections and then crops at the end. Gyroflow can stabilize Hyper/SuperView footage, but in order to do so it has to undo the Hyperview distortion first using a reverse-engineered distortion matrix and then re-apply it, so you may as well shoot without it to begin with&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/hyperview-etc.jpg?id=underwater_videography_with_gopro&quot; class=&quot;media&quot; title=&quot;hyperview-etc.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/hyperview-etc.jpg&quot; class=&quot;media&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
- 5.3k30 8:7
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; I personally find this to be the sweet spot. 5.3k 8:7 is the native sensor resolution, every single pixel. By keeping the entire frame, post processed stabilization has much more “buffer” space to crop into. In this resolution the maximum framerate is 30 - but as mentioned I almost always shoot 30fps in water, so this is fine.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
- 4k60 8:7
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; If I want to shoot 60fps for some reason then I drop the resolution to 4k in order to keep the 8:7 aspect ratio. If you want the absolute highest resolution, you can drop to 16:9 which unlocks 5.3k60. Personally I prefer working in a consistent aspect ratio and think 4k subsampling looks good enough especially at high framerates.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
- Hypersmooth off
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Water or not, I prefer to stabilize in post with [Gyroflow](&lt;a href=&quot;https://gyroflow.xyz/&quot; class=&quot;urlextern&quot; title=&quot;https://gyroflow.xyz/&quot; rel=&quot;ugc nofollow&quot;&gt;https://gyroflow.xyz/&lt;/a&gt;). The result is better and it allows more control over the stabilization parameters&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Hypersmooth is calibrated for the refractive index of air and is only 70% effective in water; Gyroflow accounts for water if configured correctly&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; See the section on stabilization for more detail&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
- Bitrate high
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Higher bitrate generally equals better quality video&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
- 10-bit on
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; More &lt;a href=&quot;https://wiki.qlyoung.net/lib/exe/fetch.php?tok=14bcdb&amp;amp;media=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FColor%20depth&quot; class=&quot;media mediafile mf_org_wiki_color_depth&quot; title=&quot;https://en.wikipedia.org/wiki/Color depth&quot;&gt;bit depth&lt;/a&gt; allows capturing finer grained color differences, which looks significantly better&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Greater bit depth also reduces &lt;a href=&quot;https://wiki.qlyoung.net/lib/exe/fetch.php?tok=ea606b&amp;amp;media=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FColor%20banding&quot; class=&quot;media mediafile mf_org_wiki_color_banding&quot; title=&quot;https://en.wikipedia.org/wiki/Color banding&quot;&gt;Color banding&lt;/a&gt; which is often a problem in underwater footage, which tends to have diffuse color gradients as the backdrop&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
- Shutter auto
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; The camera knows best&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
- ISO min 100
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; I don&amp;#039;t understand why you would change this.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
- ISO max 1600
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; This is a tough one to choose and varies based on the scene. 1600 seems to work well for most environments. I notice that in 30fps the camera will usually keep it around 800. In a cave or wreck with a torch beam it behaves similarly.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
- Sharpness low
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; I sharpen in post as desired&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
- EV Comp 0
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; I don&amp;#039;t know&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
 enough to change this
&lt;/p&gt;

&lt;p&gt;
## Labs
&lt;/p&gt;

&lt;p&gt;
I like to set these with [QRControl](&lt;a href=&quot;https://apps.apple.com/us/app/qrcontrol/id1518134202&quot; class=&quot;urlextern&quot; title=&quot;https://apps.apple.com/us/app/qrcontrol/id1518134202&quot; rel=&quot;ugc nofollow&quot;&gt;https://apps.apple.com/us/app/qrcontrol/id1518134202&lt;/a&gt;).
&lt;/p&gt;

&lt;p&gt;
 - `NR01=1`
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; GoPro&amp;#039;s built in NR is too aggressive and softens the image too much. The underwater environment is inherently noisy, especially in the ocean where large portions of the frame are a very uniform blue that makes noise very visible&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Noise reduction in post has much better results. See denoising for details.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
- `BITR=120`
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; If you have a fast sdcard you can increase the maximum bitrate with this setting. However, it does cause some instability and lost footage on my specific camera so I think this is probably a wash.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
On Hero 12/13, there is `DIVE=1` which enables the water distortion corrections I mention in Stabilization for Hypersmooth. As mentioned I don&amp;#039;t use Hypersmooth, but if you do, you should probably enable this.
&lt;/p&gt;

&lt;p&gt;
On 13 there is also `WBDV=1`, described as follows:
&lt;/p&gt;
&lt;blockquote&gt;&lt;div class=&quot;no&quot;&gt;
 White Balance DiVe improvements. Rather than WARM for improving diving white balance, which effects WB the same at all depths, WBDV is more automatic – as the scene get more blue, the more the red channel is gain up.&lt;/div&gt;&lt;/blockquote&gt;

&lt;p&gt;
I have a Hero 11 and haven&amp;#039;t used either of these settings so I can&amp;#039;t speak to how well they work.
&lt;/p&gt;

&lt;p&gt;
# Praxis
&lt;/p&gt;

&lt;p&gt;
## Flow &amp;amp; Vibe
&lt;/p&gt;

&lt;p&gt;
I find that as soon as the camera is out, it takes me out of the dive and into the camera. I dive primarily for my own enjoyment and not to produce video, so taking video is usually a net negative for my overall dive experience. Operating the camera is task load that makes more important concerns such as spatial awareness, instrument scans, buddy tracking, navigation, gas management, light communication etc more difficult. That affects my whole dive team. Since I care about my buddies enjoying their dive with me, camera time is limited, and the “harder” the dive the less time I feel comfortable using a camera.
&lt;/p&gt;

&lt;p&gt;
Aside from task loading and vibe, most of what happens on a dive is just not very interesting to watch. It&amp;#039;s a lot of swimming. Sometimes swimming can be interesting if the environment is cool - wrecks and caves for example. But even then, on a swim dive the cave or wreck doesn&amp;#039;t change much over the course of 2 minutes, which is a usual attention span. You can edit it to just show the highlights of course, but you end up taking away from your dive in order to shoot a lot of video that no one will see because it ultimately just isn&amp;#039;t interesting to watch.
&lt;/p&gt;

&lt;p&gt;
The upshot is that I try to think ahead, be intentional about what I&amp;#039;m shooting and what result I hope to achieve, and constantly reevaluate whether the final video is going to be worth the precious dive minutes I&amp;#039;m losing to shoot it.
&lt;/p&gt;

&lt;p&gt;
## Case
&lt;/p&gt;

&lt;p&gt;
I use the official GoPro case. If you dive deeper than 50m you need to use something else. Golem Gear used to make the best ones, they were acquired by SubGravity in 2022 and their cases can be found here: &lt;a href=&quot;https://sub-gravity.com/product/gopro-hero-9-case/&quot; class=&quot;urlextern&quot; title=&quot;https://sub-gravity.com/product/gopro-hero-9-case/&quot; rel=&quot;ugc nofollow&quot;&gt;https://sub-gravity.com/product/gopro-hero-9-case/&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
I find it works best with a 3/4” or 1“ bolt snap attached like this:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/rot.jpg?id=underwater_videography_with_gopro&quot; class=&quot;media&quot; title=&quot;rot.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/rot.jpg?w=400&amp;amp;tok=28865f&quot; class=&quot;media&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
During shooting I hold the camera itself. I have tried the floaty GoPro handle. It gets in the way when clipped off, doesn&amp;#039;t really fit in a bellows pocket and it certainly doesn&amp;#039;t fit in the smaller glue-on pockets on my wetsuits. In my experience it doesn&amp;#039;t improve video stability compared to holding the camera body directly. Using a handle probably reduces rotational instability somewhat but with gyro-based stabilization in post this does not matter. A slightly more aggressive crop is acceptable to me in exchange for not having to manage the handle.
&lt;/p&gt;

&lt;p&gt;
## Predive
&lt;/p&gt;

&lt;p&gt;
Nothing sucks more than turning your GoPro on in 30m of water only to see that it is stuck in the wrong video mode. You cannot change this in the water (actually you can but more on that later).
&lt;/p&gt;

&lt;p&gt;
Before every dive:
&lt;/p&gt;

&lt;p&gt;
- Check and set the time/date
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; You can do this with QRControl or Quik&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
- Ensure the video and photo modes are in the preset you want
&lt;/p&gt;

&lt;p&gt;
## During dive
&lt;/p&gt;

&lt;p&gt;
### Settings
There is a way to change settings while in the water. GoPros with the labs firmware can read QR codes. If you want, you can print out QR codes that do specific things and tape them into your wetnotes like this:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/25-05-01_12-52-56_8319.jpg?id=underwater_videography_with_gopro&quot; class=&quot;media&quot; title=&quot;25-05-01_12-52-56_8319.jpg&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/25-05-01_12-52-56_8319.jpg?w=400&amp;amp;tok=3e38ee&quot; class=&quot;media&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Then during the dive you flip to the right QR code and point the camera at it to change the setting.
&lt;/p&gt;

&lt;p&gt;
You do carry wetnotes, right?
&lt;/p&gt;

&lt;p&gt;
### Telemetry
&lt;/p&gt;

&lt;p&gt;
You may want to cross reference points in the video with the dive profile your computer records. The easiest way to do this is to take a clear shot of your dive computer in the video. Get into the habit of pointing your camera at your computer for a few seconds at the start of filming to establish a reference point. I highly recommend doing this even if it does not seem useful right now.
&lt;/p&gt;

&lt;p&gt;
# Post
&lt;/p&gt;

&lt;p&gt;
Any computational operation you do on the camera itself is limited by the embedded CPU used on the camera, its thermal and battery constraints, and time. In post these constraints are far more favorable so you can potentially get much better results.
&lt;/p&gt;

&lt;p&gt;
Video specific stuff is biased towards Resolve since that&amp;#039;s what I use.
&lt;/p&gt;

&lt;p&gt;
## Stabilization
&lt;/p&gt;

&lt;p&gt;
Image stabilization is used to turn shaky handheld camera footage into nice smooth footage. Optical image stabilization, typically found in full size cameras, does this with motors that physically move the lens to cancel out shake and vibration.
&lt;/p&gt;

&lt;p&gt;
Electronic image stabilization, which is used in small cameras with fixed lenses and in post processing workflows, stabilizes footage by calculating the camera motion and then applying transformations and warping to the frame to cancel out that motion. In the case of the GoPro the source of camera motion data is the gyroscope. Hypersmooth reads the gyroscope directly and postprocessing tools use the gyro data that is encoded alongside image data in the MP4 files that the GoPro records.
&lt;/p&gt;

&lt;p&gt;
This result is usually cropped to eliminate the crazy warping borders. In EIS you sacrifice part of the frame for stability. The tradeoff is usually worth it.
&lt;/p&gt;

&lt;p&gt;
### Distortion &amp;amp; Refractive Index
&lt;/p&gt;

&lt;p&gt;
In order to know what transformations to apply, the EIS algorithm needs to know how a change in camera position is reflected in the image. This requires EIS to know the optical properties of the lens, which is why post-processed stabilization works best when calibrated for the specific camera model / lens being used.
&lt;/p&gt;

&lt;p&gt;
One of the major optical properties of a lens is its relative refractive index. Light travels at different speeds in different media. When light traveling in one medium enters a different medium, it bends (refracts) and the degree of this bend is a function of the difference in the speed of light between the first and second medium. If you know the relative refractive index between the ambient media and the lens material (glass), when characterizing the overall scene distortion you can account for the distortion introduced by refraction.
&lt;/p&gt;

&lt;p&gt;
Virtually all EIS systems assume air is the ambient media by default. This means that when using EIS on footage shot in water, the lens distortion matrix is incorrect. It needs to be adjusted to account for the difference in relative refractive index between water and the camera lens. Gyroflow [added](&lt;a href=&quot;https://github.com/gyroflow/gyroflow/issues/398&quot; class=&quot;urlextern&quot; title=&quot;https://github.com/gyroflow/gyroflow/issues/398&quot; rel=&quot;ugc nofollow&quot;&gt;https://github.com/gyroflow/gyroflow/issues/398&lt;/a&gt;) this option in 1.6.0 and there is a Labs setting (`DIVE=1`) to enable it for Hypersmooth on Hero 12/13.
&lt;/p&gt;

&lt;p&gt;
Without this adjustment stabilization will still work, just not as well. GoPro Labs docs say that without it, Hypersmooth is about 70% effective compared to shooting in air.
&lt;/p&gt;

&lt;p&gt;
To get an idea for the difference, compare the results of distortion correction calibrated for air (left) versus water (right):
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/air.png?id=underwater_videography_with_gopro&quot; class=&quot;media&quot; title=&quot;air.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/air.png?w=400&amp;amp;tok=4e7bc9&quot; class=&quot;medialeft&quot; align=&quot;left&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;400&quot; /&gt;&lt;/a&gt; &lt;a href=&quot;https://wiki.qlyoung.net/_detail/water.png?id=underwater_videography_with_gopro&quot; class=&quot;media&quot; title=&quot;water.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/water.png?w=400&amp;amp;tok=d83bbc&quot; class=&quot;medialeft&quot; align=&quot;left&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
### GoPro specifics
&lt;/p&gt;

&lt;p&gt;
GoPro is well known for its implementation of EIS which it calls Hypersmooth. Hypersmooth is pretty good as it goes but there&amp;#039;s a few things to know about it:
&lt;/p&gt;

&lt;p&gt;
- It produces “baked” videos; stabilization parameters cannot be changed in post and the original, uncropped frame cannot be recovered
- It is performed on the camera CPU in real time and thus has hard compute limits to contend with
- It does not know what will happen in the future and cannot benefit from the additional information; stabilization in post can view the entire gyro track and perform smoothing over a larger time window, improving results
- As mentioned above, distortion correction is not calibrated for water by default, reducing stabilization quality
&lt;/p&gt;

&lt;p&gt;
For these reasons I do not use Hypersmooth when shooting underwater.
&lt;/p&gt;

&lt;p&gt;
Instead I use [Gyroflow](&lt;a href=&quot;https://gyroflow.xyz/&quot; class=&quot;urlextern&quot; title=&quot;https://gyroflow.xyz/&quot; rel=&quot;ugc nofollow&quot;&gt;https://gyroflow.xyz/&lt;/a&gt;) with the following settings:
&lt;/p&gt;

&lt;p&gt;
- Lens profile → Advanced → Lens is under water
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; This will adjust the distortion matrix to account for the refractive index of water, at once improving stabilization results and correcting for water-induced distortion in addition to the lens distortion.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
- Export settings → Preserve other tracks
- Stabilization params set to whatever I think looks good, usually the default
&lt;/p&gt;

&lt;p&gt;
The reason for the second setting: GoPro MP4 files contain multiple data tracks. Example:
&lt;/p&gt;

&lt;p&gt;
```
$ ffprobe -i &amp;lt;vid&amp;gt;
&amp;lt;snip&amp;gt;
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;Duration: 00:12:48.77, start: 0.000000, bitrate: 117879 kb/s
Stream #0:0[0x1](eng): Video: hevc (Main) (hvc1 / 0x31637668), yuvj420p(pc, bt709), 4000x3000 [SAR 1:1 DAR 4:3], 117609 kb/s, 59.94 fps, 59.94 tbr, 60k tbn (default)
    Metadata:
      creation_time   : 2025-04-30T23:12:44.000000Z
      handler_name    : GoPro H.265
      vendor_id       : [0][0][0][0]
      encoder         : GoPro H.265 encoder
      timecode        : 19:11:34:59
Stream #0:1[0x2](eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 189 kb/s (default)
    Metadata:
      creation_time   : 2025-04-30T23:12:44.000000Z
      handler_name    : GoPro AAC  
      vendor_id       : [0][0][0][0]
      timecode        : 19:11:34:59
Stream #0:2[0x3](eng): Data: none (tmcd / 0x64636D74) (default)
    Metadata:
      creation_time   : 2025-04-30T23:12:44.000000Z
      handler_name    : GoPro TCD  
      timecode        : 19:11:34:59
Stream #0:3[0x4](eng): Data: bin_data (gpmd / 0x646D7067), 56 kb/s (default)
    Metadata:
      creation_time   : 2025-04-30T23:12:44.000000Z
      handler_name    : GoPro MET  &lt;/pre&gt;

&lt;p&gt;
```
&lt;/p&gt;

&lt;p&gt;
In the above example there&amp;#039;s 4 tracks:
- H265 video track
- AAC audio track
- TCD timecode track (empty)
- A binary track containing gopro-specific metadata. This is where gyro, GPS and other metadata is stored.
&lt;/p&gt;

&lt;p&gt;
By default, GyroFlow will drop the metadata tracks when producing a stabilized output. This metadata is valuable and other tools such as [Telemetry Overlay](&lt;a href=&quot;https://goprotelemetryextractor.com/&quot; class=&quot;urlextern&quot; title=&quot;https://goprotelemetryextractor.com/&quot; rel=&quot;ugc nofollow&quot;&gt;https://goprotelemetryextractor.com/&lt;/a&gt;) rely on it to function, so you want to keep this track.
&lt;/p&gt;

&lt;p&gt;
## Denoising
&lt;/p&gt;

&lt;p&gt;
I use Resolve as my video editor and find these settings to work as a decent starting point for most underwater footage:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.qlyoung.net/_detail/settings.png?id=underwater_videography_with_gopro&quot; class=&quot;media&quot; title=&quot;settings.png&quot;&gt;&lt;img src=&quot;https://wiki.qlyoung.net/_media/settings.png?w=600&amp;amp;tok=6a97bf&quot; class=&quot;media&quot; loading=&quot;lazy&quot; alt=&quot;&quot; width=&quot;600&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
## Color correction
&lt;/p&gt;

&lt;p&gt;
As I mentioned, I don&amp;#039;t do color correction. It takes too long and Natural looks good enough to my eye.
&lt;/p&gt;

&lt;p&gt;
If you want to, though, these are the things I have used:
- &lt;a href=&quot;https://www.leeminglutpro.com/&quot; class=&quot;urlextern&quot; title=&quot;https://www.leeminglutpro.com/&quot; rel=&quot;ugc nofollow&quot;&gt;https://www.leeminglutpro.com/&lt;/a&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; If you chose to shoot in FLAT or a LOG curve, these LUTs correct to Rec709&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
- &lt;a href=&quot;https://xtremestuff.net/store/gp-tune-transform/&quot; class=&quot;urlextern&quot; title=&quot;https://xtremestuff.net/store/gp-tune-transform/&quot; rel=&quot;ugc nofollow&quot;&gt;https://xtremestuff.net/store/gp-tune-transform/&lt;/a&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; This does the same thing in an ACES plugin&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/vehicle_leases">
        <dc:format>text/html</dc:format>
        <dc:date>2025-02-15T19:11:31+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>vehicle_leases</title>
        <link>https://wiki.qlyoung.net/vehicle_leases</link>
        <description>
&lt;h4 id=&quot;vehicle_leases&quot;&gt;vehicle leases&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
The other option to buying a car is to lease it. Leasing a car is basically renting it for a period of time (the &lt;strong&gt;term&lt;/strong&gt;), usually 2 or 3 years (24 or 36 months).
&lt;/p&gt;

&lt;/div&gt;

&lt;h3 class=&quot;sectionedit1&quot; id=&quot;financing&quot;&gt;Financing&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Of course, a vehicle is an asset with a variable value. When you rent a car from Enterprise for a few days, its value doesn&amp;#039;t change much, so you just pay for the service of renting the car. With a lease, you have the asset for years, and its fair market value can change substantially during that time, so financing becomes relevant.
&lt;/p&gt;

&lt;p&gt;
A vehicle lease is financially structured like this. The y-axis is dollars.
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;                 ___ $ msrp
                ░
                ░___ $ adjusted capitalized cost
               ╏░
               ╏░
Depreciation -&amp;lt;╏░___ $ midpoint
   Cost        ╏░
               ╏░
               ╏░___ $ residual
                ▓
                ▓
                ▓
                ▓
                ▓
                ▓
                ▓
                ▓
                ▓
                ▓
                ▓
                ▓
                ▓
                ▓
                ▓
                ▓
                ▓___ $ 0&lt;/pre&gt;

&lt;p&gt;
Start with the sticker price of the vehicle, that&amp;#039;s &lt;strong&gt;MSRP&lt;/strong&gt;. It&amp;#039;s how much the vehicle is worth at the start of the lease.
&lt;/p&gt;

&lt;p&gt;
Subtract any discounts &amp;amp; incentives and your down payment (if any). That is &lt;strong&gt;adjusted capitalized cost&lt;/strong&gt;. Don&amp;#039;t worry about why it&amp;#039;s called this, just understand that is what that number means.
&lt;/p&gt;

&lt;p&gt;
The &lt;strong&gt;residual&lt;/strong&gt; is a prediction of the fair market value of the car at the end of the term. Cars are &lt;a href=&quot;https://en.wikipedia.org/wiki/depreciation&quot; class=&quot;interwiki iw_wp&quot; title=&quot;https://en.wikipedia.org/wiki/depreciation&quot;&gt;depreciating&lt;/a&gt; assets and lose value as a function of time, mileage, wear, etc.
&lt;/p&gt;

&lt;p&gt;
The last number is the &lt;strong&gt;money factor&lt;/strong&gt;. This is a small decimal like .00329 that determines how much you pay in rent to the lessor. If the number is bigger you pay more, smaller you pay less.
&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;
When you lease a car you pay the lessor for two things:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; The depreciation of the vehicle over the time that you have it&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Rent charges for the privilege of renting the vehicle&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
There&amp;#039;s also taxes and insurance but we can ignore those for now.
&lt;/p&gt;

&lt;p&gt;
To understand depreciation charges, first consider that the lessor still owns the vehicle that you&amp;#039;re renting throughout the term. That&amp;#039;s their asset. They could have sold it for cash (which does not depreciate), or used it for its useful purpose (and bore the depreciation cost themselves). Instead they&amp;#039;re letting you use the asset during the term. During that time the fair market value of the car is decreasing. When they get their car back at the end of the term it will be worth less than it was at the start. Therefore it&amp;#039;s only fair that they be compensated for that depreciation. The most fair way to do this would be to assess the fair market value of the car each month relative to the previous month, and charge you the difference. However, business is done with contracts and most people who want to lease cars would not be happy about a company getting to decide what to charge them each month on the fly. They want to agree in advance on what they will pay each month. Thus it is necessary for the lessor to make a prediction about how much the car will depreciate over the term and decide how much they think it will be worth at the end of the term - the &lt;strong&gt;residual&lt;/strong&gt;.
Once you understand that, calculating how much you pay in depreciation is easy. It&amp;#039;s &lt;code&gt;(&lt;strong&gt;adjusted capitalized cost&lt;/strong&gt; - &lt;strong&gt;residual&lt;/strong&gt;)&lt;/code&gt;. In the graph this is represented as “depreciation cost.” Note that this is not equal to the &lt;em&gt;actual&lt;/em&gt; depreciation of the vehicle! That would be &lt;code&gt;(&lt;strong&gt;MSRP&lt;/strong&gt; - &lt;strong&gt;residual&lt;/strong&gt;)&lt;/code&gt;. The incentives &amp;amp; down payment are working in your favor here.
&lt;/p&gt;

&lt;p&gt;
The other part is the rent charge. This is charged to compensate the lessor for the privilege of using the vehicle. Basically this is the profit for the lessor. Calculating this quantity is where people usually get tripped up in lease math because it&amp;#039;s not obvious why it is calculated the way that it is. To calculate how much you pay in rent in total, it&amp;#039;s &lt;code&gt;(&lt;strong&gt;adjusted cap cost&lt;/strong&gt; + &lt;strong&gt;residual&lt;/strong&gt;) x &lt;strong&gt;money factor&lt;/strong&gt; x &lt;strong&gt;term&lt;/strong&gt;&lt;/code&gt;. It&amp;#039;s calculated this way because the money factor itself is defined as &lt;code&gt;APR/(12*2)&lt;/code&gt;. The 2 in the denominator of the money factor divides the sum of adjusted cap cost and residual to get the &lt;strong&gt;midpoint&lt;/strong&gt; shown in the bar chart (the average between the residual and adjusted cap cost). Why is the money factor defined this way? Because the “principal” (to use loan terms - this is not a loan) of the asset - ie the car&amp;#039;s value currently being held by the lessor - decreases from the adjusted cap cost down to the residual over the lifetime of the lease, and the intention is to charge rent on the total value of the asset currently being leased at any given time. That value is constantly changing over the term, but ends up being roughly the same as the midpoint. But returns are usually defined in percent. So they decided it was easier to take the APY they wanted, divide it by 12 to get monthly, then divide it again by 2 for reasons unknown to anyone. They could have clearly explained that rent is charged on the average of the adjusted cap cost and the residual, but instead they included the 1/2 factor in the definition of the money factor. The upshot of this is that the &lt;strong&gt;midpoint&lt;/strong&gt; defines how much rent you pay - anything that lowers the midpoint will reduce the rent charges. Also the APR is the money factor * 2400, I have to say that because it&amp;#039;s how every website “explains” what the money factor “is”. If you write anything about car leases you have to say that.
&lt;/p&gt;

&lt;p&gt;
Obviously to get the total amount you pay over the lifetime of the lease, add the depreciation cost and rent charge. To get it monthly divide by the term (in months).
&lt;/p&gt;

&lt;p&gt;
That&amp;#039;s it. That&amp;#039;s all there is to lease financing. It took way too long for me to figure this out. The math has numerous interesting consequences. Note that any discounts off the MSRP (and your down payment, if any) work double duty to reduce both the depreciation cost and the rent charge. Also note that the rent charge cannot be completely eliminated even if you prepay the entire lease.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Financing&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;financing&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;190-6255&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit2&quot; id=&quot;things_about_leases&quot;&gt;Things about leases&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
The biggest difference between leasing and buying is that at the end of the term, you have two options:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Return the car to the lessor&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Buy the car at a predetermined price (this price is almost always the same as the residual)&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
This sets up an interesting tradeoff. If at the end of the lease the FMV of the car is more than the residual, you can buy the vehicle and “make” money. So for example if the residual is $55k and at the end of the term the FMV of the car is actually $65k, you can buy the car at $55k. Now you have $10k of equity in the car. People tout this as a benefit of a lease. Of course, you need to decide the break even point; maybe you can make $5k of equity buying the car, but getting that $5k out of the car may not be worth the time and hassle of selling it.
&lt;/p&gt;

&lt;p&gt;
On the other hand if the FMV of the car at the end of the term is less than the residual, it&amp;#039;s in the lessee&amp;#039;s best interest to return the car. At that point the lessee has compensated the lessor for the depreciation cost of the vehicle plus some amount of rent charges, but if the asset is worth substantially less than its predicted residual, it&amp;#039;s possible the lessor makes little profit, no profit, or loses money on the whole lease. As you might imagine, lease contracts typically try to ensure this does not happen, either by setting a conservative residual or by charging a lot in rent (high money factor). In either case the lessor will usually sell the car as a certified pre-owned (the fate of most returned leases).
&lt;/p&gt;

&lt;p&gt;
The nice thing about this is that it allows lessors to get a new vehicle every few years while the lessor takes the depreciation risk. The downside is that if you lease repeatedly you&amp;#039;re constantly paying for the depreciation of new cars (plus rent), which is most rapid in the initial years of their existence, so in the long run it is substantially more expensive than buying a car and owning it for a long time. This is why the most sound financial advice is to purchase a vehicle that is likely to retain its value well (such as a Tacoma) rather than lease. However the benefits may be worth this cost to some people or in some situations.
&lt;/p&gt;

&lt;p&gt;
As an example of such a situation, if there is a high risk of the vehicle being worth very significantly less than a normal car over a 2-3y time frame, it can make sense to lease that vehicle and pay a premium to the lessor in exchange for them shouldering the depreciation risk. Vehicles produced by new companies that could go out of business during the term, or using new and unproven technologies, are illustrative examples. 
&lt;/p&gt;

&lt;p&gt;
Another consideration is that car is usually covered by the manufacturer warranty during all or most of the term. By repeatedly leasing vehicles the lessee has a car that is always under warranty. This can free them up to drive cars that they may not otherwise buy for reliability reasons.
&lt;/p&gt;

&lt;p&gt;
The final major thing to consider about leases is that they always have mileage limits. These are expressed in miles/year but they aren&amp;#039;t evaluated on a yearly basis, just at the end of the term. Any overage on the allowed mileage is usually charged at a relatively high rate, commonly $.30/mile, although this is sometimes negotiable. These mileage allowances are always factored into the residual, since mileage is one of the factors affecting the value of a car. There&amp;#039;s usually stipulations about excess wear and tear as well.
&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;
Other things to know about leases:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; The lessor owns the vehicle throughout the lease period.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; You insure the vehicle on your policy.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Down payments are not required.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; If the vehicle is totaled during the lease period, you lose any down payment.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Things about leases&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;things_about_leases&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:1,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;6256-&amp;quot;} --&gt;</description>
    </item>
    <item rdf:about="https://wiki.qlyoung.net/whole_foods_pre_seasoned_protein_tier_list">
        <dc:format>text/html</dc:format>
        <dc:date>2025-03-19T02:08:31+00:00</dc:date>
        <dc:creator>Anonymous (anonymous@undisclosed.example.com)</dc:creator>
        <title>whole_foods_pre_seasoned_protein_tier_list</title>
        <link>https://wiki.qlyoung.net/whole_foods_pre_seasoned_protein_tier_list</link>
        <description>
&lt;h2 class=&quot;sectionedit1&quot; id=&quot;whole_foods_pre_seasoned_protein_tier_list&quot;&gt;whole foods pre seasoned protein tier list&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;whole foods pre seasoned protein tier list&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;whole_foods_pre_seasoned_protein_tier_list&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:1,&amp;quot;range&amp;quot;:&amp;quot;1-54&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit2&quot; id=&quot;s&quot;&gt;S&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
&lt;a href=&quot;https://www.amazon.com/WHOLE-FOODS-KITCHENS-Carne-Asada/dp/B0C8F149F3&quot; class=&quot;urlextern&quot; title=&quot;https://www.amazon.com/WHOLE-FOODS-KITCHENS-Carne-Asada/dp/B0C8F149F3&quot; rel=&quot;ugc nofollow&quot;&gt;carne asada skirt steak&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;S&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;s&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:2,&amp;quot;range&amp;quot;:&amp;quot;55-166&amp;quot;} --&gt;
&lt;h3 class=&quot;sectionedit3&quot; id=&quot;b&quot;&gt;B&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
&lt;a href=&quot;https://www.amazon.com/WHOLE-FOODS-KITCHENS-Tandoori-Boneless/dp/B0C8DZ3P5D&quot; class=&quot;urlextern&quot; title=&quot;https://www.amazon.com/WHOLE-FOODS-KITCHENS-Tandoori-Boneless/dp/B0C8DZ3P5D&quot; rel=&quot;ugc nofollow&quot;&gt;tandoori spiced chicken breast&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- EDIT{&amp;quot;target&amp;quot;:&amp;quot;section&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;B&amp;quot;,&amp;quot;hid&amp;quot;:&amp;quot;b&amp;quot;,&amp;quot;codeblockOffset&amp;quot;:0,&amp;quot;secid&amp;quot;:3,&amp;quot;range&amp;quot;:&amp;quot;167-&amp;quot;} --&gt;</description>
    </item>
</rdf:RDF>
