<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/assets/atom.xsl" media="screen"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Lark Space (#tech)</title> <subtitle>Lark&#x27;s blog and personal site</subtitle> <icon>https://lark.gay/processed_images/icon-padded.0eab696fef3b3f14.png</icon>
  <link rel="self" type="application/atom+xml"
    href="https://lark.gay/tags/tech/feed.xml" />
  <link rel="alternate" type="text/html"
    href="https://lark.gay" />
  <generator
    uri="https://www.getzola.org/">Zola</generator>
  <updated>2025-09-26T00:00:00+00:00</updated>
  <id>https://lark.gay/tags/tech/feed.xml</id> <author>
    <name>Lark Aster</name>
  </author> <entry
    xml:lang="en">
    <title>FanJam: A free event planning app for small cons
</title>
    <published>2025-09-26T00:00:00+00:00</published>
    <updated>2025-09-26T00:00:00+00:00</updated>  <author>
      <name>Lark Aster</name>
    </author>
    <category term="tech"
      label="tech"
      scheme="https://lark.gay/tags/" />  <category term="community"
      label="community"
      scheme="https://lark.gay/tags/" />  <link rel="alternate" type="text/html" href="https://lark.gay/log/fanjam-mvp/" />
    <id>urn:uuid:5a370c99-1a28-4980-a9bb-6db9f674dc75</id>  <content type="html" xml:base="https://lark.gay/log/fanjam-mvp/">&lt;p&gt;A friend approached me with the idea back in the Spring. They were frustrated
because the small con they had recently attended didn&#x27;t have a good way of
viewing the event schedule. All they had was a public link to a spreadsheet on
Google Docs. This friend had been playing around with Airtable and similar apps,
and decided to copy the con schedule into &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;nocodb.com&#x2F;&quot;&gt;NocoDB&lt;&#x2F;a&gt; so they
could have something more readable.&lt;&#x2F;p&gt;
&lt;p&gt;The pitch was, &quot;What if you built a mobile app for attendees that was just a
frontend for NocoDB&quot;? That would save a &lt;em&gt;ton&lt;&#x2F;em&gt; of work over trying to build out a
dashboard-like app for organizers. We&#x27;d just have to build the mobile app.&lt;&#x2F;p&gt;
&lt;p&gt;It turned out to be a lot of work anyways.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;fanjam.live&quot;&gt;FanJam&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;justlark&#x2F;fanjam&quot;&gt;GitHub&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The mobile app turned out to be the easy part! The hardest part was figuring out
how to make this app free for small cons. If I was planning on eating the
hosting costs, I would need it to be cheap to host. Like, &lt;em&gt;real&lt;&#x2F;em&gt; cheap. My goal
was a dozen cons and a couple thousand concurrent users for less than $40 USD
per month. Yikes.&lt;&#x2F;p&gt;
&lt;p&gt;There are a couple factors that make this more reasonable than you might think:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Cons generally only last a few days, and generally don&#x27;t all happen the same
weekend, so traffic is pretty evenly distributed, and resources can be scaled
up to accommodate burst traffic without exceeding the budget.&lt;&#x2F;li&gt;
&lt;li&gt;Planning the schedule for a given con isn&#x27;t generally a 24&#x2F;7&#x2F;365 affair, so I
can automatically suspend those servers when they&#x27;re not being used to save
money.&lt;&#x2F;li&gt;
&lt;li&gt;The data set is tiny, which means I can keep a full local copy on the user&#x27;s
device. This means no loading time in the app and less frequent requests to
the server.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;But there were some challenges to overcome as well, namely that NocoDB is quite
slow. There&#x27;s no way I can rely on it to absorb significant traffic, which means
I needed to build a caching layer in front of it.&lt;&#x2F;p&gt;
&lt;p&gt;I might share more of the technical details in future log entries. There&#x27;s
documentation on the architecture of the app in the GitHub repo, including
diagrams.&lt;&#x2F;p&gt;
&lt;p&gt;When I show people FanJam, they&#x27;ll often pull out their phone and show me the
app from a con they&#x27;ve attended, usually to complain about it. It turns out
there are a &lt;em&gt;lot&lt;&#x2F;em&gt; of bad apps out there trying to do what FanJam does. If I can
build something that does it better and cheaper, I think there might actually be
a market out there for me.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m really excited about this project. I have a long backlog of new features and
enhancements I still want to implement, but in the coming weeks I&#x27;m going to
start soliciting feedback from more than just a few friends, and maybe even
start approaching some cons.&lt;&#x2F;p&gt;
&lt;p&gt;I imagine the hardest part will be convincing them it&#x27;s not a scam lol.&lt;&#x2F;p&gt;
</content> <rights type="text">Copyright
  © Lark Aster
  2025 CC BY-NC 4.0</rights>
  </entry> <entry
    xml:lang="en">
    <title>Self-hosting a Matrix homeserver
