<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  
  <title>Annika Backstrom - sixohthree.com</title>
  <subtitle>The personal blog of Annika Backstrom</subtitle>
  <link href="https://sixohthree.com/feeds/atom.xml" rel="self" />
  <link href="https://sixohthree.com/" />
  <updated>2026-04-18T00:00:00Z</updated>
  <id>https://sixohthree.com/</id>
  <author>
    <name>Annika Backstrom</name>
  </author>
  <entry>
    <title>What&#39;s in Your Kit? 2026</title>
    <link href="https://sixohthree.com/blog/2025/whats-in-your-kit/" />
    <updated>2026-04-18T00:00:00Z</updated>
    <id>https://sixohthree.com/blog/2025/whats-in-your-kit/</id>
    <content type="html">&lt;p&gt;I started list late last year but never finished it! Anything that&#39;s changed since then is struck through with the replacement listed.&lt;/p&gt;
&lt;p&gt;I have added a &lt;a href=&quot;https://sixohthree.com/uses&quot;&gt;/uses&lt;/a&gt; page for this list, which may be more up-to-date than this page.&lt;/p&gt;
&lt;h2 id=&quot;hardware&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://sixohthree.com/blog/2025/whats-in-your-kit/#hardware&quot;&gt;Hardware&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Fairphone 6&lt;/strong&gt; running &lt;strong&gt;Murena /e/OS&lt;/strong&gt; (de-Googled Android). Card holder
accessory so I can lose all my important stuff at the same time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;s&gt;MacBook Pro (2020)&lt;/s&gt;&lt;/strong&gt;&lt;s&gt;, Core i7. Feels pretty slow on modern macOS but i don&#39;t&lt;/s&gt;
&lt;s&gt;relish the idea of buying a new Apple computer.&lt;/s&gt; &lt;strong&gt;Lenovo ThinkPad X1 Carbon Gen. 12&lt;/strong&gt; (16 GB RAM, 500 GB HD) with &lt;strong&gt;Ubuntu 24.04&lt;/strong&gt;. My daily driver.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://shop.boox.com/products/palma&quot;&gt;&lt;strong&gt;Boox Palma&lt;/strong&gt;&lt;/a&gt;, Android e-ink device in a phone form factor.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mntmn.com/&quot;&gt;&lt;strong&gt;MNT Pocket Reform&lt;/strong&gt;&lt;/a&gt; open hardware laptop. I don&#39;t use this a ton, despite wanting it to be a part of my regular workflow. Not having a suspend function makes it a poor fit for my distracted lifestyle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;reMarkable 2&lt;/strong&gt; e-ink tablet, rarely. My son has commandeered this for drawing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AMD Ryzen 7&lt;/strong&gt; PC with &lt;strong&gt;Nvidia 4070 RTX&lt;/strong&gt;. For gaming and dev work using Windows
Subsystem for Linux (WSL).&lt;/p&gt;
&lt;p&gt;My work provides me with a &lt;strong&gt;MacBook Pro M4&lt;/strong&gt;. I use a &lt;strong&gt;Logitech StreamCam&lt;/strong&gt;, &lt;strong&gt;M-AUDIO M-Track Solo&lt;/strong&gt;, &lt;strong&gt;Marantz Professional MPM-1000&lt;/strong&gt; condenser mic, and &lt;strong&gt;Sennheiser HD 599&lt;/strong&gt; headphones (those open back ones in the ivory and brown colours).&lt;/p&gt;
&lt;h2 id=&quot;software&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://sixohthree.com/blog/2025/whats-in-your-kit/#software&quot;&gt;Software&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Neovim&lt;/strong&gt; with &lt;a href=&quot;https://www.lazyvim.org/&quot;&gt;&lt;strong&gt;LazyVim&lt;/strong&gt;&lt;/a&gt; for coding.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Termius&lt;/strong&gt; (sparingly) for mobile SSH.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Signal&lt;/strong&gt; (often) and &lt;a href=&quot;https://delta.chat/en/&quot;&gt;&lt;strong&gt;Delta Chat&lt;/strong&gt;&lt;/a&gt; (rarely, though I wish this was my primary) for encrypted chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Synology Photos&lt;/strong&gt; for photo organisation. I would like to self-host &lt;a href=&quot;https://immich.app/&quot;&gt;&lt;strong&gt;Immich&lt;/strong&gt;&lt;/a&gt; some day.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;FreshRSS&lt;/strong&gt; (self-hosted) for feed reading, self-hosted.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gitea&lt;/strong&gt; (self-hosted) for git repositories. Possibly moving to &lt;strong&gt;Forgejo&lt;/strong&gt; on &lt;a href=&quot;https://source.tube&quot;&gt;source.tube&lt;/a&gt; (part of &lt;a href=&quot;https://omg.lol&quot;&gt;omg.lol&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tusky&lt;/strong&gt; for &lt;strong&gt;GoToSocial&lt;/strong&gt; and &lt;strong&gt;Mastodon&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://convos.chat/&quot;&gt;&lt;strong&gt;Convos&lt;/strong&gt;&lt;/a&gt; (self-hosted) for IRC.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;s&gt;Firefox&lt;/s&gt;&lt;/strong&gt; &lt;strong&gt;Zen Browser&lt;/strong&gt; (Firefox fork) for web browsing, &lt;strong&gt;Lagrange&lt;/strong&gt; for Gemini and Gopher.&lt;/p&gt;
&lt;h2 id=&quot;websites&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://sixohthree.com/blog/2025/whats-in-your-kit/#websites&quot;&gt;Websites&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;DuckDuckGo&lt;/strong&gt; for search.&lt;/p&gt;
&lt;p&gt;Vanishingly few regular websites these days. A sad state of affairs.&lt;/p&gt;
&lt;h2 id=&quot;miscellaneous&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://sixohthree.com/blog/2025/whats-in-your-kit/#miscellaneous&quot;&gt;Miscellaneous&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://nordace.com/en/product/nordace-aerial-infinity-flap-backpack/&quot;&gt;&lt;strong&gt;Nordace Aerial Infinity Flap&lt;/strong&gt;&lt;/a&gt; backpack, 22L.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Pinned Tabs in Zen Browser</title>
    <link href="https://sixohthree.com/blog/2026/pinned-tabs-in-zen-browser/" />
    <updated>2026-01-03T00:00:00Z</updated>
    <id>https://sixohthree.com/blog/2026/pinned-tabs-in-zen-browser/</id>
    <content type="html">&lt;p&gt;I recently switched from Firefox to &lt;a href=&quot;https://zen-browser.app/&quot;&gt;Zen&lt;/a&gt;, since Mozilla won&#39;t stop fucking
