<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/static/feed.xslt" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>lnikki.la</title>
  <id>https://lnikki.la/feed.xml</id>
  <subtitle>Recent posts</subtitle>
  <updated>2025-05-02T19:25:36Z</updated>
  <link rel="self" href="https://lnikki.la/feed.xml" />
  <link href="https://lnikki.la" />
  <entry>
    <title>Use a global gitignore file, please!</title>
    <id>https://lnikki.la/posts/use-a-global-gitignore-file-please.html</id>
    <author>
      <name>Nix build user</name>
      <email>nixbld@localhost</email>
    </author>
    <updated>2025-04-11T00:00:00Z</updated>
    <link rel="alternate" href="https://lnikki.la/posts/use-a-global-gitignore-file-please.html" />
    <content type="html">&lt;p&gt; Here’s a quick tip that I find myself repeating, so I thought I’d write this up somewhere I can reliably link to. &lt;/p&gt; &lt;p&gt; Many Git repositories have a &lt;code&gt;.gitignore&lt;/code&gt; file at the root of the repository. Generally this is used to exclude build outputs from ending up in source control, however often these files also accumulate file and directory names related to various editors (e.g. &lt;code&gt;.vscode&lt;/code&gt;, &lt;code&gt;.idea&lt;/code&gt;) and operating system caches (&lt;code&gt;.DS_Store&lt;/code&gt;, &lt;code&gt;Thumbs.db&lt;/code&gt;). The more people work on a repository, the longer these files tend to get. &lt;/p&gt; &lt;p&gt; It doesn’t have to be this way. Git has a &lt;a href="https://git-scm.com/docs/gitignore"&gt;global gitignore file&lt;/a&gt; where you should ignore files that relate to &lt;i&gt;your&lt;/i&gt; tools. The default path of this file is &lt;code&gt;$XDG_CONFIG_HOME/git/ignore&lt;/code&gt;, and following the &lt;a href="https://specifications.freedesktop.org/basedir-spec/latest/#basics"&gt;XDG Base Directory Specification&lt;/a&gt;, this means the file is generally at &lt;code&gt;~/.config/git/ignore&lt;/code&gt;. You’ll need to create this file if it doesn’t exist. &lt;/p&gt; &lt;p&gt; To give you an example, currently my global gitignore file is: &lt;/p&gt; &lt;pre class="example"&gt; *~ .#* .DS_Store [Tt][Aa][Gg][Ss] /NOTES.org &lt;/pre&gt; &lt;p&gt; In this file, I’m excluding: &lt;/p&gt; &lt;ol class="org-ol"&gt; &lt;li&gt;common names of backup files&lt;/li&gt; &lt;li&gt;&lt;code&gt;.DS_Store&lt;/code&gt; in case I’ve rsynced something from macOS&lt;/li&gt; &lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Ctags"&gt;Ctags&lt;/a&gt;-like index files (case-insensitively)&lt;/li&gt; &lt;li&gt;the file I keep my notes related to a repository.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt; Using a global gitignore file, you free up &lt;code&gt;.gitignore&lt;/code&gt; files in source control to list only those files that are related to the project (e.g. build outputs). Even better, you’ll only need to configure the global file once, since it’ll apply to all repositories on your machine. &lt;/p&gt; &lt;p&gt; Should you need one, there’s also a repository-specific gitignore file that doesn’t get tracked in source control at &lt;code&gt;.git/info/exclude&lt;/code&gt;. &lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Using Lua on Nginx to fix Finder's WebDAV quirks</title>
    <id>https://lnikki.la/posts/using-lua-on-nginx-to-fix-finders-webdav-quirks.html</id>
    <author>
      <name>Nix build user</name>
      <email>nixbld@localhost</email>
    </author>
    <updated>2024-02-04T00:00:00Z</updated>
    <link rel="alternate" href="https://lnikki.la/posts/using-lua-on-nginx-to-fix-finders-webdav-quirks.html" />
    <content type="html">&lt;nav role="doc-toc" id="table-of-contents"&gt; &lt;h2&gt;Table of Contents&lt;/h2&gt; &lt;div role="doc-toc" id="text-table-of-contents"&gt; &lt;ul&gt; &lt;li&gt;&lt;a href="#orgdf24862"&gt;Nginx and Lua&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="#orgaadd4fe"&gt;Unicode normalisation forms&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="#org60a41fc"&gt;Unicode and file systems&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="#org14d05be"&gt;Writing the configuration in Lua&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="#org48b47f3"&gt;Using this yourself&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="#org54021f2"&gt;Bonus: Disabling Finder’s Quick Look thumbnail generation&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;/div&gt; &lt;/nav&gt; &lt;p&gt; This is a response to &lt;a href="https://www.rebeccapeck.org/2020/06/making-webdav-actually-work-on-nginx/"&gt;Rebecca Peck’s post about making WebDAV actually work on Nginx&lt;/a&gt; a few years back. Her post was incredibly helpful when I was moving my WebDAV server from lighttpd&lt;sup&gt;&lt;a role="doc-backlink" id="fnr.1" href="#fn.1" class="footref"&gt;1&lt;/a&gt;&lt;/sup&gt; to Nginx and trying to put this puzzle together on my own. I highly recommend you take a look at it first. &lt;/p&gt; &lt;p&gt; My short summary of the post is that Nginx’s &lt;code&gt;ngx_http_dav_module&lt;/code&gt; lacks support for parts of the WebDAV specification that Finder depends on, but the third-party &lt;code&gt;ngx_http_dav_ext_module&lt;/code&gt; adds support for those. Finder is also non-compliant and doesn’t follow the specification properly, which means you need to work around some quirks, like adding trailing slashes to all directory paths. &lt;/p&gt; &lt;p&gt; In her closing words, Rebecca mentions wanting to use Lua instead of various Nginx modules to jump through these hoops. I agreed; in particular I didn’t want to install the &lt;code&gt;ngx_headers_more&lt;/code&gt; module just to rewrite some request headers. &lt;/p&gt; &lt;p&gt; I got invested enough to roll all of this configuration into a set of Lua scripts that &lt;a href="#org48b47f3"&gt;you can install&lt;/a&gt; with relative ease on your own WebDAV server. &lt;/p&gt; &lt;p&gt; While working on this, I also discovered and fixed another Finder compatibility issue that Rebecca’s configuration didn’t address: Finder has trouble with certain kinds of Unicode paths. &lt;/p&gt; &lt;p&gt; But first, let’s look at how Nginx works with Lua, and what Unicode normalisation forms are. &lt;/p&gt; &lt;div id="outline-container-orgdf24862" class="outline-2"&gt; &lt;h2 id="orgdf24862"&gt;Nginx and Lua&lt;/h2&gt; &lt;div id="text-orgdf24862" class="outline-text-2"&gt; &lt;p&gt; Lua support for Nginx is provided by the &lt;code&gt;ngx_http_lua_module&lt;/code&gt; courtesy of the OpenResty project, who have their own Nginx distribution with additional Lua-based features. However, it’s entirely possible to use the module with the mainline version of Nginx as well, which is what I’m doing. &lt;/p&gt; &lt;p&gt; Loading the module is as simple as calling &lt;code&gt;load_module&lt;/code&gt;: &lt;/p&gt; &lt;div class="org-src-container"&gt; &lt;pre class="src src-conf"&gt;load_module /usr/lib/nginx/modules/ngx_http_lua_module.so; &lt;/pre&gt; &lt;/div&gt; &lt;p&gt; Setting the appropriate Lua load paths with &lt;code&gt;lua_package_path&lt;/code&gt; and &lt;code&gt;lua_package_cpath&lt;/code&gt; (for Lua files and compiled modules respectively) allows loading Lua libraries: &lt;/p&gt; &lt;div class="org-src-container"&gt; &lt;pre class="src src-conf"&gt;lua_package_path &lt;span style="font-style: italic;"&gt;'/usr/share/lua/5.1/?.lua;;'&lt;/span&gt;; lua_package_cpath &lt;span style="font-style: italic;"&gt;'/usr/lib/x86_64-linux-gnu/lua/5.1/?.so;;'&lt;/span&gt;; &lt;/pre&gt; &lt;/div&gt; &lt;p&gt; Once configured, you can call Lua code with the various configuration directives, e.g.: &lt;/p&gt; &lt;div class="org-src-container"&gt; &lt;pre class="src src-conf"&gt;&lt;span style="font-weight: bold; text-decoration: underline;"&gt;location /&lt;/span&gt; { &lt;span style="font-weight: bold; text-decoration: underline;"&gt;content_by_lua_block&lt;/span&gt; { ngx.say(&lt;span style="font-style: italic;"&gt;&amp;quot;Hello from Lua!&amp;quot;&lt;/span&gt;) } } &lt;/pre&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;div id="outline-container-orgaadd4fe" class="outline-2"&gt; &lt;h2 id="orgaadd4fe"&gt;Unicode normalisation forms&lt;/h2&gt; &lt;div id="text-orgaadd4fe" class="outline-text-2"&gt; &lt;p&gt; Here’s a quick example: my name (Leo Nikkilä) has multiple ways of being represented in Unicode. The most common is to use the &lt;i&gt;precomposed&lt;/i&gt; code point U+00E4 LATIN SMALL LETTER A WITH DIAERESIS (ä). In UTF-8, this is: &lt;/p&gt; &lt;pre id="orgbfaceac" class="example"&gt; $ printf ä | od -An -tx1 -v c3 a4 &lt;/pre&gt; &lt;p&gt; However, you can also write the same letter by composing U+0061 LATIN SMALL LETTER A (a) and U+0308 COMBINING DIAERESIS (◌̈) to form ä which looks identical, but is different on the wire: &lt;/p&gt; &lt;pre id="orgd11d53d" class="example"&gt; $ printf ä | od -An -tx1 -v 61 cc 88 &lt;/pre&gt; &lt;p&gt; This makes it difficult to compare strings, especially arbitrary inputs that should match pre-existing values like path names, which is why Unicode defines various &lt;i&gt;normalisation forms&lt;/i&gt; to deal with the issue. The most common forms are the ones above: NFC (“Normalization Form Canonical Composition”, preferring (pre)composed code points) and NFD (“Normalization Form Canonical Decomposition”, preferring decomposed code points)&lt;sup&gt;&lt;a role="doc-backlink" id="fnr.2" href="#fn.2" class="footref"&gt;2&lt;/a&gt;&lt;/sup&gt;. &lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;div id="outline-container-org60a41fc" class="outline-2"&gt; &lt;h2 id="org60a41fc"&gt;Unicode and file systems&lt;/h2&gt; &lt;div id="text-org60a41fc" class="outline-text-2"&gt; &lt;p&gt; Most file systems these days use NFC to encode Unicode path names&lt;sup&gt;&lt;a role="doc-backlink" id="fnr.3" href="#fn.3" class="footref"&gt;3&lt;/a&gt;&lt;/sup&gt;. &lt;/p&gt; &lt;p&gt; However, Apple’s (now legacy) HFS+ file system for Mac OS X &lt;a href="https://developer.apple.com/library/archive/technotes/tn/tn1150.html#UnicodeSubtleties"&gt;used NFD to store path names&lt;/a&gt;. This caused problems with interoperability, giving rise to utilities (like Björn Jacke’s &lt;a href="https://www.j3e.de/linux/convmv/man/"&gt;&lt;code&gt;convmv&lt;/code&gt;&lt;/a&gt;) to rename files transferred over from Mac OS X. &lt;/p&gt; &lt;p&gt; This is still evident in HFS+’s successor APFS, which &lt;a href="https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/APFS_Guide/FAQ/FAQ.html#//apple_ref/doc/uid/TP40016999-CH6-DontLinkElementID_3"&gt;supports &lt;i&gt;both&lt;/i&gt; NFC and NFD&lt;/a&gt;. Similar to how file systems provide case-insensitivity, APFS is able to recall a file using its original name, while preventing you from creating another file with the same name in another form. &lt;/p&gt; &lt;p&gt; Unfortunately Apple hasn’t completely moved away from NFD: as of macOS Sonoma 14.2.1, Finder’s WebDAV implementation &lt;i&gt;still&lt;/i&gt; expects the server to encode paths in NFD! I’m guessing this is something Apple did to support WebDAV on macOS Server and HFS+, which was supported since Mac OS X Server 10.0. &lt;/p&gt; &lt;p&gt; Ultimately this means that if your WebDAV server uses NFC for path names (extremely likely since macOS Server is now discontinued) and a path’s NFC and NFD representations differ, Finder is unable to access that path through WebDAV. &lt;/p&gt; &lt;p&gt; The PROPFIND response body includes URL-encoded path names: &lt;/p&gt; &lt;div class="org-src-container"&gt; &lt;pre class="src src-xml"&gt;&amp;lt;?&lt;span style="font-weight: bold;"&gt;xml&lt;/span&gt; &lt;span style="font-style: italic;"&gt;version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot; &lt;/span&gt;?&amp;gt; &amp;lt;&lt;span style="font-weight: bold;"&gt;D&lt;/span&gt;:&lt;span style="font-weight: bold;"&gt;multistatus&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;xmlns&lt;/span&gt;:&lt;span style="font-weight: bold; font-style: italic;"&gt;D&lt;/span&gt;=&lt;span style="font-style: italic;"&gt;&amp;quot;DAV:&amp;quot;&lt;/span&gt;&amp;gt; &amp;lt;&lt;span style="font-weight: bold;"&gt;D&lt;/span&gt;:&lt;span style="font-weight: bold;"&gt;response&lt;/span&gt;&amp;gt; &amp;lt;&lt;span style="font-weight: bold;"&gt;D&lt;/span&gt;:&lt;span style="font-weight: bold;"&gt;href&lt;/span&gt;&amp;gt;/Leo%20Nikkil%C3%A4&amp;lt;/&lt;span style="font-weight: bold;"&gt;D&lt;/span&gt;:&lt;span style="font-weight: bold;"&gt;href&lt;/span&gt;&amp;gt; &amp;lt;&lt;span style="font-weight: bold;"&gt;D&lt;/span&gt;:&lt;span style="font-weight: bold;"&gt;propstat&lt;/span&gt;&amp;gt; &amp;lt;&lt;span style="font-weight: bold;"&gt;D&lt;/span&gt;:&lt;span style="font-weight: bold;"&gt;prop&lt;/span&gt;&amp;gt; &amp;lt;&lt;span style="font-weight: bold;"&gt;D&lt;/span&gt;:&lt;span style="font-weight: bold;"&gt;displayname&lt;/span&gt;&amp;gt;Leo Nikkilä&amp;lt;/&lt;span style="font-weight: bold;"&gt;D&lt;/span&gt;:&lt;span style="font-weight: bold;"&gt;displayname&lt;/span&gt;&amp;gt; &amp;lt;/&lt;span style="font-weight: bold;"&gt;D&lt;/span&gt;:&lt;span style="font-weight: bold;"&gt;prop&lt;/span&gt;&amp;gt; &amp;lt;&lt;span style="font-weight: bold;"&gt;D&lt;/span&gt;:&lt;span style="font-weight: bold;"&gt;status&lt;/span&gt;&amp;gt;HTTP/1.1 200 OK&amp;lt;/&lt;span style="font-weight: bold;"&gt;D&lt;/span&gt;:&lt;span style="font-weight: bold;"&gt;status&lt;/span&gt;&amp;gt; &amp;lt;/&lt;span style="font-weight: bold;"&gt;D&lt;/span&gt;:&lt;span style="font-weight: bold;"&gt;propstat&lt;/span&gt;&amp;gt; &amp;lt;/&lt;span style="font-weight: bold;"&gt;D&lt;/span&gt;:&lt;span style="font-weight: bold;"&gt;response&lt;/span&gt;&amp;gt; &amp;lt;/&lt;span style="font-weight: bold;"&gt;D&lt;/span&gt;:&lt;span style="font-weight: bold;"&gt;multistatus&lt;/span&gt;&amp;gt; &lt;/pre&gt; &lt;/div&gt; &lt;p&gt; To work around this issue, we should mangle &lt;code&gt;&amp;lt;D:href&amp;gt;&lt;/code&gt; elements from NFC to NFD. (The above should be &lt;code&gt;/Leo%20Nikkila%CC%88&lt;/code&gt;.) We should also mangle request paths back from NFD to NFC when Finder tries to access them&lt;sup&gt;&lt;a role="doc-backlink" id="fnr.4" href="#fn.4" class="footref"&gt;4&lt;/a&gt;&lt;/sup&gt;. &lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;div id="outline-container-org14d05be" class="outline-2"&gt; &lt;h2 id="org14d05be"&gt;Writing the configuration in Lua&lt;/h2&gt; &lt;div id="text-org14d05be" class="outline-text-2"&gt; &lt;p&gt; Nginx can serve content from Lua, but we can also rewrite requests and responses from other Nginx modules, so that we can still serve responses using &lt;code&gt;ngx_http_dav_module&lt;/code&gt; and &lt;code&gt;ngx_http_dav_ext_module&lt;/code&gt; but tweak things where needed. &lt;/p&gt; &lt;p&gt; &lt;code&gt;rewrite_by_lua_block&lt;/code&gt; allows modifying the request, e.g. rewriting the request URI and HTTP headers: &lt;/p&gt; &lt;div class="org-src-container"&gt; &lt;pre class="src src-conf"&gt;&lt;span style="font-weight: bold; text-decoration: underline;"&gt;location /&lt;/span&gt; { &lt;span style="font-weight: bold; text-decoration: underline;"&gt;rewrite_by_lua_block&lt;/span&gt; { ngx.req.set_uri(&lt;span style="font-style: italic;"&gt;&amp;quot;/new/path/&amp;quot;&lt;/span&gt;) ngx.req.set_header(&lt;span style="font-style: italic;"&gt;&amp;quot;Destination&amp;quot;&lt;/span&gt;, &lt;span style="font-style: italic;"&gt;&amp;quot;/new/destination/&amp;quot;&lt;/span&gt;) } } &lt;/pre&gt; &lt;/div&gt; &lt;p&gt; &lt;code&gt;header_filter_by_lua_block&lt;/code&gt; and &lt;code&gt;body_filter_by_lua_block&lt;/code&gt; allow modifying the response, e.g. rewriting response headers and the response body: &lt;/p&gt; &lt;div class="org-src-container"&gt; &lt;pre class="src src-conf"&gt;&lt;span style="font-weight: bold; text-decoration: underline;"&gt;location /&lt;/span&gt; { &lt;span style="font-weight: bold; text-decoration: underline;"&gt;header_filter_by_lua_block&lt;/span&gt; { -- Reset Content-Length since we&lt;span style="font-style: italic;"&gt;'re changing the body length.&lt;/span&gt; &lt;span style="font-style: italic;"&gt; ngx.header.content_length = nil&lt;/span&gt; &lt;span style="font-style: italic;"&gt; }&lt;/span&gt; &lt;span style="font-style: italic;"&gt; &lt;/span&gt;&lt;span style="font-weight: bold; font-style: italic; text-decoration: underline;"&gt;body_filter_by_lua_block&lt;/span&gt;&lt;span style="font-style: italic;"&gt; {&lt;/span&gt; &lt;span style="font-style: italic;"&gt; -- Append a comment to the response body. The response is chunked,&lt;/span&gt; &lt;span style="font-style: italic;"&gt; -- so we'&lt;/span&gt;re processing every chunk separately here. &lt;span style="font-weight: bold; font-style: italic;"&gt;let buf, eof&lt;/span&gt; = ngx.arg[1], ngx.arg[2] if eof then &lt;span style="font-weight: bold; font-style: italic;"&gt;ngx.arg&lt;/span&gt;[&lt;span style="font-weight: bold; text-decoration: underline;"&gt;1&lt;/span&gt;] = { buf, &lt;span style="font-style: italic;"&gt;&amp;quot;&amp;lt;!-- Hello from Lua! --&amp;gt;&amp;quot;&lt;/span&gt; } end } } &lt;/pre&gt; &lt;/div&gt; &lt;p&gt; These are enough to implement Rebecca’s WebDAV configuration and rewrite NFC to NFD and back where necessary. &lt;/p&gt; &lt;p&gt; Rewriting request URIs and Destination headers was the easy part. Rewriting NFC to NFD was more difficult. Since the PROPFIND response is XML, I needed to parse the XML in Lua using LuaExpat. I could then decide what to do with different parts of the response and rewrite the &lt;code&gt;&amp;lt;D:href&amp;gt;&lt;/code&gt; elements only&lt;sup&gt;&lt;a role="doc-backlink" id="fnr.5" href="#fn.5" class="footref"&gt;5&lt;/a&gt;&lt;/sup&gt;. &lt;/p&gt; &lt;p&gt; To make things more difficult, I couldn’t find a widely available Lua library to transform NFC into NFD and back, although &lt;code&gt;luautf8&lt;/code&gt; &lt;a href="https://github.com/starwing/luautf8/pull/44"&gt;recently gained support&lt;/a&gt; for &lt;code&gt;isnfc&lt;/code&gt; and &lt;code&gt;normalize_nfc&lt;/code&gt; thanks to Alex Dowad. Since the normalisation data is now included, the NFD functions would be a nice addition as well. I’ve been going through the C source code but unfortunately haven’t found the time to attempt this myself. &lt;/p&gt; &lt;p&gt; For now, I ended up stealing the little-known &lt;code&gt;ustring&lt;/code&gt; module from &lt;a href="https://github.com/wikimedia/mediawiki-extensions-Scribunto/tree/f89e892a062cf6492343cba2f0f9940194ec58c5/includes/Engines/LuaCommon/lualib/ustring"&gt;Wikimedia’s Scribunto codebase&lt;/a&gt;, which implements functions for both transformations in pure Lua. The downside is that it isn’t available in any repositories, so I had to vendor in the whole thing. &lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;div id="outline-container-org48b47f3" class="outline-2"&gt; &lt;h2 id="org48b47f3"&gt;Using this yourself&lt;/h2&gt; &lt;div id="text-org48b47f3" class="outline-text-2"&gt; &lt;p&gt; This took quite a bit of work, but I’m glad I didn’t have to change my name&lt;sup&gt;&lt;a role="doc-backlink" id="fnr.6" href="#fn.6" class="footref"&gt;6&lt;/a&gt;&lt;/sup&gt; to access my files with Finder. &lt;/p&gt; &lt;p&gt; You’ll find the resulting Lua scripts and how to install them at &lt;a href="https://git.sr.ht/~lnikkila/nginx-webdav-quirks"&gt;https://git.sr.ht/~lnikkila/nginx-webdav-quirks&lt;/a&gt;. For discoverability, I’ve also made the repository available at a GitHub mirror. &lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;div id="outline-container-org54021f2" class="outline-2"&gt; &lt;h2 id="org54021f2"&gt;Bonus: Disabling Finder’s Quick Look thumbnail generation&lt;/h2&gt; &lt;div id="text-org54021f2" class="outline-text-2"&gt; &lt;p&gt; While working on this, I discovered that Finder requests some magic files from the server after connecting, e.g. &lt;code&gt;/.ql_disablethumbnails&lt;/code&gt; and &lt;code&gt;/.ql_disablecache&lt;/code&gt;. &lt;/p&gt; &lt;p&gt; I’ve been looking for a way to globally disable thumbnail generation while browsing remote files since this generates a lot of traffic. Creating empty files at these paths seems to work, but browsing will inexplicably get much slower. &lt;/p&gt; &lt;p&gt; Based on the access logs, Finder is downloading entire files from the server to determine which file types they are, whereas normally it only looks at the header/trailer of each file. This doesn’t make much sense, but I thought I’d document this here since the problem &lt;a href="https://apple.stackexchange.com/q/12016"&gt;is being discussed&lt;/a&gt; but the magic files are rarely mentioned. &lt;/p&gt; &lt;p&gt; I ended up disabling this hack, but maybe someone is able to figure out something that works. Clearly Apple is aware of similar problems since they’ve also implemented the &lt;a href="https://support.apple.com/en-my/HT208209"&gt;&lt;code&gt;DSDontWriteNetworkStores&lt;/code&gt;&lt;/a&gt; flag. &lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;div id="footnotes"&gt; &lt;h2 class="footnotes"&gt;Footnotes: &lt;/h2&gt; &lt;div id="text-footnotes"&gt; &lt;div class="footdef"&gt;&lt;sup&gt;&lt;a role="doc-backlink" id="fn.1" href="#fnr.1" class="footnum"&gt;1&lt;/a&gt;&lt;/sup&gt; &lt;div role="doc-footnote" class="footpara"&gt;&lt;p class="footpara"&gt; lighttpd is rarely mentioned when it comes to WebDAV, it has a very nice &lt;code&gt;mod_webdav&lt;/code&gt; module that relies on SQLite for locking. &lt;/p&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="footdef"&gt;&lt;sup&gt;&lt;a role="doc-backlink" id="fn.2" href="#fnr.2" class="footnum"&gt;2&lt;/a&gt;&lt;/sup&gt; &lt;div role="doc-footnote" class="footpara"&gt;&lt;p class="footpara"&gt; Some languages (like Vietnamese) have characters with multiple combining marks, which makes their ordering affect the on-the-wire representation as well. Normalisation forms also define a canonical order for ordering multiple combining characters. &lt;/p&gt; &lt;p class="footpara"&gt; For a comprehensive overview and all the things I glossed over, see the &lt;a href="https://en.wikipedia.org/wiki/Unicode_equivalence"&gt;Unicode equivalence&lt;/a&gt; article on Wikipedia. &lt;/p&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="footdef"&gt;&lt;sup&gt;&lt;a role="doc-backlink" id="fn.3" href="#fnr.3" class="footnum"&gt;3&lt;/a&gt;&lt;/sup&gt; &lt;div role="doc-footnote" class="footpara"&gt;&lt;p class="footpara"&gt; I have no proof to back this up, but I’m assuming this is because precomposed characters use less storage space. &lt;/p&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="footdef"&gt;&lt;sup&gt;&lt;a role="doc-backlink" id="fn.4" href="#fnr.4" class="footnum"&gt;4&lt;/a&gt;&lt;/sup&gt; &lt;div role="doc-footnote" class="footpara"&gt;&lt;p class="footpara"&gt; Finder sends an easily sniffed User-Agent like &lt;code&gt;WebDAVFS/3.0.0 (03008000) Darwin/23.2.0 (arm64)&lt;/code&gt; which helps us avoid mangling these path names unless we’re specifically talking to Finder. &lt;/p&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="footdef"&gt;&lt;sup&gt;&lt;a role="doc-backlink" id="fn.5" href="#fnr.5" class="footnum"&gt;5&lt;/a&gt;&lt;/sup&gt; &lt;div role="doc-footnote" class="footpara"&gt;&lt;p class="footpara"&gt; LuaExpat doesn’t currently expose Expat’s &lt;a href="https://libexpat.github.io/doc/api/latest/#XML_DefaultCurrent"&gt;&lt;code&gt;XML_DefaultCurrent&lt;/code&gt;&lt;/a&gt; function, which would make it easier to output the original XML without having to format and escape it again. I had to write some extra code to deal with this. &lt;/p&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="footdef"&gt;&lt;sup&gt;&lt;a role="doc-backlink" id="fn.6" href="#fnr.6" class="footnum"&gt;6&lt;/a&gt;&lt;/sup&gt; &lt;div role="doc-footnote" class="footpara"&gt;&lt;p class="footpara"&gt; Nor Kieran Hebden’s tracks in my music collection under his cheeky ⣎⡇ꉺლ༽இ•̛)ྀ◞ ༎ຶ ༽ৣৢ؞ৢ؞ؖ ꉺლ moniker. &lt;/p&gt;&lt;/div&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt;</content>
  </entry>
  <entry>
    <title>Advanced HTML with Org mode to impress your friends</title>
    <id>https://lnikki.la/posts/advanced-html-with-org-mode-to-impress-your-friends.html</id>
    <author>
      <name>Nix build user</name>
      <email>nixbld@localhost</email>
    </author>
    <updated>2023-09-02T00:00:00Z</updated>
    <link rel="alternate" href="https://lnikki.la/posts/advanced-html-with-org-mode-to-impress-your-friends.html" />
    <content type="html">&lt;nav role="doc-toc" id="table-of-contents"&gt; &lt;h2&gt;Table of Contents&lt;/h2&gt; &lt;div role="doc-toc" id="text-table-of-contents"&gt; &lt;ul&gt; &lt;li&gt;&lt;a href="#org657f941"&gt;Building the site&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="#org8732272"&gt;HTML export variables&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="#org68eeecf"&gt;Demystifying org-export&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="#orgddd773b"&gt;Deriving the org-html backend&lt;/a&gt; &lt;ul&gt; &lt;li&gt;&lt;a href="#orgddf90d5"&gt;Examples&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt;&lt;a href="#orgf1ae52d"&gt;The info alist&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="#org3eb0e6b"&gt;But wait, there’s more&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="#org15d45e5"&gt;Acknowledgements&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;/div&gt; &lt;/nav&gt; &lt;p&gt; This time, let’s indulge in the favourite pastime of every blog-building hacker out there: blogging about blogging. &lt;/p&gt; &lt;p&gt; This website replaced my decade-old and dormant site. Over time, the threshold to post got higher and higher. I wasn’t interested in re-learning how to build it just to write something. &lt;/p&gt; &lt;p&gt; I’ve since learned from my mistake. This site is built with &lt;a href="https://orgmode.org"&gt;Org mode&lt;/a&gt; using org-publish. I use Org daily, and there isn’t really anything to re-learn. Everything happens from Emacs, and there are zero additional dependencies to download. &lt;/p&gt; &lt;div id="outline-container-org657f941" class="outline-2"&gt; &lt;h2 id="org657f941"&gt;Building the site&lt;/h2&gt; &lt;div id="text-org657f941" class="outline-text-2"&gt; &lt;p&gt; I’m not going to discuss how to set up org-publish, there are plenty of resources online. I’ve linked some posts that I found exceptionally helpful&lt;sup&gt;&lt;a role="doc-backlink" id="fnr.1" href="#fn.1" class="footref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;sup&gt;, &lt;/sup&gt;&lt;sup&gt;&lt;a role="doc-backlink" id="fnr.2" href="#fn.2" class="footref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;sup&gt;, &lt;/sup&gt;&lt;sup&gt;&lt;a role="doc-backlink" id="fnr.3" href="#fn.3" class="footref"&gt;3&lt;/a&gt;&lt;/sup&gt;. &lt;/p&gt; &lt;p&gt; Essentially, I’ve set up a &lt;code&gt;build-site.el&lt;/code&gt; script that runs Emacs with &lt;code&gt;emacs -Q --script&lt;/code&gt; to invoke &lt;a href="https://orgmode.org/manual/Triggering-Publication.html"&gt;&lt;code&gt;org-publish&lt;/code&gt;&lt;/a&gt;. This way the build can be fully automated with source control, behaving more like a usual static site generator, although you can still invoke it directly from the editor. &lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;div id="outline-container-org8732272" class="outline-2"&gt; &lt;h2 id="org8732272"&gt;HTML export variables&lt;/h2&gt; &lt;div id="text-org8732272" class="outline-text-2"&gt; &lt;p&gt; org-html exposes a number of variables to customise the HTML output, for example: &lt;/p&gt; &lt;ul class="org-ul"&gt; &lt;li&gt;&lt;code&gt;org-html-doctype&lt;/code&gt;&lt;/li&gt; &lt;li&gt;&lt;code&gt;org-html-head&lt;/code&gt;&lt;/li&gt; &lt;li&gt;&lt;code&gt;org-html-head-extra&lt;/code&gt;&lt;/li&gt; &lt;li&gt;&lt;code&gt;org-html-meta-tags&lt;/code&gt;&lt;/li&gt; &lt;li&gt;&lt;code&gt;org-html-divs&lt;/code&gt;&lt;/li&gt; &lt;li&gt;&lt;code&gt;org-html-preamble&lt;/code&gt;&lt;/li&gt; &lt;li&gt;&lt;code&gt;org-html-postamble&lt;/code&gt;&lt;/li&gt; &lt;li&gt;&lt;code&gt;org-html-scripts&lt;/code&gt;&lt;/li&gt; &lt;/ul&gt; &lt;p&gt; For a full list, see &lt;code&gt;M-x describe-variable ^org-html-&lt;/code&gt;. Most notable are &lt;a href="https://orgmode.org/manual/HTML-preamble-and-postamble.html"&gt;&lt;code&gt;org-html-{pre,post}amble&lt;/code&gt;&lt;/a&gt;, which people have used for all kinds of layouts. &lt;/p&gt; &lt;p&gt; However, all of these options are fairly limited. Conceptually simple changes, like placing the date and author information below the title, or changing the “Table of Contents” heading, are surprisingly tricky with these. &lt;/p&gt; &lt;p&gt; Now that I’ve learned about the better way to achieve these types of changes, I wish org-html didn’t have some of these variables at all. They make some narrow use cases easy, but at the expense of obscuring more powerful concepts behind org-export. &lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;div id="outline-container-org68eeecf" class="outline-2"&gt; &lt;h2 id="org68eeecf"&gt;Demystifying org-export&lt;/h2&gt; &lt;div id="text-org68eeecf" class="outline-text-2"&gt; &lt;p&gt; org-export supports swappable backends for formats. These are defined using &lt;a href="https://orgmode.org/worg/dev/org-export-reference.html#back-end"&gt;&lt;code&gt;org-export-define-backend&lt;/code&gt;&lt;/a&gt; which takes a unique backend name and an alist of &lt;i&gt;transcoders&lt;/i&gt;. Broadly speaking, transcoders are functions that take an Org element plus some metadata and return a string representing it in the output format. &lt;/p&gt; &lt;p&gt; Here’s an example of a minimal S-expression backend:&lt;sup&gt;&lt;a role="doc-backlink" id="fnr.4" href="#fn.4" class="footref"&gt;4&lt;/a&gt;&lt;/sup&gt; &lt;/p&gt; &lt;div class="org-src-container"&gt; &lt;pre class="src src-elisp"&gt;(&lt;span style="font-weight: bold;"&gt;defun&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;indent-string&lt;/span&gt; (string width) (org-ascii--indent-string (string-trim string) width)) (&lt;span style="font-weight: bold;"&gt;defun&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;sexp-template&lt;/span&gt; (contents info) (format &lt;span style="font-style: italic;"&gt;&amp;quot;(template\n%s)&amp;quot;&lt;/span&gt; (indent-string contents 1))) (&lt;span style="font-weight: bold;"&gt;defun&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;sexp-inner-template&lt;/span&gt; (contents info) (format &lt;span style="font-style: italic;"&gt;&amp;quot;(inner-template\n%s)&amp;quot;&lt;/span&gt; (indent-string contents 1))) (&lt;span style="font-weight: bold;"&gt;defun&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;sexp-section&lt;/span&gt; (section contents info) (format &lt;span style="font-style: italic;"&gt;&amp;quot;(section\n%s)&amp;quot;&lt;/span&gt; (indent-string contents 1))) (&lt;span style="font-weight: bold;"&gt;defun&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;sexp-headline&lt;/span&gt; (headline contents info) (format &lt;span style="font-style: italic;"&gt;&amp;quot;(headline %s\n%s)&amp;quot;&lt;/span&gt; (org-export-data (org-element-property &lt;span style="font-weight: bold;"&gt;:title&lt;/span&gt; headline) info) (indent-string contents 1))) (&lt;span style="font-weight: bold;"&gt;defun&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;sexp-paragraph&lt;/span&gt; (paragraph contents info) (format &lt;span style="font-style: italic;"&gt;&amp;quot;(paragraph %s)&amp;quot;&lt;/span&gt; contents)) (&lt;span style="font-weight: bold;"&gt;defun&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;sexp-plain-text&lt;/span&gt; (text info) (format &lt;span style="font-style: italic;"&gt;&amp;quot;\&amp;quot;%s\&amp;quot;&amp;quot;&lt;/span&gt; (string-replace &lt;span style="font-style: italic;"&gt;&amp;quot;\n&amp;quot;&lt;/span&gt; &lt;span style="font-style: italic;"&gt;&amp;quot;\\n&amp;quot;&lt;/span&gt; text))) (org-export-define-backend 'sexp '((template . sexp-template) (inner-template . sexp-inner-template) (section . sexp-section) (headline . sexp-headline) (paragraph . sexp-paragraph) (plain-text . sexp-plain-text))) &lt;/pre&gt; &lt;/div&gt; &lt;p&gt; and its output for this simple document: &lt;/p&gt; &lt;div class="org-src-container"&gt; &lt;pre class="src src-elisp"&gt;(&lt;span style="font-weight: bold;"&gt;with-temp-buffer&lt;/span&gt; (insert &lt;span style="font-style: italic;"&gt;&amp;quot;Hello world!\n&amp;quot;&lt;/span&gt;) (insert &lt;span style="font-style: italic;"&gt;&amp;quot;* Here's a headline\n&amp;quot;&lt;/span&gt;) (insert &lt;span style="font-style: italic;"&gt;&amp;quot;Here's some more text\n&amp;quot;&lt;/span&gt;) (org-export-as 'sexp)) &lt;/pre&gt; &lt;/div&gt; &lt;pre class="example"&gt; (template (inner-template (section (paragraph &amp;quot;Hello world!\n&amp;quot;)) (headline &amp;quot;Here's a headline&amp;quot; (section (paragraph &amp;quot;Here's some more text\n&amp;quot;))))) &lt;/pre&gt; &lt;/div&gt; &lt;/div&gt; &lt;div id="outline-container-orgddd773b" class="outline-2"&gt; &lt;h2 id="orgddd773b"&gt;Deriving the org-html backend&lt;/h2&gt; &lt;div id="text-orgddd773b" class="outline-text-2"&gt; &lt;p&gt; In addition to defining new backends, you can also derive an existing backend using &lt;a href="https://orgmode.org/worg/dev/org-export-reference.html#back-end"&gt;&lt;code&gt;org-export-define-derived-backend&lt;/code&gt;&lt;/a&gt;. Can you see where this is going? &lt;/p&gt; &lt;p&gt; Here’s how to derive the HTML backend and replace some transcoders: &lt;/p&gt; &lt;div class="org-src-container"&gt; &lt;pre class="src src-elisp"&gt;(&lt;span style="font-weight: bold;"&gt;defun&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;my-html-paragraph&lt;/span&gt; (paragraph contents info) (format &lt;span style="font-style: italic;"&gt;&amp;quot;&amp;lt;p class=\&amp;quot;my-paragraph\&amp;quot;&amp;gt;%s&amp;lt;/p&amp;gt;&amp;quot;&lt;/span&gt; contents)) (org-export-define-derived-backend 'my-html 'html &lt;span style="font-weight: bold;"&gt;:translate-alist&lt;/span&gt; '((paragraph . my-html-paragraph))) &lt;/pre&gt; &lt;/div&gt; &lt;p&gt; yielding HTML with a modified &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tag: &lt;/p&gt; &lt;pre class="example"&gt; &amp;lt;p class=&amp;quot;my-paragraph&amp;quot;&amp;gt;Hello world!&amp;lt;/p&amp;gt; &lt;/pre&gt; &lt;p&gt; What can you customise with this? Everything&lt;sup&gt;&lt;a role="doc-backlink" id="fnr.5" href="#fn.5" class="footref"&gt;5&lt;/a&gt;&lt;/sup&gt;. &lt;/p&gt; &lt;/div&gt; &lt;div id="outline-container-orgddf90d5" class="outline-3"&gt; &lt;h3 id="orgddf90d5"&gt;Examples&lt;/h3&gt; &lt;div id="text-orgddf90d5" class="outline-text-3"&gt; &lt;p&gt; Rather than relying the various &lt;code&gt;org-html*&lt;/code&gt; variables to modify the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; element and other pieces of the template, it’s possible to render the whole thing yourself: &lt;/p&gt; &lt;div class="org-src-container"&gt; &lt;pre class="src src-elisp"&gt;(&lt;span style="font-weight: bold;"&gt;defun&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;cool-html-template&lt;/span&gt; (contents info) (concat &lt;span style="font-style: italic;"&gt;&amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n&amp;quot;&lt;/span&gt; &lt;span style="font-style: italic;"&gt;&amp;quot;&amp;lt;html lang=\&amp;quot;en\&amp;quot;&amp;gt;\n&amp;quot;&lt;/span&gt; &lt;span style="font-style: italic;"&gt;&amp;quot; &amp;lt;head&amp;gt;\n&amp;quot;&lt;/span&gt; &lt;span style="font-style: italic;"&gt;&amp;quot; &amp;lt;title&amp;gt;&amp;quot;&lt;/span&gt; (org-export-data (plist-get info &lt;span style="font-weight: bold;"&gt;:title&lt;/span&gt;) info) &lt;span style="font-weight: bold; font-style: italic;"&gt;&amp;quot;&amp;lt;/title&amp;gt;\n&amp;quot;&lt;/span&gt; &lt;span style="font-style: italic;"&gt;&amp;quot; &amp;lt;/head&amp;gt;\n&amp;quot;&lt;/span&gt; &lt;span style="font-style: italic;"&gt;&amp;quot; &amp;lt;body&amp;gt;\n&amp;quot;&lt;/span&gt; (indent-string contents 4) &lt;span style="font-style: italic;"&gt;&amp;quot;\n&amp;quot;&lt;/span&gt; &lt;span style="font-style: italic;"&gt;&amp;quot; &amp;lt;/body&amp;gt;\n&amp;quot;&lt;/span&gt; &lt;span style="font-style: italic;"&gt;&amp;quot;&amp;lt;/html&amp;gt;&amp;quot;&lt;/span&gt;)) (org-export-define-derived-backend 'cool-html 'html &lt;span style="font-weight: bold;"&gt;:translate-alist&lt;/span&gt; '((template . cool-html-template))) &lt;/pre&gt; &lt;/div&gt; &lt;p&gt; resulting in this majestic display: &lt;/p&gt; &lt;pre class="example"&gt; &amp;lt;!DOCTYPE html&amp;gt; &amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt; &amp;lt;head&amp;gt; &amp;lt;title&amp;gt;cool crab's cool page&amp;lt;/title&amp;gt; &amp;lt;/head&amp;gt; &amp;lt;body&amp;gt; 🦀 &amp;lt;/body&amp;gt; &amp;lt;/html&amp;gt; &lt;/pre&gt; &lt;p&gt; Rather than using &lt;code&gt;org-html-{pre,post}amble&lt;/code&gt;, it’s possible to render the &lt;code&gt;inner-template&lt;/code&gt; as well, e.g. to wrap the document in &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;: &lt;/p&gt; &lt;div class="org-src-container"&gt; &lt;pre class="src src-elisp"&gt;(&lt;span style="font-weight: bold;"&gt;defun&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;cool-html-inner-template&lt;/span&gt; (contents info) (concat &lt;span style="font-style: italic;"&gt;&amp;quot;&amp;lt;main&amp;gt;\n&amp;quot;&lt;/span&gt; (indent-string contents 2) &lt;span style="font-style: italic;"&gt;&amp;quot;\n&amp;lt;/main&amp;gt;&amp;quot;&lt;/span&gt;)) (org-export-define-derived-backend 'cool-html 'html &lt;span style="font-weight: bold;"&gt;:translate-alist&lt;/span&gt; '((template . cool-html-template) (inner-template . cool-html-inner-template))) &lt;/pre&gt; &lt;/div&gt; &lt;pre id="orga20926d" class="example"&gt; &amp;lt;!DOCTYPE html&amp;gt; &amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt; &amp;lt;head&amp;gt; &amp;lt;title&amp;gt;cool crab's cool page&amp;lt;/title&amp;gt; &amp;lt;/head&amp;gt; &amp;lt;body&amp;gt; &amp;lt;main&amp;gt; &amp;lt;img src=&amp;quot;/cool-crab.png&amp;quot; alt=&amp;quot;a cool-looking crab on the beach, wearing dark sunglasses and enjoying a refreshing beverage&amp;quot; /&amp;gt; &amp;lt;/main&amp;gt; &amp;lt;/body&amp;gt; &amp;lt;/html&amp;gt; &lt;/pre&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;div id="outline-container-orgf1ae52d" class="outline-2"&gt; &lt;h2 id="orgf1ae52d"&gt;The info alist&lt;/h2&gt; &lt;div id="text-orgf1ae52d" class="outline-text-2"&gt; &lt;p&gt; I haven’t yet mentioned the &lt;code&gt;info&lt;/code&gt; argument passed into the transcoder functions. The &lt;a href="https://orgmode.org/worg/dev/org-export-reference.html#communication"&gt;Org export reference documentation&lt;/a&gt; describes this as a “communication channel”, it’s an alist used to pass around metadata. &lt;/p&gt; &lt;p&gt; There’s a lot of data in there, including keywords defined in the Org document. This makes it useful to implement custom options that can be set in the document to influence the generated HTML. &lt;/p&gt; &lt;p&gt; There are convenience functions for accessing data within the &lt;code&gt;info&lt;/code&gt; alist, like &lt;code&gt;org-export-get-date&lt;/code&gt;. &lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;div id="outline-container-org3eb0e6b" class="outline-2"&gt; &lt;h2 id="org3eb0e6b"&gt;But wait, there’s more&lt;/h2&gt; &lt;div id="text-org3eb0e6b" class="outline-text-2"&gt; &lt;p&gt; I’ve only scratched the surface of what org-export backends can do. &lt;/p&gt; &lt;p&gt; You can also go nuts with the Org parse tree before it’s exported using filters, and modify the plain markup before parsing using hooks. See the &lt;a href="https://orgmode.org/manual/Advanced-Export-Configuration.html"&gt;Advanced Export Configuration&lt;/a&gt; manual page for details. &lt;/p&gt; &lt;p&gt; To sum up, export backends provide extremely flexible and elegant rendering of Org documents, and you can do a lot more with a backend compared to the available export options. &lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;div id="outline-container-org15d45e5" class="outline-2"&gt; &lt;h2 id="org15d45e5"&gt;Acknowledgements&lt;/h2&gt; &lt;div id="text-org15d45e5" class="outline-text-2"&gt; &lt;p&gt; Thanks to Jason Stewart for feedback on an earlier draft of this post. &lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;div id="footnotes"&gt; &lt;h2 class="footnotes"&gt;Footnotes: &lt;/h2&gt; &lt;div id="text-footnotes"&gt; &lt;div class="footdef"&gt;&lt;sup&gt;&lt;a role="doc-backlink" id="fn.1" href="#fnr.1" class="footnum"&gt;1&lt;/a&gt;&lt;/sup&gt; &lt;div role="doc-footnote" class="footpara"&gt;&lt;p class="footpara"&gt; &lt;a href="https://thibaultmarin.github.io/blog/posts/2016-11-13-Personal_website_in_org.html"&gt;https://thibaultmarin.github.io/blog/posts/2016-11-13-Personal_website_in_org.html&lt;/a&gt; &lt;/p&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="footdef"&gt;&lt;sup&gt;&lt;a role="doc-backlink" id="fn.2" href="#fnr.2" class="footnum"&gt;2&lt;/a&gt;&lt;/sup&gt; &lt;div role="doc-footnote" class="footpara"&gt;&lt;p class="footpara"&gt; &lt;a href="https://taingram.org/blog/org-mode-blog.html"&gt;https://taingram.org/blog/org-mode-blog.html&lt;/a&gt; &lt;/p&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="footdef"&gt;&lt;sup&gt;&lt;a role="doc-backlink" id="fn.3" href="#fnr.3" class="footnum"&gt;3&lt;/a&gt;&lt;/sup&gt; &lt;div role="doc-footnote" class="footpara"&gt;&lt;p class="footpara"&gt; &lt;a href="https://www.brautaset.org/posts/blogging-with-org-mode.html"&gt;https://www.brautaset.org/posts/blogging-with-org-mode.html&lt;/a&gt; &lt;/p&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="footdef"&gt;&lt;sup&gt;&lt;a role="doc-backlink" id="fn.4" href="#fnr.4" class="footnum"&gt;4&lt;/a&gt;&lt;/sup&gt; &lt;div role="doc-footnote" class="footpara"&gt;&lt;p class="footpara"&gt; These examples are generated by &lt;a href="https://orgmode.org/worg/org-contrib/babel/"&gt;Org Babel&lt;/a&gt;! &lt;/p&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="footdef"&gt;&lt;sup&gt;&lt;a role="doc-backlink" id="fn.5" href="#fnr.5" class="footnum"&gt;5&lt;/a&gt;&lt;/sup&gt; &lt;div role="doc-footnote" class="footpara"&gt;&lt;p class="footpara"&gt; See the &lt;code&gt;org-element-all-elements&lt;/code&gt; and &lt;code&gt;org-element-all-objects&lt;/code&gt; variables for the list of types, also documented in the &lt;a href="https://orgmode.org/worg/dev/org-element-api.html#attributes"&gt;Org element API&lt;/a&gt;. In addition to those, org-export adds the &lt;code&gt;template&lt;/code&gt;, &lt;code&gt;inner-template&lt;/code&gt;, and &lt;code&gt;plain-text&lt;/code&gt; types. &lt;/p&gt;&lt;/div&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt;</content>
  </entry>
  <entry>
    <title>Disabling CLAT46 on macOS Ventura</title>
    <id>https://lnikki.la/posts/disabling-clat46-on-macos-ventura.html</id>
    <author>
      <name>Nix build user</name>
      <email>nixbld@localhost</email>
    </author>
    <updated>2023-08-12T00:00:00Z</updated>
    <link rel="alternate" href="https://lnikki.la/posts/disabling-clat46-on-macos-ventura.html" />
    <content type="html">&lt;nav role="doc-toc" id="table-of-contents"&gt; &lt;h2&gt;Table of Contents&lt;/h2&gt; &lt;div role="doc-toc" id="text-table-of-contents"&gt; &lt;ul&gt; &lt;li&gt;&lt;a href="#orgf24f6b3"&gt;Auto-configuration on macOS&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="#orgcde2411"&gt;Problems in macOS Ventura&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="#org05c1696"&gt;Workaround&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;/div&gt; &lt;/nav&gt; &lt;aside&gt; &lt;h4&gt;Update on 2024-02-05&lt;/h4&gt; &lt;p&gt; Some months ago I disabled NAT66 on my network, and the problems I’ve had with macOS’s CLAT went away entirely. &lt;/p&gt; &lt;p&gt; I was NATing traffic while figuring out how to configure my devices properly, that must be what I get for not doing things the right IPv6 way… :) &lt;/p&gt; &lt;/aside&gt; &lt;hr /&gt; &lt;p&gt; Long story short: I run an IPv6-only network at home. Explaining why is out of scope here. &lt;/p&gt; &lt;p&gt; Like &lt;a href="https://labs.ripe.net/author/ondrej_caletka_1/deploying-ipv6-mostly-access-networks/"&gt;Ondřej Caletka&lt;/a&gt; and &lt;a href="https://web.suffieldacademy.org/~jhealy/posts/2023/05/05/automatic-clat-on-macos/"&gt;Jason Healy&lt;/a&gt; have already discussed in their respective posts, the past couple of macOS releases have been shipping with IPv4-to-IPv6 client-side translation (CLAT46). &lt;/p&gt; &lt;p&gt; Essentially this means you can transparently interact with IPv4 addresses without an IPv4 connection, provided that your IPv6 connection supports NAT64: &lt;/p&gt; &lt;pre id="org692f10b" class="example"&gt; $ sudo networksetup -setv4off Wi-Fi $ ping -c1 1.1.1.1 PING 1.1.1.1 (1.1.1.1): 56 data bytes 64 bytes from 1.1.1.1: icmp_seq=0 ttl=59 time=6.691 ms --- 1.1.1.1 ping statistics --- 1 packets transmitted, 1 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 6.691/6.691/6.691/0.000 ms &lt;/pre&gt; &lt;p&gt; See the previous links for how CLAT works in detail. &lt;/p&gt; &lt;div id="outline-container-orgf24f6b3" class="outline-2"&gt; &lt;h2 id="orgf24f6b3"&gt;Auto-configuration on macOS&lt;/h2&gt; &lt;div id="text-orgf24f6b3" class="outline-text-2"&gt; &lt;p&gt; This is what little I’ve found out about the implementation. &lt;/p&gt; &lt;p&gt; During network setup, the &lt;code&gt;com.apple.IPConfiguration&lt;/code&gt; subsystem decides whether CLAT should be enabled, and logs this in Console: &lt;/p&gt; &lt;pre id="org0f20355" class="example"&gt; default 08:46:40.447314+0300 configd RTADV en0: CLAT46 enabled using address 192.0.0.2 default 08:46:42.059657+0300 configd RTADV en0: removed CLAT46 address 192.0.0.2 &lt;/pre&gt; &lt;p&gt; The IPConfiguration agent can be tinkered with using &lt;code&gt;ipconfig&lt;/code&gt;, which returns the CLAT status through &lt;code&gt;ipconfig getsummary &amp;lt;interface&amp;gt;&lt;/code&gt; when active: &lt;/p&gt; &lt;pre id="orgd34b365" class="example"&gt; $ ipconfig getsummary en0 &amp;lt;dictionary&amp;gt; { IPv6 : &amp;lt;array&amp;gt; { 0 : &amp;lt;dictionary&amp;gt; { RTADV : &amp;lt;dictionary&amp;gt; { CLAT46Active : TRUE ... } ... } } ... } &lt;/pre&gt; &lt;p&gt; There’s also a sysctl flag called &lt;code&gt;net.inet6.ip6.clat_debug&lt;/code&gt; that I assume outputs additional details in the logs. This might require running &lt;code&gt;sudo ipconfig setverbose 1&lt;/code&gt;. &lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;div id="outline-container-orgcde2411" class="outline-2"&gt; &lt;h2 id="orgcde2411"&gt;Problems in macOS Ventura&lt;/h2&gt; &lt;div id="text-orgcde2411" class="outline-text-2"&gt; &lt;p&gt; More recently, macOS Ventura seems to have relaxed the requirements to automatically enable CLAT. &lt;/p&gt; &lt;p&gt; My home network uses OpenWrt, and it’s configured IPv6-only with SLAAC (without DHCPv6) and has both NAT66 and NAT64 (using Jool). I haven’t set the &lt;a href="https://www.rfc-editor.org/rfc/rfc7050"&gt;RFC 7050&lt;/a&gt; Pref64 RA flag, nor does the network have a DHCPv4 server that might advertise the &lt;a href="https://www.rfc-editor.org/rfc/rfc8925"&gt;RFC 8925&lt;/a&gt; IPv6-Only Preferred option. &lt;/p&gt; &lt;p&gt; After upgrading to macOS Ventura, I was very confused to see IPv4 traffic supposedly working, until I learned about the CLAT. I think auto-configuring CLAT is &lt;i&gt;great&lt;/i&gt; for IPv6 adoption, but it seems there are still some bugs: &lt;/p&gt; &lt;ol class="org-ol"&gt; &lt;li&gt;&lt;p&gt; Firefox and other software now stubbornly prefer IPv4, although explicit IPv6 connections are handled correctly. Even attempting to force curl’s &lt;a href="https://daniel.haxx.se/blog/2020/03/16/curl-ootw-happy-eyeballs-timeout/"&gt;happy eyeballs implementation&lt;/a&gt; to give IPv6 a generous head start doesn’t work, and an IPv4 connection is made immediately: &lt;/p&gt; &lt;pre class="example"&gt; $ curl -vvv --happy-eyeballs-timeout-ms=1000 https://icanhazip.com * Trying 104.18.114.97:443... * Connected to icanhazip.com (104.18.114.97) port 443 (#0) &lt;/pre&gt;&lt;/li&gt; &lt;li&gt;&lt;p&gt; A more annoying bug is one that Jason Healy wrote about, where CLAT routes are sometimes misconfigured after switching networks. This has also happened to me, often after waking my laptop from sleep in the morning. &lt;/p&gt; &lt;p&gt; Disconnecting and reconnecting fixes this right away, but the issue is usually back the next morning. Restarting macOS gets rid of this issue for a while, but it’ll eventually return. &lt;/p&gt;&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;div id="outline-container-org05c1696" class="outline-2"&gt; &lt;h2 id="org05c1696"&gt;Workaround&lt;/h2&gt; &lt;div id="text-org05c1696" class="outline-text-2"&gt; &lt;p&gt; I wanted to validate that CLAT was really the source of these issues. The first thing I tried was disabling IPv4 altogether in System Settings, which can also be done using &lt;code&gt;networksetup&lt;/code&gt;: &lt;/p&gt; &lt;pre class="example"&gt; $ sudo networksetup -setv4off Wi-Fi &lt;/pre&gt; &lt;p&gt; Unfortunately CLAT doesn’t care about whether IPv4 is enabled, and it’s auto-configured nevertheless. Next, I simply deleted the configured IPv4 address using &lt;code&gt;ifconfig&lt;/code&gt;: &lt;/p&gt; &lt;pre id="orgcc1d969" class="example"&gt; $ sudo ifconfig en0 delete 192.0.0.2 &lt;/pre&gt; &lt;p&gt; This did the trick, and the issues I’d seen went away immediately. &lt;/p&gt; &lt;p&gt; Unfortunately just removing the address doesn’t persist anywhere, and after reconnecting to the network, CLAT was auto-configured again with the same settings. &lt;/p&gt; &lt;p&gt; After perusing manpages and the morsels of macOS IPv6 documentation I could find, I was unable to find a nice CLAT-specific flag that could disable it. &lt;/p&gt; &lt;p&gt; Eventually I was able to figure out a workaround: configuring IPv4 with a link-local address from 169.254.0.0/16. Here’s an example, you might want to pick a random address from that block instead of this one: &lt;/p&gt; &lt;pre id="org2308fb1" class="example"&gt; $ sudo networksetup -setmanual Wi-Fi 169.254.1.1 255.255.255.0 &lt;/pre&gt; &lt;p&gt; &lt;code&gt;ipconfig getsummary en0&lt;/code&gt; now reports &lt;code&gt;ConfigMethod : LinkLocal&lt;/code&gt; for IPv4, and more crucially, &lt;code&gt;CLAT46Active&lt;/code&gt; is nowhere to be seen. &lt;/p&gt; &lt;/div&gt; &lt;/div&gt;</content>
  </entry>
</feed>