</title>
    <published>2025-03-24T00:00:00+00:00</published>
    <updated>2025-03-28T00:00:00+00:00</updated>  <author>
      <name>Lark Aster</name>
    </author>
    <category term="tech"
      label="tech"
      scheme="https://lark.gay/tags/" />  <link rel="alternate" type="text/html" href="https://lark.gay/log/matrix-homeserver/" />
    <id>urn:uuid:d78bedd1-e974-43aa-9ba3-65d3ee6b1b88</id>  <content type="html" xml:base="https://lark.gay/log/matrix-homeserver/">&lt;h2 id=&quot;what-is-matrix&quot;&gt;What is Matrix?&lt;&#x2F;h2&gt;
&lt;p&gt;If you&#x27;re not familiar with &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;&quot;&gt;Matrix&lt;&#x2F;a&gt;, it&#x27;s an open-source,
decentralized, end-to-end encrypted chat platform. It&#x27;s somewhat like Discord,
with direct messages, &lt;em&gt;rooms&lt;&#x2F;em&gt; (comparable to channels or group chats in
Discord), and &lt;em&gt;spaces&lt;&#x2F;em&gt; (comparable to Discord servers).&lt;&#x2F;p&gt;
&lt;p&gt;Matrix doesn&#x27;t quite have feature parity with Discord, but it has pretty much
all the features you expect of a chat app. I think the platform needs to mature
before it&#x27;s ready for mainstream adoption, but I&#x27;m holding out hope that someday
it will be viable enough for Discord-based communities to feel enticed to switch
over.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-the-appeal&quot;&gt;What&#x27;s the appeal?&lt;&#x2F;h2&gt;
&lt;p&gt;With Discord almost certainly approaching an IPO, I fear that the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Enshittification&quot;&gt;enshittification&lt;&#x2F;a&gt; of the
platform is imminent. Paired with the wave of public awareness of decentralized
platforms like Mastodon, Lemmy, Pixelfed, and Peertube that we&#x27;ve seen in recent
months, it feels like a perfect storm for Matrix to start seeing more adoption.&lt;&#x2F;p&gt;
&lt;p&gt;I worry about how deeply invested I am in Discord. There&#x27;s little stopping the
platform from implementing user-hostile changes, suspending my account without
recourse, spying on me, getting hacked, or disappearing entirely. I believe very
strongly in the power of federated, decentralized networks, and I think they&#x27;re
the only path forward for a free and open internet. Matrix being end-to-end
encrypted is huge&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;; it protects privacy and security in a way that Discord
fundamentally cannot.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s encouraging to see that there&#x27;s already a healthy diversity of
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;ecosystem&#x2F;clients&#x2F;&quot;&gt;client&lt;&#x2F;a&gt; and
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;ecosystem&#x2F;servers&#x2F;&quot;&gt;server&lt;&#x2F;a&gt; implementations, even if most of
them are not yet feature-complete. I would have less confidence in the strength
of the community and ecosystem if it consisted of only a single reference
implementation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;self-hosting-a-matrix-homeserver&quot;&gt;Self-hosting a Matrix homeserver&lt;&#x2F;h2&gt;
&lt;p&gt;Matrix has a concept of a &lt;em&gt;homeserver&lt;&#x2F;em&gt;, which is the server that hosts your user
account and stores your (encrypted) messages. The domain of your homeserver
forms part of your handle, which takes the form &lt;code&gt;@username:example.com&lt;&#x2F;code&gt;. From
the start, I was obsessed with the idea of having a &lt;code&gt;lark.gay&lt;&#x2F;code&gt; handle. This does
require hosting your own homeserver, but that also appeals to me because I want
ownership and sovereignty over my own data. The only way to protect your data
from disappearing someday is to host it yourself.&lt;&#x2F;p&gt;
&lt;p&gt;After assessing the landscape of server implementations, I settled on one called
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;conduwuit.puppyirl.gay&#x2F;&quot;&gt;conduwuit&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The only fully feature-complete and stable server implementation available right
now is &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;element-hq&#x2F;synapse&quot;&gt;Synapse&lt;&#x2F;a&gt;, however my
understanding is that it&#x27;s mired in performance issues and is intended to
eventually be phased out by second-gen implementations like
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;element-hq&#x2F;dendrite&quot;&gt;Dendrite&lt;&#x2F;a&gt;. I chose conduwuit over
Dendrite because I have a lot more confidence in its maintainership; it seems to
be seeing incredibly active development, whereas Dendrite&#x27;s commit history
indicates it has been in development for approximately &lt;strong&gt;eight years&lt;&#x2F;strong&gt;. I also
happen to love using software made by trans people 🏳️‍⚧️.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-devlog&quot;&gt;The devlog&lt;&#x2F;h2&gt;
&lt;p&gt;Self-hosting a Matrix homeserver turned out to be somewhat more complicated than
I would have hoped, even for an implementation that&#x27;s meant to be easy to set
up. This section is a technical devlog detailed how I did it. Before we dive in,
I want to stress that &lt;strong&gt;you do not need to do any of this to use Matrix&lt;&#x2F;strong&gt;.
Unless you &lt;em&gt;really&lt;&#x2F;em&gt; know that you want this, I would strongly recommend you
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;servers.joinmatrix.org&#x2F;&quot;&gt;join an existing homeserver&lt;&#x2F;a&gt; instead.
Registering is very easy.&lt;&#x2F;p&gt;
&lt;p&gt;conduwuit itself is just a statically linked binary with the database embedded,
which is refreshingly simple. I just dropped the binary in &lt;code&gt;&#x2F;usr&#x2F;local&#x2F;bin&lt;&#x2F;code&gt;,
created a system user to run it, and used one of the provided &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;conduwuit.puppyirl.gay&#x2F;configuration&#x2F;examples.html&quot;&gt;systemd unit
files&lt;&#x2F;a&gt;--which are
configured to provide a reasonable degree of sandboxing--to run it.&lt;&#x2F;p&gt;
&lt;p&gt;Here are the relevant sections from my conduwuit config:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;[global]