around with inane AI features and Google cannot be trusted. Zen has some great
features, including a UI mostly shoved into the sidebar, pop-up pages, and split
tabs. It also handles &lt;strong&gt;pinned tabs&lt;/strong&gt; &lt;em&gt;really&lt;/em&gt; nicely but I didn&#39;t grok them
right away, so I&#39;m writing up my findings here.&lt;/p&gt;
&lt;p&gt;In Firefox, I have a bookmark bar with folders of frequently-viewed pages: Jira
boards, collections of pull requests, dashboards, etc. Clicking on a bookmark
opens an ephemeral tab that I close when I&#39;m done. I also have a few pinned tabs
for things like email.&lt;/p&gt;
&lt;p&gt;Zen, on the other hand, organises pinned tabs in two different ways, with
different visual treatment and ways of handling tab persistence.&lt;/p&gt;
&lt;p class=&quot;figure&quot;&gt;&lt;img src=&quot;https://sixohthree.com/media/2026/01/zen-sidebar.png&quot; alt=&quot;Screenshot of the Zen browser sidebar. There is a 3x2 grid of &amp;quot;Essentials&amp;quot; tabs at the top, with a folder named &amp;quot;to read&amp;quot; below, containing three tabs for various sites.&quot; title=&quot;Essentials at the top in the blue dashed box, and pinned tabs below in the red solid box&quot;&gt;&lt;/p&gt;
&lt;p&gt;At the top we have &amp;quot;Essentials,&amp;quot; a grid of pins (the blue dashed box in the
above screenshot). They are somewhat similar to pinned tabs in Firefox.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Clicking an on-site link will navigate within the tab.&lt;/li&gt;
&lt;li&gt;Clicking an off-site link in the tab will open a pop-up &amp;quot;Glance&amp;quot; page.&lt;/li&gt;
&lt;li&gt;Closing or &amp;quot;unloading&amp;quot; the tab will reset the pin to its original location.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The tabs between Essentials and the &amp;quot;New Tab&amp;quot; button (red solid box) are &lt;em&gt;also&lt;/em&gt;
pinned tabs, but they work a bit differently, especially when organised into
folders:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Same behaviour as Essentials with regards to clicking links and resetting the
tab.&lt;/li&gt;
&lt;li&gt;Collapsing a folder of pins will hide child tabs and sub-folders.&lt;/li&gt;
&lt;li&gt;Hovering the folder shows a popup with the folder contents.&lt;/li&gt;
&lt;li&gt;Clicking a tab in the popup will show that tab under the collapsed folder,
until that tab is unloaded. The various &amp;quot;Switch to Tab&amp;quot; methods that open a
pin will act the same way.&lt;/li&gt;
&lt;li&gt;Tabs can be unloaded individually or folder-by-folder.&lt;/li&gt;
&lt;li&gt;A pinned tab title can be renamed by double-clicking the name in the sidebar.&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;figure&quot;&gt;&lt;img src=&quot;https://sixohthree.com/media/2026/01/zen-pinned-tabs.png&quot; alt=&quot;Screenshot of a collapsed folder of pinned tabs in the Zen browser sidebar. One sub-tab is visible, and two more
show up in a popup to the right.&quot; title=&quot;Collapsed folders show open tabs, with other tabs in the hover.&quot;&gt;&lt;/p&gt;
&lt;p&gt;In aggregate this feels really different from how I use the Bookmark Bar and
pinned tabs in Firefox, but I&#39;m a total convert after a day using it. Many of
the pages I visit in a workday are recurring and temporary. Zen lets me load my
pull requests tab, click around to do my work, and Cmd-W to close the tab to
reset it for next time. I can even bind a key to quickly reset the pinned tab to
the original URL. This keeps the page confined to a single consistent part of
the sidebar, which is a real boon to the way I want to work.&lt;/p&gt;
&lt;p&gt;Props to the Zen folks for a really great addition to the browser landscape.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Migrating from Logseq to Trilium</title>
    <link href="https://sixohthree.com/blog/2025/migrating-logseq-to-trilium/" />
    <updated>2025-12-16T00:00:00Z</updated>
    <id>https://sixohthree.com/blog/2025/migrating-logseq-to-trilium/</id>
    <content type="html">&lt;p&gt;Here&#39;s how I migrated my 500-something page knowledge graph/base/whatever from