server_name = &amp;quot;lark.gay&amp;quot;
port = 6167
database_path = &amp;quot;&amp;#x2F;var&amp;#x2F;lib&amp;#x2F;conduwuit&amp;quot;

# I had to set this to true temporarily to register my user account. Because I
# don&amp;#x27;t plan on sharing this server with anyone else, I set it back to false
# immediately after.
allow_registration = false

# In order to set up my user account, I had to generate a random token. After
# disabling registration, there&amp;#x27;s no need for one.
#registration_token = &amp;quot;&amp;quot;

[global.well_known]

# I&amp;#x27;ll explain this later.
client = &amp;quot;https:&amp;#x2F;&amp;#x2F;matrix.lark.gay&amp;quot;
server = &amp;quot;matrix.lark.gay:8448&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As recommended in the documentation, I set up &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;caddyserver.com&#x2F;&quot;&gt;Caddy&lt;&#x2F;a&gt;
as a reverse proxy, which automatically handles acquiring and renewing TLS
certificates. The config is remarkably simple:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;matrix.lark.gay, matrix.lark.gay:8448 {
    reverse_proxy 127.0.0.1:6167
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I had to open up port 8448 (the default port for the Matrix federation API) on
my firewall, in addition to 443 (HTTPS) and 80 (HTTP). Port 80 is necessary for
Caddy to acquire TLS certificates. Because my server is running Fedora Atomic,
which has SELinux enabled, I had to run the following commands to allow Caddy to
bind the port.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;shell&quot; class=&quot;language-shell &quot;&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;sudo semanage port -a -t http_port_t -p tcp 8448
sudo semanage port -a -t http_port_t -p udp 8448
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Some of the complexity setting this up comes from the fact that I &lt;em&gt;really&lt;&#x2F;em&gt;
wanted my homeserver&#x27;s domain to be &lt;code&gt;lark.gay&lt;&#x2F;code&gt;, which will obviously need to be
shared with this site. To accomplish this, Matrix has a feature called
&lt;em&gt;delegation&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;To implement Matrix server delegation, I have to serve these two files from this
site, which tell Matrix clients and other servers that they can &lt;em&gt;actually&lt;&#x2F;em&gt; find
my homeserver at &lt;code&gt;matrix.lark.gay&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;&#x2F;.well-known&#x2F;matrix&#x2F;server&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;{
  &amp;quot;m.server&amp;quot;: &amp;quot;matrix.lark.gay:8448&amp;quot;
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;&#x2F;.well-known&#x2F;matrix&#x2F;client&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;{
  &amp;quot;m.homeserver&amp;quot;: {
    &amp;quot;base_url&amp;quot;: &amp;quot;https:&amp;#x2F;&amp;#x2F;matrix.lark.gay&amp;quot;
  }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For Matrix server delegation to work, you also need to proxy traffic to
&lt;code&gt;&#x2F;_matrix&#x2F;*&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;_conduwuit&#x2F;*&lt;&#x2F;code&gt; to your homeserver. However, &lt;code&gt;lark.gay&lt;&#x2F;code&gt; doesn&#x27;t
point to a web server that I can configure to do that; it points directly to a
CDN. However, Cloudflare &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developers.cloudflare.com&#x2F;pages&#x2F;functions&#x2F;advanced-mode&#x2F;&quot;&gt;has a
feature&lt;&#x2F;a&gt; that
lets me put a serverless function in front of the CDN, which you can see
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;justlark&#x2F;lark.gay&#x2F;blob&#x2F;main&#x2F;static&#x2F;_worker.js&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I believe that about covers it! Not too complex in hindsight, but it did take me
some time to figure out, particularly troubleshooting some of the more subtle
bits, like SELinux.&lt;&#x2F;p&gt;
&lt;p&gt;A fun anecdote: I started the process of setting this all up while I was bored
on a flight, connecting to the server over SSH. At one point I needed to reboot,
and committed the classic blunder of locking myself out of SSH access. The
server has full-disk encryption configured with a password, meaning that I need
to be &lt;em&gt;physically present&lt;&#x2F;em&gt; to enter the password so it can boot. After that
ordeal, I decided to configure LUKS to use the TPM instead, which allows it to
boot unattended. I also flipped the switch in the UEFI firmware settings setting
it to automatically power back on if it loses and then regains power--say if the
electricity goes out.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m quite pleased with my progress, and excited to start using Matrix… once I
actually have friends to talk to 😅. All my friends are still on Discord, and it
might take some convincing to get them to register an account and install a new
chat app just for me. Thankfully I have some nerd friends who I&#x27;m sure will be
happy to indulge me.&lt;&#x2F;p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;Soatok, a security expert, has raised some &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;soatok.blog&#x2F;2024&#x2F;08&#x2F;14&#x2F;security-issues-in-matrixs-olm-library&#x2F;&quot;&gt;very serious
criticisms&lt;&#x2F;a&gt;
of the actual security of the end-to-end encryption implementation in
several Matrix clients. If your privacy or freedom are at risk, Signal is
probably the better choice. &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</content> <rights type="text">Copyright
  © Lark Aster
  2025 CC BY-NC 4.0</rights>
  </entry> <entry
    xml:lang="en">
    <title>Switching to Jujutsu: A git-compatible VCS
</title>
    <published>2025-02-22T00:00:00+00:00</published>
    <updated>2025-02-22T00:00:00+00:00</updated>  <author>
      <name>Lark Aster</name>
    </author>
    <category term="tech"
      label="tech"
      scheme="https://lark.gay/tags/" />  <link rel="alternate" type="text/html" href="https://lark.gay/log/jujutsu/" />
    <id>urn:uuid:c11df468-1dda-4cec-a4a5-fb93c34f9458</id>  <content type="html" xml:base="https://lark.gay/log/jujutsu/">&lt;p&gt;I&#x27;ve been curious about non-git version control systems like
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.mercurial-scm.org&#x2F;&quot;&gt;Mercurial&lt;&#x2F;a&gt;,
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;fossil-scm.org&#x2F;home&#x2F;doc&#x2F;trunk&#x2F;www&#x2F;index.wiki&quot;&gt;Fossil&lt;&#x2F;a&gt;, and
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;pijul.org&#x2F;&quot;&gt;Pijul&lt;&#x2F;a&gt; for a while, but switching away from git is
difficult given its ubiquity, particularly considering the other VCSes have next
to no support among the major hosting platforms.&lt;&#x2F;p&gt;
&lt;p&gt;However, a friend recently clued me in to a relatively new VCS that&#x27;s
repo-compatible with git. It&#x27;s called &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jj-vcs&#x2F;jj&quot;&gt;Jujutsu&lt;&#x2F;a&gt;.
You can use Jujutsu with git forges like GitHub, and you can collaborate with
people using git--without them even knowing!&lt;&#x2F;p&gt;
&lt;p&gt;After playing around with it for a few weeks, it&#x27;s almost completely supplanted
git for me; I don&#x27;t foresee myself going back. The CLI is much more sensible--a
famous shortcoming of git--but it&#x27;s more than just another git frontend. Its
data model makes rewriting history, rebasing, and conflict resolution &lt;em&gt;much&lt;&#x2F;em&gt;
less painful. It took some adjustment and unlearning of git paradigms, but after
just a few weeks I&#x27;m already more confident navigating complex situations in
Jujutsu than I am after 10 years of working with git.&lt;&#x2F;p&gt;
&lt;p&gt;My one gripe with Jujutsu is the lack of git LFS support; using Jujutsu with
LFS-enabled repos will implicitly check in those files, which is annoying.&lt;&#x2F;p&gt;
&lt;p&gt;A friend and I collaborated to write a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;justinpombrio.net&#x2F;src&#x2F;jj-cheat-sheet.pdf&quot;&gt;quick reference and cheat
sheet&lt;&#x2F;a&gt; for Jujutsu. You can
find his blog post
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;justinpombrio.net&#x2F;2025&#x2F;02&#x2F;11&#x2F;jj-cheat-sheet.html&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I highly recommend you give it a try.&lt;&#x2F;p&gt;
</content> <rights type="text">Copyright
  © Lark Aster
  2025 CC BY-NC 4.0</rights>
  </entry> <entry
    xml:lang="en">
    <title>Migrating my photo library to Immich
</title>
    <published>2024-11-20T00:00:00+00:00</published>
    <updated>2024-11-20T00:00:00+00:00</updated>  <author>
      <name>Lark Aster</name>
    </author>
    <category term="tech"
      label="tech"
      scheme="https://lark.gay/tags/" />  <link rel="alternate" type="text/html" href="https://lark.gay/log/photoprism-to-immich/" />
    <id>urn:uuid:30d66200-1969-4fac-91e8-f5270ceec604</id>  <content type="html" xml:base="https://lark.gay/log/photoprism-to-immich/">&lt;p&gt;A few years ago I undertook a project to de-Google my life, as much as possible.
There are a few Google services I still use--I haven&#x27;t been able to divorce
myself from Google Calendar just yet--but overall the endeavor has been fairly
successful!&lt;&#x2F;p&gt;
&lt;p&gt;Beyond the obvious reason to move away from Google services--privacy--having
custody over my own data is important to me. I&#x27;ve heard too many stories of
people having their Google accounts suspended due to a false-positive in
Google&#x27;s abuse detection systems, often without recourse.&lt;&#x2F;p&gt;
&lt;p&gt;One of the major Google services I wanted to move away from is Google Photos.
It&#x27;s admittedly a good product, but I wanted something open-source and
self-hosted with reasonable feature parity. I eventually settled on
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.photoprism.app&#x2F;&quot;&gt;PhotoPrism&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;PhotoPrism replicates some of Google Photos&#x27; most useful features, namely face
and object detection. It also adds some new ones, like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Screenshots, memes, and other non-photographic images are automatically
partitioned off into a separate folder. This is really nice for allowing me to
back up everything without cluttering my photos timeline with memes.&lt;&#x2F;li&gt;
&lt;li&gt;Nearly-identical photos (usually photos that were taken in burst mode or in
quick succession) are stacked together, so they&#x27;re still individually
accessible but don&#x27;t clutter up your library.&lt;&#x2F;li&gt;
&lt;li&gt;You can mark some photos as private so they&#x27;re hidden from your library by
default. This makes handing your phone to someone to scroll through your
photos much less anxiety-provoking.&lt;&#x2F;li&gt;
&lt;li&gt;You can configure it to automatically detect NSFW photos and mark them as
private.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Unfortunately though, the PhotoPrism UX isn&#x27;t great. It&#x27;s slow, there&#x27;s no
native app for mobile (only a PWA), and it relies on a clunky third-party app to
sync photos from your phone.&lt;&#x2F;p&gt;
&lt;p&gt;A friend recently introduced me to &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;immich.app&#x2F;&quot;&gt;Immich&lt;&#x2F;a&gt;, and I wanted
to give it a try. The UI is much faster than PhotoPrism and more touch-friendly
on mobile, comparable to Google Photos. It doesn&#x27;t have private photos, photo
stacking, or non-photographic image detection like PhotoPrism, but it does have
a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;immich.app&#x2F;roadmap&#x2F;&quot;&gt;promising roadmap&lt;&#x2F;a&gt; which includes at least some
of those features.&lt;&#x2F;p&gt;
&lt;p&gt;Migrating my photo library over from PhotoPrism to Immich wasn&#x27;t terribly
difficult. I found &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;v411e&#x2F;ppim-migrator&quot;&gt;this script&lt;&#x2F;a&gt; for
migrating albums and favorites, which worked reasonably well. The most
challenging part was that my ISP only offers paltry upload speeds.&lt;&#x2F;p&gt;
&lt;p&gt;I really love just how much high-quality open-source software there is out there
going toe-to-toe with proprietary services from major tech companies. If you&#x27;re
interested in trying Immich yourself, but find self-hosting intimidating, I
recommend checking out &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.pikapods.com&#x2F;&quot;&gt;PikaPods&lt;&#x2F;a&gt;. They offer managed
hosting for a bunch of FOSS apps, and they make it pretty simple.&lt;&#x2F;p&gt;
</content> <rights type="text">Copyright
  © Lark Aster
  2024 CC BY-NC 4.0</rights>
  </entry> <entry
    xml:lang="en">
    <title>Web feeds: An antidote to doomscrolling
</title>
    <published>2024-10-17T00:00:00+00:00</published>
    <updated>2024-10-17T00:00:00+00:00</updated>  <author>
      <name>Lark Aster</name>
    </author>
    <category term="tech"
      label="tech"
      scheme="https://lark.gay/tags/" />  <category term="smallweb"
      label="smallweb"
      scheme="https://lark.gay/tags/" />  <link rel="alternate" type="text/html" href="https://lark.gay/log/about-web-feeds/" />
    <id>urn:uuid:c7ca24b6-47c3-41aa-8b92-88539fffa13a</id>  <content type="html" xml:base="https://lark.gay/log/about-web-feeds/">&lt;p&gt;Web feeds, commonly known as RSS feeds&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, are a mechanism for subscribing to
news sites, blogs, podcasts, social media posts, and really anything else on the
web.&lt;&#x2F;p&gt;
&lt;p&gt;Most blogging engines automatically generate a web feed. Most news sites have
web feeds. Social media posts on &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;joinmastodon.org&#x2F;&quot;&gt;Mastodon&lt;&#x2F;a&gt; have web
feeds. The US government has a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.govinfo.gov&#x2F;feeds&quot;&gt;collection of
feeds&lt;&#x2F;a&gt; for everything from congressional bills to
court decisions.&lt;&#x2F;p&gt;
&lt;p&gt;You install an app on your phone or computer, pick which feeds you want to
subscribe to, and they appear in your own personal timeline.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s a simple system that has incredible power for putting you back in control
of your mental health.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re used to massive machine learning models curating our feeds for us,
deciding what media we consume. And this concept is sold to us under the premise
that these feeds are &quot;personalized&quot;--relevant to our interests.&lt;&#x2F;p&gt;
&lt;p&gt;What we often forget is the incentives at play. Companies make money when we
engage with their platforms, and so they are incentivized to monopolize our
attention by any means necessary. This causes tragic, inflammatory, and
polarizing content to bubble up to the top, which affects not just our health as
individuals, but the cohesion and strength of our communities.&lt;&#x2F;p&gt;
&lt;p&gt;Shortly after coming out as trans, my news feed was inundated with transphobic
content, assaulting me with news of oppressive policy decisions and violence
around the country and around the world. This was happening during a vulnerable
period in my transition, when I was trying to figure out what my future would
look like. And it gave me anxiety about the world I was coming into.&lt;&#x2F;p&gt;
&lt;p&gt;This prompted me to step away from algorithmic news feeds and focus on the news
I actually want to consume--the news that isn&#x27;t detrimental to my health--and
web feeds were the solution to that.&lt;&#x2F;p&gt;
&lt;p&gt;Feed readers let you subscribe to the content you want to see. Many let you set
up custom feeds aggregating multiple news sources. Some even let you set up
keyword filters to block out content you don&#x27;t want to see or notify you for
content you do. There are a number of open-source and proprietary feed readers
for desktop and mobile.&lt;&#x2F;p&gt;
&lt;p&gt;Seeing exactly the content you&#x27;ve subscribed to shouldn&#x27;t be a radical concept.
Web feeds are an open standard; anyone can publish them, and anyone can
subscribe to them, and corporations can&#x27;t take that away from you.&lt;&#x2F;p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;Technically, RSS is one of several web feed formats, but it&#x27;s been
genericized as a term for web feeds generally. This site has an
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Atom_(web_standard)&quot;&gt;Atom&lt;&#x2F;a&gt; feed instead,
which is a more modern alternative. If you&#x27;re making a website, you should
probably use Atom instead of RSS. &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</content> <rights type="text">Copyright
  © Lark Aster
  2024 CC BY-NC 4.0</rights>
  </entry> <entry
    xml:lang="en">
    <title>The Gemini protocol: Bringing back the old web
</title>
    <published>2024-10-15T00:00:00+00:00</published>
    <updated>2024-10-15T00:00:00+00:00</updated>  <author>
      <name>Lark Aster</name>
    </author>
    <category term="tech"
      label="tech"
      scheme="https://lark.gay/tags/" />  <category term="smallweb"
      label="smallweb"
      scheme="https://lark.gay/tags/" />  <link rel="alternate" type="text/html" href="https://lark.gay/log/gemini-intro/" />
    <id>urn:uuid:6127366a-d257-48cc-bc3a-039402eb2cb9</id>  <content type="html" xml:base="https://lark.gay/log/gemini-intro/">&lt;p&gt;&lt;em&gt;This has no relation to Google&#x27;s Gemini large language model.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;geminiprotocol.net&#x2F;&quot;&gt;The Gemini protocol&lt;&#x2F;a&gt; is a lightweight alternative
to the web. I&#x27;ll often jokingly refer to it as &quot;Web 0.5&quot;, playing on terms like
&quot;Web 2.0&quot; and &quot;Web 3.0&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;Over the past few decades, the web has grown into a behemoth--a massive, global
platform for building rich applications and networks that serve billions of
users. The smallweb movement presents an alternative: small websites and small
networks. Tight-knit communities of peers. A focus on content over presentation.&lt;&#x2F;p&gt;
&lt;p&gt;In its earliest iterations, the web was nothing more than a network of documents
that link to one another. Check out &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;info.cern.ch&#x2F;hypertext&#x2F;WWW&#x2F;TheProject.html&quot;&gt;the first ever
website&lt;&#x2F;a&gt; as an example. Over
time, the web has accumulated layers of complexity to enable the applications
it&#x27;s used for today. That complexity comes at a cost, however: ballooning
resource usage requiring ever-more powerful computers, threats to our privacy,
and the commodification of our attention.&lt;&#x2F;p&gt;
&lt;p&gt;Gemini is a return to the principles of the early web: a network of documents
that link to one another. It&#x27;s private, secure, and lightweight by
default--deliberately designed to head off any attempts to expand its scope or
complexity.&lt;&#x2F;p&gt;
&lt;p&gt;Instead of massive social media networks controlled by big tech monopolies,
Gemini is full of small blogs and personal sites hosted by individuals just for
the fun of it. If this appeals to you, I encourage you to give it a try. I
recommend downloading &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gmi.skyjake.fi&#x2F;lagrange&#x2F;&quot;&gt;Lagrange&lt;&#x2F;a&gt;, a browser
like Chrome or Firefox, but for Gemini.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re interested in hosting your own Gemini site, I built &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;justlark&#x2F;gempost&quot;&gt;a simple static
site generator&lt;&#x2F;a&gt; that you might find useful.&lt;&#x2F;p&gt;
&lt;p&gt;I used to host a Gemini site under this domain. I&#x27;ve taken a break from it, but
might return someday. Amid the growing enshittification of the web, alternatives
like Gemini are starting to look more and more appealing. They won&#x27;t replace the
web, but they don&#x27;t need to. Gemini is a small, quiet corner I can retreat to
when I need a break, and I&#x27;m grateful for it.&lt;&#x2F;p&gt;
</content> <rights type="text">Copyright
  © Lark Aster
  2024 CC BY-NC 4.0</rights>
  </entry> <entry
    xml:lang="en">
    <title>A case for typewriters
</title>
    <published>2024-10-14T00:00:00+00:00</published>
    <updated>2024-10-15T00:00:00+00:00</updated>  <author>
      <name>Lark Aster</name>
    </author>
    <category term="tech"
      label="tech"
      scheme="https://lark.gay/tags/" />  <link rel="alternate" type="text/html" href="https://lark.gay/log/my-typewriter/" />
    <id>urn:uuid:a1184477-288e-446f-8cf3-41b389c0742b</id>  <content type="html" xml:base="https://lark.gay/log/my-typewriter/">&lt;p&gt;A few months ago I bought myself a typewriter. Friends were somewhat baffled,
given that I work in tech, but I&#x27;ve found that I actually really enjoy using it.&lt;&#x2F;p&gt;
&lt;p&gt;Between my job and hobbies, I spend a &lt;em&gt;lot&lt;&#x2F;em&gt; of time in front of a screen.
Computers are attention-grabbing and pull me away from my physical environment;
I can spend hours working on a personal project and fully disconnect from the
world around me, distorting the passage of time.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t like this sensation of realizing I&#x27;ve been absorbed for hours, and
typewriters solve that for me. Using a typewriter is a very manual and
intentional process that I find grounding and meditative. I&#x27;m sitting down to
use it for a specific purpose, without any distractions to divert my attention.
The process of it--lifting the bail, inserting the paper, turning the platen,
setting margins--is a ritual I really enjoy.&lt;&#x2F;p&gt;
&lt;p&gt;I mostly use it to write letters; I enjoy exchanging letters with my friends. I
find them to be a more personal and thoughtful way to express my appreciation
for someone--it&#x27;s easier to be vulnerable the more asynchronous the
communication method is.&lt;&#x2F;p&gt;
&lt;p&gt;I also use it for taking notes occasionally. This one is situational, because I
need most of my notes in my notes app so I always have them available and can
search them, but some things I actually prefer to have on paper, like if I want
to pin them to the fridge to reference at home.&lt;&#x2F;p&gt;
&lt;p&gt;Often when writing notes or letters or stories, I get caught up fussing with
wording and structure, and that gets in the way of me actually getting my
thoughts out of my head. Because my typewriter makes editing more difficult, I&#x27;m
forced to just write, without being perfectly happy with what I&#x27;ve written. I
may go through multiple drafts, but there&#x27;s a practical limit to how much I can
revise.&lt;&#x2F;p&gt;
&lt;p&gt;For me, one of the major hurdles to doing creative work is accepting that &quot;good
enough&quot; is good enough. To that end, my typewriter has been quite helpful.&lt;&#x2F;p&gt;
</content> <rights type="text">Copyright
  © Lark Aster
  2024 CC BY-NC 4.0</rights>
  </entry> </feed>