&lt;a href=&quot;https://logseq.com/&quot;&gt;Logseq&lt;/a&gt; to &lt;a href=&quot;https://triliumnotes.org/&quot;&gt;Trilium&lt;/a&gt;. Just want the script? Download &lt;a href=&quot;http://sixohthree.com/media/2025/11/trilium-import.py&quot;&gt;trilium-import.py&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Importing Logseq into Trilium is not streamlined. You can zip up parts of your
Logseq directory and import them through the Trilium app, but the notes will
retain Logseq&#39;s bullet point structure and document links will break.
Block-level meta for things like Cards will be inlined into the bullet point.
Document links are replaced with the text &amp;quot;missing note.&amp;quot; The content comes over
but overall it&#39;s a mess.&lt;/p&gt;
&lt;p&gt;Trilium itself &lt;a href=&quot;https://github.com/Nriver/trilium-py#import-from-logseq&quot;&gt;recommends&lt;/a&gt; &lt;code&gt;upload_md_folder&lt;/code&gt; from trilium-py, but this is
only marginally better than the manual import. Linked documents retain their
names instead of saying &amp;quot;missing note&amp;quot; but they are not links. This still didn&#39;t
feel like a good starting point.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/petervanhaaften/logseq2trilium&quot;&gt;logseq2trilium&lt;/a&gt; did a good job of formatting my Logseq graph for Trilium. It
splits bullet points into paragraphs, which is a better fit for Trilium. The
links come across properly. Images linked in documents work after importing. The
journal files also have a better name.&lt;/p&gt;
&lt;h2 id=&quot;cleaning-up-the-import&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://sixohthree.com/blog/2025/migrating-logseq-to-trilium/#cleaning-up-the-import&quot;&gt;Cleaning up the import&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once imported, everything was tucked away inside its own top-level folder. I
needed the 3 years of journal files to fit in with Trilium&#39;s Journal: year
folders, with nested day-of-month folders, with journals inside.&lt;/p&gt;
&lt;p&gt;As a first step, I selected all the journal notes in the Trilium app and
manually applied the template &amp;quot;Day Note Template&amp;quot; using Bulk Actions &amp;gt; Add
Relation: Template.&lt;/p&gt;
&lt;p&gt;I wrote a script &lt;a href=&quot;http://sixohthree.com/media/2025/11/trilium-import.py&quot;&gt;trilium-import.py&lt;/a&gt; that uses trilium-py to clean up the
imported journal files:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a set of year notes with nested month notes (this does not happen
lazily)&lt;/li&gt;
&lt;li&gt;Grab all imported child notes from a hardcoded parent note ID&lt;/li&gt;
&lt;li&gt;Update titles to match the format &amp;quot;10 - Monday&amp;quot; (day of month, weekday)&lt;/li&gt;
&lt;li&gt;Add journal note attributes&lt;/li&gt;
&lt;li&gt;Move notes to correct spot in the hierarchy&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The import felt much more manageable after getting the journal sorted, and I
could get to the more enjoyable part of reorganising and settings icons.&lt;/p&gt;
&lt;h2 id=&quot;trilium-import.py&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://sixohthree.com/blog/2025/migrating-logseq-to-trilium/#trilium-import.py&quot;&gt;&lt;a href=&quot;http://trilium-import.py&quot;&gt;trilium-import.py&lt;/a&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; trilium_py&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; ETAPI

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; calendar
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; pprint

TOKEN_ID &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;8EnnBezl2LMP_oa4bkgdp9oG/CYRjyl+MihBIJqbB5HcwipVomH+ovSU=&quot;&lt;/span&gt;
JOURNAL_ID &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TPz1EGZtugH3&quot;&lt;/span&gt;
IMPORTED_JOURNAL_PARENT_ID &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Sy22f3PKxxka&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Years to create (note that stop value is not included)&lt;/span&gt;
YEARS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2022&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2026&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;has_attr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;note&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; attribute&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;attr&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; attribute &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; attr &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; note&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;attributes&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Graph&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        server_url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;http://localhost:37840&#39;&lt;/span&gt;
        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ea &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ETAPI&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;server_url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; TOKEN_ID&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Find a note by its title.&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; parent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        res &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ea&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;search_note&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            search&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;note.title = &#39;%s&#39;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            ancestorNoteId&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;parent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            fastSearch&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            orderBy&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            limit&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;results&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;results&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Find single note by its ID, or raise an exception.&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;find_by_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        res &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ea&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;search_note&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            search&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;note.noteId = &#39;%s&#39;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            fastSearch&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;results&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;results&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;raise&lt;/span&gt; RuntimeException&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;could not find note with id %s&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Find a note by its name or create it.&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;find_or_create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; parentNoteId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        note &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;find&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;parentNoteId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; note&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            note &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;create_empty_note&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;parentNoteId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; note

    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Create an empty note.&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;create_empty_note&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; parent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        res &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ea&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;create_note&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            parentNoteId&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;parent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            title&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token builtin&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            content&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;...&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;# For some reason, we can&#39;t use an empty content above.&lt;/span&gt;
        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ea&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;update_note_content&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;res&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;note&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;noteId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;note&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Set a label attribute if the attribute does not already exist.&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add_label_if_missing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; note&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; has_attr&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;note&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ea&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;create_attribute&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                noteId&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;note&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;noteId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token builtin&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;label&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                value&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                isInheritable&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;False&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Create the requested year folders, if they do not exist.

    Set missing attributes for yearNote and sorting.
    &quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;populate_years&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# It would be better to create this lazily based on incoming notes.&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; year &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; YEARS&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            yearNote &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;find_or_create&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;JOURNAL_ID&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; year&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

            self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;add_label_if_missing&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;yearNote&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yearNote&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; year&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;add_label_if_missing&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;yearNote&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sorted&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

            self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notes&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;year&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;yearNote&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

            self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;populate_months&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;year&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Create month folders for a year, if they do not exist.

    Set missing attributes for monthNote and sorting.
    &quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;populate_months&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; year&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;yearNote&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; months&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notes&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;year&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; i &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;%02d - %s&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; calendar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;month_name&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

            monthNote &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;find_or_create&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;yearNote&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;noteId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

            self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;add_label_if_missing&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;monthNote&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;monthNote&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;%d-%02d&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;year&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;add_label_if_missing&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;monthNote&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sorted&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

            months&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; monthNote

    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Generator to return all children of a given note.&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;imported_journals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; parentId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        results &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ea&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;search_note&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            search&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;note.noteId = &#39;%s&#39;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;parentId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            fastSearch&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            orderBy&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;results&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;results&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;raise&lt;/span&gt; RuntimeException&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cannot find parent journal by ID&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        parentJournal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; results&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;results&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; childId &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; parentJournal&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;childNoteIds&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;find_by_id&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;childId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Given a journal note, fix titles and add journal attributes, and
    relocate to Journal tree.&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;relocate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; note&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; note&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; weekday&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;split&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;year&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; month&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; day&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;split&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        year &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;year&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        month &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;month&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        day &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;day&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        newTitle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;%02d - %s&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;day&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; weekday&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;{0} -&gt; {1}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newTitle&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;add_label_if_missing&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;note&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;dateNote&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;%04d-%02d-%02d&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;year&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; month&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; day&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ea&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;patch_note&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;noteId&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;note&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;noteId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;newTitle&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        monthNote &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notes&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;year&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;month&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ea&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;create_branch&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            noteId&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;note&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;noteId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            parentNoteId&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;monthNote&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;noteId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ea&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;delete_branch&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            branchId&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;%s_%s&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;IMPORTED_JOURNAL_PARENT_ID&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; note&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;noteId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; __name__ &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;__main__&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    graph &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Graph&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Populating years... &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    graph&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;populate_years&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;done.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; note &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; graph&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;imported_journals&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;IMPORTED_JOURNAL_PARENT_ID&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        graph&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;relocate&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;note&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  <entry>
    <title>Fediverse Rules of Engagement</title>
    <link href="https://sixohthree.com/blog/2025/fediverse-rules-of-engagement/" />
    <updated>2025-10-18T00:00:00Z</updated>
    <id>https://sixohthree.com/blog/2025/fediverse-rules-of-engagement/</id>
    <content type="html">&lt;p&gt;These are my &amp;quot;rules&amp;quot; for how I interact on the Fediverse (Mastodon, GoToSocial,
and other ActivityPub friends). I don&#39;t claim that they&#39;re perfect or correct or
appropriate for anyone other than me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Follow and unfollow liberally.&lt;/strong&gt; Don&#39;t be afraid to follow someone new, even
if I&#39;m happy with my feed. Follow folks boosted into my feed, follow folks who
interact with me, follow from the federated timeline. Unfollow if I want to, but
consider using private notes, hiding boosts, and filters as &amp;quot;strikes.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Check remote replies before sending my own reply.&lt;/strong&gt; Every server has a partial
window of the Fediverse. Is my reply redundant? Is the person already drowning
in replies?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Do not meta.&lt;/strong&gt; &lt;a href=&quot;https://xoxo.zone/@annika/113548210141988451&quot;&gt;Meta is the mind killer&lt;/a&gt;. Meta is the little death that
brings total obliteration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Don&#39;t overload posts with disclaimers.&lt;/strong&gt; They clutter up the post and deprive
me of the opportunity to block jerks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Always post alt text.&lt;/strong&gt; Do not boost anything without alt text, but don&#39;t
harass individuals for not writing alt text.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Treat unverified accounts of &amp;quot;famous&amp;quot; people as illegitimate.&lt;/strong&gt; Misinfo is too
rampant to risk engaging with impersonators.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Don&#39;t boost news that lacks a clear citation.&lt;/strong&gt; Be wary of screenshots of text
that appear to be from legit sources, or are from unidentifiable sources.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No GenAI.&lt;/strong&gt; Don&#39;t boost, don&#39;t interact, consider muting or blocking the
poster depending on severity. Be extremely wary of &amp;quot;activists&amp;quot; posting GenAI
slop.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Eleventy Migration Notes</title>
    <link href="https://sixohthree.com/blog/2025/eleventy-migration-notes/" />
    <updated>2025-10-13T00:00:00Z</updated>
    <id>https://sixohthree.com/blog/2025/eleventy-migration-notes/</id>
    <content type="html">&lt;p&gt;In lieu of a retrospective of my weekend-long mad dash to convert this site from
&lt;a href=&quot;https://getpelican.com/&quot;&gt;Pelican&lt;/a&gt; to &lt;a href=&quot;https://11ty.dev&quot;&gt;Eleventy&lt;/a&gt; (11ty), here
are a few pages I found helpful and some things I learned along the way.&lt;/p&gt;
&lt;p&gt;Neovim &lt;a href=&quot;https://kezhenxu94.me/blog/lazyvim-project-specific-settings&quot;&gt;supports a &lt;code&gt;.lazy.lua&lt;/code&gt;&lt;/a&gt; file for project- or directory-specific
settings.&lt;/p&gt;
&lt;p&gt;Working with &lt;a href=&quot;https://cfjedimaster.github.io/eleventy-blog-guide/guide.html#:~:text=Category%20Pages,-Alright&quot;&gt;custom taxonomies in Eleventy&lt;/a&gt; means jumping through a
couple hoops but it&#39;s not impossible. Once you get a handle of the Collections
API, it&#39;s alright.&lt;/p&gt;
&lt;p&gt;It&#39;s helpful to have a script for manipulating &lt;a href=&quot;https://www.11ty.dev/docs/data-frontmatter/&quot;&gt;front matter&lt;/a&gt;.
&lt;a href=&quot;https://sixohthree.com/media/2025/10/front-matter.js&quot;&gt;Here&#39;s mine&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Links (16 July 2025)</title>
    <link href="https://sixohthree.com/blog/2025/links-16-july-2025/" />
    <updated>2025-07-16T00:00:00Z</updated>
    <id>https://sixohthree.com/blog/2025/links-16-july-2025/</id>
    <content type="html">&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://chaos.social/@VoltPaperScissors/114388324275114780&quot;&gt;DIY Book Lamp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pikvm.org/&quot;&gt;PiKVM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.experimental-history.com/p/28-slightly-rude-notes-on-writing&quot;&gt;28 slightly rude notes on writing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://json-structure.org/&quot;&gt;JSON Structure&lt;/a&gt; -- JSON Structure is a data
structure definition language that enforces strict typing, modularity, and
determinism.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fedify.dev/&quot;&gt;Fedify&lt;/a&gt; -- A TypeScript library for building federated
server apps powered by ActivityPub and other standards, so-called fediverse&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.aaihs.org/the-sinners-movie-syllabus/&quot;&gt;The ‘Sinners’ Movie Syllabus&lt;/a&gt;
via &lt;a href=&quot;https://todon.eu/@jalcine&quot;&gt;@jalcine@todon.eu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sli.dev/&quot;&gt;Slidev&lt;/a&gt; -- Presentation slides for developers&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://charm.sh/&quot;&gt;Charm&lt;/a&gt; -- Various commands and helpers for pretty
interactions in your CLI&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.conventionalcommits.org/en/v1.0.0/#summary&quot;&gt;Conventional Commits&lt;/a&gt;
-- A specification for adding human and machine readable meaning to commit
messages&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://corrode.dev/blog/flattening-rusts-learning-curve/&quot;&gt;Flattening Rust&#39;s Learning Curve&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/PechaKucha&quot;&gt;PechaKucha&lt;/a&gt; -- &amp;quot;a storytelling
format in which a presenter shows 20 slides for 20 seconds per slide&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.saysomethingin.com/&quot;&gt;SaySomethingin&lt;/a&gt; language learning&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://24hourtime.info/&quot;&gt;24hourtime.info&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://sunclock.net/&quot;&gt;sunclock.net&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/open-cli-tools/concurrently#readme&quot;&gt;Concurrently&lt;/a&gt; -- Run
multiple commands concurrently&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://rioterm.com/&quot;&gt;Rio&lt;/a&gt; -- A modern terminal for the 21st century&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ibm.com/able/toolkit/tools/&quot;&gt;IBM Equal Access Toolkit&lt;/a&gt; --
accessibility tools from IBM&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Links (22 April 2025)</title>
    <link href="https://sixohthree.com/blog/2025/links-22-april-2025/" />
    <updated>2025-04-22T00:00:00Z</updated>
    <id>https://sixohthree.com/blog/2025/links-22-april-2025/</id>
    <content type="html">&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://bylandandsea.ie/&quot;&gt;bylandandsea.ie&lt;/a&gt; – Travel to and from Ireland without flying&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://garagehq.deuxfleurs.fr/&quot;&gt;Garage&lt;/a&gt; — An open-source distributed object storage service tailored for
self-hosting, supporting the S3 API&lt;/li&gt;
&lt;li&gt;Human chains feel like a metaphor in these dark times
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.nbcnews.com/news/us-news/ice-tries-detain-man-tennessee-home-neighbors-form-human-chain-n1032791&quot;&gt;ICE came for their neighbor, so these Tennesseans formed a human chain to
protect him&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.theguardian.com/us-news/2025/apr/17/book-brigade-us-town-forms-human-chain-to-move-9100-books-one-by-one&quot;&gt;‘Book brigade’: US town forms human chain to move 9,100 books
one-by-one&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stty -ixon&lt;/code&gt; to stop ^S from freezing your terminal via
&lt;a href=&quot;https://social.jvns.ca/@b0rk/114354742870242559&quot;&gt;@b0rk@social.jvns.ca&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.chiark.greenend.org.uk/~ianmdlvl/rust-polyglot/&quot;&gt;Rust for the Polyglot Programmer&lt;/a&gt; via
&lt;a href=&quot;https://infosec.exchange/@raptor/114363710652153874&quot;&gt;@raptor@infosec.exchange&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.seriouseats.com/chai-recipe-8364307&quot;&gt;How to Make Chai&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fxrant.blogspot.com/2025/04/the-movie-mistake-mystery-from-revenge.html?m=1&quot;&gt;The Movie Mistake Mystery from &amp;quot;Revenge of the Sith&amp;quot;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://typst.app/&quot;&gt;Typst&lt;/a&gt; – A more productive workflow for science.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://twinery.org/cookbook/&quot;&gt;Twine Cookbook&lt;/a&gt; – &amp;quot;This Cookbook contains documentation, tips, and
examples for using the non-linear story creation tool &lt;a href=&quot;https://twinery.org/&quot;&gt;Twine&lt;/a&gt;&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Webhooks as a (Systemd) Service</title>
    <link href="https://sixohthree.com/blog/2025/webhooks-as-systemd-service/" />
    <updated>2025-04-12T00:00:00Z</updated>
    <id>https://sixohthree.com/blog/2025/webhooks-as-systemd-service/</id>
    <content type="html">&lt;p&gt;The &lt;a href=&quot;https://xoxo.zone/docs/&quot;&gt;&amp;quot;docs&amp;quot; microsite&lt;/a&gt; for &lt;a href=&quot;https://xoxo.zone/&quot;&gt;xoxo.zone&lt;/a&gt; is a static page built with
Eleventy. I explicitly didn&#39;t want to overcomplicate the site&#39;s setup with a
cloud build process triggered by commit actions: the static site is compiled
locally and committed alongside the content changes. A cron on the server runs
&lt;code&gt;git pull&lt;/code&gt; on the repo every 5 minutes. The web server can directly serve the
site without any additional build.&lt;/p&gt;
&lt;p&gt;The site is updated a couple times a month on average. Of the 8,640 average
monthly cron git pulls, 8,638 will do nothing. The net impact of this is
probably negligible, but it did annoy me. Besides, Codeberg (my forge of choice
for xoxo) gets the occasional DDoS and I&#39;m sure is getting hammered by &amp;quot;AI&amp;quot;
scrapers that never sleep. Why send them more traffic than necessary?&lt;/p&gt;
&lt;p&gt;I wanted to update the site when I pushed to the repo, without a complex
configuration that would be difficult for someone else to pick up. Maybe I could
write a lightweight web server that listened for a request and perform an
action?&lt;/p&gt;
&lt;h2 id=&quot;stop-giving-things-generic-names&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://sixohthree.com/blog/2025/webhooks-as-systemd-service/#stop-giving-things-generic-names&quot;&gt;Stop giving things generic names&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The obvious choice for triggering an action on push is a &lt;a href=&quot;https://en.wikipedia.org/wiki/Webhook&quot;&gt;Webhook&lt;/a&gt;, which
Codeberg supports natively. Expose an endpoint, verify incoming requests, run a
command.&lt;/p&gt;
&lt;p&gt;Doing some preliminary searches, I stumbled across the extremely
generically-named &lt;a href=&quot;https://github.com/adnanh/webhook/&quot;&gt;webhook&lt;/a&gt;, a Go web server for triggering commands based
on webhook requests. It&#39;s apt-installable on Ubuntu, has a simple configuration
file syntax in JSON or YAML, and doesn&#39;t mind being proxied behind nginx. From a
maintenance perspective, that&#39;s better than rolling my own server.&lt;/p&gt;
&lt;h2 id=&quot;webhook-configuration&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://sixohthree.com/blog/2025/webhooks-as-systemd-service/#webhook-configuration&quot;&gt;Webhook configuration&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After &lt;code&gt;apt install webhook&lt;/code&gt;, I customised some of the launch parameters with
&lt;code&gt;systemctl edit webhook&lt;/code&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://sixohthree.com/blog/2025/webhooks-as-systemd-service/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;ConditionPathExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;ConditionPathExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;/etc/webhook.yaml&lt;/span&gt;

&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;Service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;ExecStart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;ExecStart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;/usr/bin/webhook -nopanic -hooks /etc/webhook.yaml -ip 127.0.0.1 -port 9899 -urlprefix my-webhook-prefix&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;www-data&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;Group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;www-data&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I configured my hook in &lt;code&gt;/etc/webhook.yaml&lt;/code&gt;. &lt;code&gt;webhook&lt;/code&gt; has an awkward syntax for
passing arguments to commands, so I made a small bin script to wrap &lt;code&gt;cd&lt;/code&gt; and
&lt;code&gt;git pull ...&lt;/code&gt;. &lt;code&gt;webhook&lt;/code&gt; also includes &amp;quot;matchers&amp;quot; to further customise the hook
based on incoming parameters. This config performs request signature
verification based on a shared secret, and filters for push events.&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; deploy&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;docs
  &lt;span class=&quot;token key atrule&quot;&gt;execute-command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /usr/local/bin/xoxo&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;docs&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;pull
  &lt;span class=&quot;token key atrule&quot;&gt;response-message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ok
  &lt;span class=&quot;token key atrule&quot;&gt;trigger-rule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; payload&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;hmac&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;sha256
          &lt;span class=&quot;token key atrule&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; my_secret
          &lt;span class=&quot;token key atrule&quot;&gt;parameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; header
            &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; X&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;Forgejo&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;Signature
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; value
          &lt;span class=&quot;token key atrule&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; push
          &lt;span class=&quot;token key atrule&quot;&gt;parameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; header
            &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; X&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;Forgejo&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;Event&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I proxied the requests through Nginx:&lt;/p&gt;
&lt;pre class=&quot;language-nginx&quot;&gt;&lt;code class=&quot;language-nginx&quot;&gt;&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;upstream&lt;/span&gt; webhooks&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;server&lt;/span&gt; 127.0.0.1:9899 fail_timeout=5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;server&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# the rest of the server config...&lt;/span&gt;

    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;location&lt;/span&gt; ~ ^/my-webhook-prefix/&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_pass&lt;/span&gt; http://webhooks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&#39;s enough to get a working webhook. No more cron needed!&lt;/p&gt;
&lt;h2 id=&quot;the-future&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://sixohthree.com/blog/2025/webhooks-as-systemd-service/#the-future&quot;&gt;The future&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Mastodon itself supports webhooks, and I&#39;d love to improve our admin hooks in
the future. Today we get a Discord message when a report is created, but it&#39;s
not very readable and there&#39;s no ability to update the initial message when the
report is actioned. &lt;code&gt;webhook&lt;/code&gt; feels like a good starting place to improve that
experience.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Technically these are in an &lt;a href=&quot;https://www.ansible.com/&quot;&gt;Ansible&lt;/a&gt; playbook, but I&#39;m simplifying so
the code examples are more self-contained. &lt;a href=&quot;https://sixohthree.com/blog/2025/webhooks-as-systemd-service/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <title>Migrating xoxo.zone to OVHcloud</title>
    <link href="https://sixohthree.com/blog/2025/migrating-xoxo-zone-to-ovhcloud/" />
    <updated>2025-04-10T00:00:00Z</updated>
    <id>https://sixohthree.com/blog/2025/migrating-xoxo-zone-to-ovhcloud/</id>
    <content type="html">&lt;p&gt;Mastodon hosted on &lt;a href=&quot;https://xoxo.zone/&quot;&gt;xoxo.zone&lt;/a&gt; is now living on its new server at OVHcloud.
We were hosted at Hetzner for &lt;a href=&quot;https://xoxo.zone/docs/&quot;&gt;just about 2 years&lt;/a&gt;, but draconian terms of
service and &lt;a href=&quot;https://tenforward.blog/hetzner-considered-hostile-a-psa/&quot;&gt;some scary experiences&lt;/a&gt; for other communities made the move
inevitable. I evaluated a few EU-based hosts, including Netcup and Scaleway. OVH
hit the sweet spot of pricing, specs, and terms of use.&lt;/p&gt;
&lt;h2 id=&quot;we-built-this-shitty&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://sixohthree.com/blog/2025/migrating-xoxo-zone-to-ovhcloud/#we-built-this-shitty&quot;&gt;We built this shitty&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Due to a miscalculation, the server I provisioned on Hetzner used spinning rust
HDDs instead of SSDs. This gave us a ton of storage overhead we didn&#39;t need, and
was also slow as fuck and made many simple things very painful. Even git
operations and restarting services could take minutes instead of seconds.&lt;/p&gt;
&lt;p&gt;The upgrade to Mastodon v4.2.0 last October was particularly painful. I first
upgraded the server from Ubuntu 20.04 to Ubuntu 22.04, and planned to keep going
to Ubuntu 24.04. This required a PostgreSQL upgrade from v15 to v17. I started
this upgrade at 15:00, and gave up for the day when the database finally
finished rebuilding at 01:30. The server was down the whole time. Bummer.&lt;/p&gt;
&lt;h2 id=&quot;a-lot-of-effort-went-into-making-this-look-effortless&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://sixohthree.com/blog/2025/migrating-xoxo-zone-to-ovhcloud/#a-lot-of-effort-went-into-making-this-look-effortless&quot;&gt;A lot of effort went into making this look effortless&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A challenge of running a server like this is needing to know a little bit about
everything. It was clear to me that I could do better than 10.5 hours of
downtime, but I wasn&#39;t sure how to get there. I&#39;ve done a lot of reading about
PostgreSQL migration strategies since October.&lt;/p&gt;
&lt;p&gt;The server database is backed up twice a day. The &lt;code&gt;pg_dump&lt;/code&gt; takes about 2 hours,
and the upload is another half hour, to say nothing of restoring the db on a new
host. Not awesome.&lt;/p&gt;
&lt;p&gt;A test &lt;code&gt;rsync --checksum&lt;/code&gt; of the database took about 80 minutes, even for
subsequent rsyncs that (in theory) had to transfer less data.&lt;/p&gt;
&lt;p&gt;Replication was daunting, but I stuck with it. In the end it was pretty painless
and worked &lt;em&gt;extremely&lt;/em&gt; well.&lt;/p&gt;
&lt;p&gt;I created a replication role on the old server, xoxo-4:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE ROLE xoxo5 WITH REPLICATION PASSWORD &#39;secret_password&#39; LOGIN;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And I updated the access rules in &lt;code&gt;/etc/postgresql/17/main/pg_hba.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Allow replication from xoxo5@10.0.0.5
host    replication     xoxo5           10.0.0.5/32       scram-sha-256
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On the new host, xoxo-5, I emptied out the &lt;code&gt;/var/lib/postgresql/17/main&lt;/code&gt;
directory and enabled replication:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo -u postgres pg_basebackup -h 10.0.0.4 -p 5432 -U xoxo5 -D /var/lib/postgresql/17/main/ -Fp -Xs -R
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This took a few hours but it was worth every second. Once the backup was done,
the results were extremely promising: &lt;code&gt;select * from pg_stat_replication&lt;/code&gt; and
&lt;a href=&quot;https://pgmetrics.io/&quot;&gt;pgmetrics&lt;/a&gt; showed delays in the milliseconds, and spot checking counts in
the &lt;code&gt;statuses&lt;/code&gt; and &lt;code&gt;accounts&lt;/code&gt; tables looked good (other than &lt;code&gt;count(*)&lt;/code&gt; taking
over 10 minutes on xoxo-4).&lt;/p&gt;
&lt;p&gt;In theory the hardest, slowest part was done: the 88GB database was ready for
cutover whenever we were.&lt;/p&gt;
&lt;h2 id=&quot;not-zero-downtime-but-i-remain-chuffed&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://sixohthree.com/blog/2025/migrating-xoxo-zone-to-ovhcloud/#not-zero-downtime-but-i-remain-chuffed&quot;&gt;Not zero-downtime but I remain chuffed&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I was emboldened by successful replication and from listening to Eurovision
playlists at high volume for the previous hour. After some encouraging words
like &amp;quot;why not&amp;quot; and &amp;quot;if you fuck this up maybe i can focus on work,&amp;quot; I finalised
a migration plan and kicked things off.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://sixohthree.com/media/2025/04/focus.png&quot; alt=&quot;Screenshot from Discord, Clarity says: thinking emoji, if it goes wrong and the server crashes that&#39;ll probably just help me focus on work better&quot;&gt;&lt;/p&gt;
&lt;p&gt;I had done a lot of work already at this point:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I had run the &lt;a href=&quot;https://docs.joinmastodon.org/admin/install/&quot;&gt;server setup guide&lt;/a&gt; and finished an initial rsync on some
key directories, including the nginx config&lt;/li&gt;
&lt;li&gt;A bunch of server config, including Mastodon service files, backup
configuration, and crons are in an Ansible playbook, which I had already run&lt;/li&gt;
&lt;li&gt;The domain TTL was already ramped down to 60 seconds&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Winding down xoxo-4 looked like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mastodon-bounce stop all     # wrapper script that runs systemctl on all mastodon services
mastodon-bounce disable all
systemctl disable --now redis-server.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bringing things back up on xoxo-5 looked like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/root/sync.sh # /home/mastodon/live, /var/lib/redis, /etc/letsencrypt, /etc/nginx
pg_ctlcluster 17 main promote
systemctl enable --now redis-server.service
mastodon-bounce start all
mastodon-bounce enable all
RAILS_ENV=production ./bin/tootctl feeds build
RAILS_ENV=production ./bin/tootctl search deploy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I would also run a few SQL commands to check data consistency:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo -u postgres psql -c &#39;&#92;x&#39; -c &#39;select * from pg_stat_replication&#39;
time sudo -u mastodon psql mastodon_production -c &amp;quot;select count(1) from statuses&amp;quot;
time sudo -u mastodon psql mastodon_production -c &amp;quot;select id, created_at from statuses order by created_at desc limit 10&amp;quot;
time sudo -u mastodon psql mastodon_production -c &amp;quot;select count(1) from accounts&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;job&#39;s-done&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://sixohthree.com/blog/2025/migrating-xoxo-zone-to-ovhcloud/#job&#39;s-done&quot;&gt;Job&#39;s done&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The migration took about 15 minutes from start to finish. Next time, with SSD
hosts on both sides, I could probably get it down to seconds or maybe even zero
downtime.&lt;/p&gt;
&lt;p&gt;I did hit one snag that was obfuscated by caching in the Mastodon service
worker: the mastodon account&#39;s home directory was created with permissions
&lt;code&gt;0750&lt;/code&gt;, and Nginx could not read files in the web directory, causing a lot of
busted client pages for about 30 minutes after I &amp;quot;finished&amp;quot; the migration.
There&#39;s always something.&lt;/p&gt;
&lt;p&gt;But still! It&#39;s done and I&#39;m happy with how it went.&lt;/p&gt;
&lt;p&gt;Let&#39;s not do it again for a long time.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>XOXO 2024</title>
    <link href="https://sixohthree.com/blog/2024/xoxo-2024/" />
    <updated>2024-08-27T00:00:00Z</updated>
    <id>https://sixohthree.com/blog/2024/xoxo-2024/</id>
    <content type="html">&lt;p&gt;&lt;img src=&quot;https://sixohthree.com/media/2024/08/badges.jpg&quot; alt=&quot;Five XOXO Festival badges&quot;&gt; &lt;em&gt;Badge lineup: 2015, 2016, 2018, 2019, 2024&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The final XOXO Festival wrapped up this weekend in Portland, Oregon. It was my
fifth XOXO, and it&#39;s been 5 years since the previous festival, XOXO 2019.&lt;/p&gt;
&lt;p&gt;Five years is a long time. Maybe long enough that, like me, you see a change in
yourself. Since 2019, I&#39;m a little frayed around the edges, more cautious, more
reserved in the face of an unfriendly world.&lt;/p&gt;
&lt;p&gt;I have allowed myself to harden in the past five years. I saw it happening and I
fucking leaned in. It was a learned response to a hostile environment. The
timeline of this blog is a reflection of that change: withdrawal, retreat, a
reluctance to engage.&lt;/p&gt;
&lt;p&gt;XOXO is an event, yes. It&#39;s a particular point in time with a beginning and an
end. But XOXO is also a feeling of curiosity, a sense of wonder, a rejection of
cynicism, a community. A reminder. &lt;a href=&quot;https://xoxo.zone/@jkent/113036275692117626&quot;&gt;A dream&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;XOXO is made of people. This weekend, those people reminded me what it&#39;s like to
allow myself to feel joy and hope. I saw a lot of old friends and maybe made
some new ones. I heard their stories and felt their excitement. Hibernating
parts of me woke up, did a little stretch, and thought maybe it&#39;s time to leave
the cave.&lt;/p&gt;
&lt;p&gt;It&#39;s easy to harden up. &lt;a href=&quot;https://www.youtube.com/watch?v=cNwy1Th4NYo&quot;&gt;Only natural&lt;/a&gt;, as I heard through &lt;a href=&quot;https://aratin.gay/&quot;&gt;a friend&lt;/a&gt;.
Going soft again is not so easy. All these great people make me want to at least
try.&lt;/p&gt;
&lt;p&gt;There will not be another XOXO Festival, but there are many people who embody
its spirit, whether they associate it with the festival or not. I will try to
remember that without the festival&#39;s periodic reminder.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://sixohthree.com/media/2024/08/tent.jpg&quot; alt=&quot;People at night on a lawn under strings of lights, in front of a large white tent&quot;&gt;
&lt;em&gt;The traditional &amp;quot;leaving XOXO&amp;quot; shot, 2024&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;collected-xoxo-2024-posts&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://sixohthree.com/blog/2024/xoxo-2024/#collected-xoxo-2024-posts&quot;&gt;Collected XOXO 2024 Posts&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.spideyj.com/the-final-xoxo/&quot;&gt;The Final XOXO&lt;/a&gt; by Kit Jones
(SpideyJ)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hey.georgie.nu/post-xoxo/&quot;&gt;some thoughts (but not enough thoughts), post-XOXO 2024&lt;/a&gt;
by Georgie&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://buttondown.com/hupfen/archive/this-will-all-end-in-tears/&quot;&gt;This Will All End in Tears&lt;/a&gt;
by Zoe Landon&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lyonheart.us/xoxo-2024/&quot;&gt;My experience of XOXO 2024&lt;/a&gt; by Matthew Lyon&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://brookshelley.com/posts/2024-08-27-xoxo/&quot;&gt;The Last XOXO: 2024&lt;/a&gt; by
Brook Shelley&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kottke.org/24/08/thanks-xoxo&quot;&gt;Thanks, XOXO&lt;/a&gt; by Jason Kottke&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://phildini.dev/xoxo-2024&quot;&gt;XOXO 2024&lt;/a&gt; by Phil Dini&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bamoon.com/my-first-year-with-xoxo/&quot;&gt;My first year with XOXO&lt;/a&gt; by
Brian Moon&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://justinpot.com/the-internet-doesnt-have-to-feel-like-this/&quot;&gt;The internet doesn’t have to feel like this&lt;/a&gt;
by Justin Pot&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://netninja.com/2024/08/29/lowering-expectations-one-project-at-a-time/&quot;&gt;Lowering Expectations, One Project at a Time&lt;/a&gt;
by BrianEnigma&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://geekdad.com/2024/08/creating-community-at-xoxo/&quot;&gt;Creating Community at XOXO&lt;/a&gt;
by Jonathan Liu&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jrubenoff.com/writing/xoxo-festival/&quot;&gt;XOXO taught me it was OK to be weird&lt;/a&gt;
by Josh Rubenoff&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://benjaminchait.net/archives/xoxo-2024&quot;&gt;XOXO 2024&lt;/a&gt; by Benjamin Chait&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://emorydunn.com/blog/2024/09/xoxo/&quot;&gt;XOXO&lt;/a&gt; by Emory Dunn&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://open.substack.com/pub/byvishmili/p/spotted-a-deep-dive-into-the-digital&quot;&gt;Spotted: A deep dive into the digital jungle, where the survival of the fittest isn’t about brawn but brains—and a little (a lot of) kindness, too&lt;/a&gt;
by Višnja Milidragović&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jwithy.weblog.lol/2024/09/it-has-been-a-time&quot;&gt;It has been a time&lt;/a&gt; by
Jim Withington&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.mmmx.cloud/after-xoxo&quot;&gt;After xoxo&lt;/a&gt; by Ním Daghlian&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://clarity.flowers/journal/goodbye_xoxo.html&quot;&gt;goodbye, xoxo&lt;/a&gt; by clarity
flowers&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.rawsignal.ca/newsletter-archive/lower-your-expectations&quot;&gt;Lower your expectations&lt;/a&gt;
by Johnathan and Melissa Nightingale&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arun.is/blog/farewell-xoxo/&quot;&gt;Farewell, XOXO&lt;/a&gt; by Arun Venkatesan&lt;/li&gt;
&lt;li&gt;Photo album:
&lt;a href=&quot;https://www.flickr.com/photos/linkletter/albums/72177720320167689/&quot;&gt;XOXO Festival 2024&lt;/a&gt;
by Ian Linkletter&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
</feed>