<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/assets/site_slatecave/atom_to_html.xslt"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>slatecave.net - Slatians Blog</title>
    <subtitle>Technical explorations, Minddumps, Opinions and whatever I&#x27;m interested in.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://slatecave.net/blog/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://slatecave.net/blog/"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-03-10T00:00:00+00:00</updated>
    <id>https://slatecave.net/blog/atom.xml</id>
    <entry xml:lang="en">
        <title>The 39c3 Search Engines Post</title>
        <published>2026-03-10T00:00:00+00:00</published>
        <updated>2026-03-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/post-39c3/"/>
        <id>https://slatecave.net/blog/post-39c3/</id>
        
        <summary type="text">I went to 39c3 with one main quest: Search Engines.</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/post-39c3/">&lt;p&gt;At 39c3 I visited with one main quest: Talk to as many beings who have anything to do with search engines as possible.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;To summarise it went very well and I was pretty tired after congress.&lt;&#x2F;p&gt;







	
		
		
		
		
		
			
			
		
	

	
		
	


	
&lt;div class=&quot;chat-box&quot;&gt;
	&lt;img src=&quot;&amp;#x2F;resources&amp;#x2F;emoji&amp;#x2F;slatian_bean&amp;#x2F;slatian_bean_happy.png&quot; alt=&quot;Slatian interjects:&quot; class=&quot;chat-avatar&quot; width=&quot;64&quot; height=&quot;64&quot;&gt;
	&lt;blockquote&gt;
&lt;p&gt;&lt;b&gt;Future Slatian here:&lt;&#x2F;b&gt; I was actually pretty unsure about this post and fell into a perfectionism trap which is why it is a quarter of a year late. The weighting of the text is off and doesn&#x27;t really match my real experience and some things just feel rough. But it&#x27;s real text of a real human experience written by a real human who overthought it without actually improving it. So without any further improvements, here it is!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;&#x2F;div&gt;



&lt;p&gt;In case you missed it, I hosted two self organized sessions:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;events.ccc.de&#x2F;congress&#x2F;2025&#x2F;hub&#x2F;de&#x2F;event&#x2F;detail&#x2F;search-engine-creators-meetup&quot;&gt;Search Engine Creators Meetup&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;events.ccc.de&#x2F;congress&#x2F;2025&#x2F;hub&#x2F;de&#x2F;event&#x2F;detail&#x2F;search-engines-for-the-curious&quot;&gt;Search Engines for the Curious&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I&#x27;ll organise such meetups at other events too.&lt;&#x2F;p&gt;
&lt;p&gt;I also spent some time at the Digitalcourage assembly.&lt;&#x2F;p&gt;
&lt;p&gt;And visited the beings behind the &lt;em&gt;two&lt;&#x2F;em&gt; search engines that made searching the file shares at congress possible, both built at 39c3 itself.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-search-engine-creators-meetup&quot;&gt;The Search Engine Creators Meetup&lt;&#x2F;h2&gt;
&lt;p&gt;While shorter than I had hoped (Forgot the room reservation over Christmas) it was a success most beings who showed up were mostly interested in searching personal&#x2F;organisation knowledge bases. Since we didn&#x27;t get much past the introduction round I&#x27;ll try to paste the notes here with some annotations that I remember.&lt;&#x2F;p&gt;
&lt;p&gt;Technologies used by people who were present:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Elasticsearch (Mostly used in organisations, but nobody really liked it, and apparently it uses a &lt;strong&gt;lot&lt;&#x2F;strong&gt; of resources)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sqlite.org&#x2F;fts5.html&quot;&gt;SQLite fts5&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.meilisearch.com&#x2F;&quot;&gt;meilisearch&lt;&#x2F;a&gt; was one of the most used and loved search cores&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;quickwit-oss&#x2F;tantivy&quot;&gt;tantivy&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;manticoresearch.com&#x2F;&quot;&gt;Manticore&lt;&#x2F;a&gt; through its MySQL compatible interface (Fun fact: I first heard of Manticore in the talk &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;media.ccc.de&#x2F;v&#x2F;ds25-558-building-and-maintaining-large-scale-data-leak-databases&quot;&gt;Building and maintaining large-scale data leak databases&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;yacy.net&quot;&gt;YaCy&lt;&#x2F;a&gt; which uses &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;solr.apache.org&#x2F;&quot;&gt;Apache Solr&lt;&#x2F;a&gt; under the hood.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Main topics people were interested in:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Tokenizing (splitting text into words or word-like chunks)&lt;&#x2F;li&gt;
&lt;li&gt;Vector Search&lt;&#x2F;li&gt;
&lt;li&gt;Metadata&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Tokenizing was interesting because it starts easy (just split on spaces and special characters) and then get complicated pretty quickly, for example with compound words (very common in German for example). To drop some names of libraries that try to solve this: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;meilisearch&#x2F;charabia&quot;&gt;charabia&lt;&#x2F;a&gt; from meilisearch, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;tantivy&#x2F;latest&#x2F;tantivy&#x2F;tokenizer&#x2F;index.html&quot;&gt;tantivy&lt;&#x2F;a&gt; has tokenization infrastructure and I of course mentioned my own &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;unobtanium&#x2F;unobtanium-segmenter&quot;&gt;unobtanium-segmenter&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Vector Search was mentioned multiple times, but I don&#x27;t remember if any discussion arose from that but I&#x27;d like to hear of any unconventional implementations because my current opinion on vector search is that it depends on LLMs which will always have hidden baked in assumptions (intentional and unintentional) that may not align with ones mission of building an independent search system.&lt;&#x2F;p&gt;
&lt;p&gt;Metadata took the most time of the discussion and the main points were:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Visible Metadata (i.e. OpenGraph previews) is way better maintained than invisible Metadata.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;title&amp;gt;&lt;&#x2F;code&gt; elements sometimes are crap because apparently browser tabs aren&#x27;t that visible.&lt;&#x2F;li&gt;
&lt;li&gt;Semantic Web (JSON-LD) and Semantic Markup are loved, but unfortunately invisible Metadata. Someone mentioned &quot;usually that depends on person putting in the work&quot; with the consequence that its quality has a huge dependency on site and timeframe.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Someone also had a project to make OpenStreetMap searchable in an unusual way (I unfortunately forgot who that was).&lt;&#x2F;p&gt;
&lt;p&gt;In case you want to connect: There is the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lists.riseup.net&#x2F;www&#x2F;subscribe&#x2F;search-collab&quot;&gt;search-collab Mailing list&lt;&#x2F;a&gt; for beings interested in building search engines and exchanging knowledge about search engines.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-ask-me-anything&quot;&gt;The ask me anything&lt;&#x2F;h2&gt;
&lt;p&gt;The ask me anything was great, the room at the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;39c3.c3nav.de&#x2F;l&#x2F;free-knowledge-habitat&#x2F;details&#x2F;&quot;&gt;Free Knowledge Habitat&lt;&#x2F;a&gt; was full despite the early hour (10:30 on day 2, thanks to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;39c3.c3nav.de&#x2F;l&#x2F;heaven&#x2F;details&#x2F;&quot;&gt;heaven&lt;&#x2F;a&gt; for supplying me with a cup of coffee).&lt;&#x2F;p&gt;
&lt;p&gt;The questions were mostly about search engines in general or about my specific search engine.&lt;&#x2F;p&gt;
&lt;p&gt;One very interesting question that I want to share here was:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Do you rank by accessibility?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This question is very good, because a search engine preferring more accessible sites is a very good incentive to improve web accessibility. The answer I had to give was an unfortunate &quot;no&quot;, but in general search engines also make use of some accessibility features so there is a bit of a coincidence in what is accessible to the search engine and what is accessible to humans, but no accessibility ranking.&lt;&#x2F;p&gt;
&lt;p&gt;The answer I didn&#x27;t have ready is that the reality of making things accessible beyond the basics is multidimensional and complicated. There are automatic accessibility checks, but those alone without good intentions on the other end only incentivize cheating the test.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;meeting-c3search-and-c3ordnung&quot;&gt;Meeting c3search and c3ordnung&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve met the creators of both &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;events.ccc.de&#x2F;congress&#x2F;2025&#x2F;hub&#x2F;de&#x2F;project&#x2F;detail&#x2F;assembly&#x2F;c3search-full-advanced-file-search-engine&#x2F;&quot;&gt;c3search&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;m4rc3l&#x2F;ordnung&quot;&gt;c3ordnung&lt;&#x2F;a&gt;. The two search engines that were created for the fileshares at congress.&lt;&#x2F;p&gt;
&lt;p&gt;By coincidence both use meilisearch and had a three step crawl, index, search pipeline with no long term persistence between crawling and indexing. They&#x27;re both written very quickly which is why there is no source code available for c3search (yet?).&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, if you&#x27;re reading this: It has been nice to meet you!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;meeting-digitalcourage&quot;&gt;Meeting Digitalcourage&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve made some noise at the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;digitalcourage.de&#x2F;&quot;&gt;Digitalcourage&lt;&#x2F;a&gt; assembly about Europe currently being without a sovereign search engine and had the joy to exchange some knowledge about this with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;digitalcourage.de&#x2F;agentur&#x2F;personen&#x2F;rena-tangens&quot;&gt;Rena Tangens&lt;&#x2F;a&gt; (Huge thanks for taking the time!).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;&quot;No sovereign European search engine&quot;:&lt;&#x2F;b&gt; There are currently 4 search engines on this planet that are truly large enough to be useful as go-to search engines for most of the world: Google, Bing, Yandex, Baidu … you may have noticed that all of those are located in countries we as Europeans shouldn&#x27;t trust nowadays. The only large European search engine is Mojeek, but it&#x27;s more on the scale of largest of the small ones.&lt;&#x2F;p&gt;
&lt;p&gt;Turns out that this has already been a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;digitalcourage.de&#x2F;blog&#x2F;2017&#x2F;google-strafzahlung-fuer-offenen-suchindex-nutzen&quot;&gt;topic at Digitalcourage way back in 2017&lt;&#x2F;a&gt; and I want contact the people behind the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;openwebindex.eu&#x2F;&quot;&gt;Open Web Index&lt;&#x2F;a&gt;, even though the project seems to be mainly aimed at academics.&lt;&#x2F;p&gt;
&lt;p&gt;Update: I kind of did contact them, though as things go a lot of distractions showed up and I haven&#x27;t gone further on this end yet.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;resources-discovered&quot;&gt;Resources Discovered&lt;&#x2F;h2&gt;




	


&lt;dl class=&quot;&quot;&gt;

	
		&lt;dt&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;madata.bib.uni-mannheim.de&#x2F;207&#x2F;&quot;&gt;Web Data Commons - University of Mannheim&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;dl.acm.org&#x2F;doi&#x2F;10.1145&#x2F;2872518.2889386&quot;&gt;&quot;A large public corpus of web tables containing time and context metadata&quot;&lt;&#x2F;a&gt; tables an in HTML tables, that contain useful data. Someone dropped this at the ask me anything. Thank you!
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;openwebindex.eu&#x2F;&quot;&gt;Open Web Index&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		is a project by the University of Passau and the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;opensearchfoundation.org&#x2F;&quot;&gt;Open Search Foundation&lt;&#x2F;a&gt;, I knew this one before, but dismissed it, thanks to Rena Tangens for bringing this to my attention. The best description of the open web index is: A research focused CommonCrawl made in Europe that goes an extra &lt;s&gt;mile&lt;&#x2F;s&gt; kilometre.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;uni-tuebingen.de&#x2F;en&#x2F;faculties&#x2F;faculty-of-humanities&#x2F;departments&#x2F;modern-languages&#x2F;department-of-linguistics&#x2F;chairs&#x2F;general-and-computational-linguistics&#x2F;ressources&#x2F;lexica&#x2F;germanet&#x2F;&quot;&gt;GermaNet&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		a German version of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wordnet.princeton.edu&#x2F;&quot;&gt;WordNet&lt;&#x2F;a&gt; made at the University of Tübingen.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;opus4.kobv.de&#x2F;opus4-hs-augsburg&#x2F;frontdoor&#x2F;index&#x2F;index&#x2F;docId&#x2F;2838&quot;&gt;Website Taxonomy Extraction – Identifying and Extracting Navigation Structures from Websites&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		An interesting paper about extracting menu structure from HTML to make a whole new dimension of pages machine readable, discoverable and searchable.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Modern Web Search: Not Just an Index</title>
        <published>2025-12-26T00:00:00+00:00</published>
        <updated>2026-01-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/not-just-an-index/"/>
        <id>https://slatecave.net/blog/not-just-an-index/</id>
        
        <summary type="text">Why an index isn&#x27;t enough for a modern search engine.</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/not-just-an-index/">&lt;h2 id=&quot;what-this-post-is-about&quot;&gt;What this post is about&lt;&#x2F;h2&gt;
&lt;p&gt;This post is abut the claim of:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;We need a sovereign European search index!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The short answer is: Yes, absolutely! But a search index alone won&#x27;t be enough.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; I&#x27;m living in Europe so I&#x27;ve started this with the European version of the claim. But I hope that it is quite obvious that everyone deserves a sovereign and local search engine, this is a global problem, not just a European one.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-a-search-index&quot;&gt;What is a search index?&lt;&#x2F;h2&gt;
&lt;p&gt;To show why am index is not enough we have to first answer what an index is:&lt;&#x2F;p&gt;
&lt;p&gt;A search index is a database of documents (web pages) reorganised in a way that they can be queried in an efficient way by their contents (i.e. the search term &quot;cat&quot; matches all documents that contain the word &quot;cat&quot; or &quot;cats&quot;). It can also include functionality that allows querying which words follow each other, or how important some arbitrary scoring algorithm determined a word or phrase to be.&lt;&#x2F;p&gt;
&lt;p&gt;So an index is the basic functionality of what one thinks when someone wants to build a search engine.&lt;&#x2F;p&gt;
&lt;p&gt;While this on a small scale is already pretty useful, in the context of a large database with a diverse set of topics or languages it isn&#x27;t, you need some extra components.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-do-we-need-something-else-too&quot;&gt;Why do we need something else too?&lt;&#x2F;h2&gt;
&lt;p&gt;The problem with the search index is that it can search for words, but doesn&#x27;t neither considers the fact, that multiple, very different words can mean the same thing nor the fact that the same word can mean multiple things depending on the wider context. Those are two problems, which require different solutions, that rely on other components.&lt;&#x2F;p&gt;
&lt;p&gt;The rest of the article will go through them component by component.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;forbidden-knowledge&quot;&gt;&quot;Forbidden Knowledge&quot;&lt;&#x2F;h2&gt;
&lt;p&gt;The web of today is full of sites that intentionally or not don&#x27;t follow web standards in their structure and content, to avoid filling the index with useless data the crawlers collecting the data usually cheat by having additional Knowledge about popular content management systems, wiki engines and services baked in to know what is worth visiting and what isn&#x27;t, even though the information isn&#x27;t communicated through a mechanisms like &lt;code&gt;robots.txt&lt;&#x2F;code&gt; or &lt;code&gt;sitemap.xml&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Big search engines either guess that information or collect it via their proprietary web admin tools.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-secondary-index&quot;&gt;A Secondary Index&lt;&#x2F;h2&gt;
&lt;p&gt;Every mainstream search engine nowadays isn&#x27;t one, but at least two search engines. One with the index of all known web pages, one carefully curated, that is displayed in the form of infoboxes from Wikipedia, answers from Stack Overflow (or somewhere else) or dictionary lookups.&lt;&#x2F;p&gt;
&lt;p&gt;Without those the sources that are known to reliably provide useful results would often be drowned by other pages that may be very relevant, but not what someone may be looking for.&lt;&#x2F;p&gt;
&lt;p&gt;This is &lt;strong&gt;not&lt;&#x2F;strong&gt; about LLM (&quot;AI&quot;) generated answers, those are not a part of the searching machinery.&lt;&#x2F;p&gt;
&lt;p&gt;There is an ethical dimension to this on&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;who gets included in the this exclusive clubs and why&lt;&#x2F;li&gt;
&lt;li&gt;the search engine turning into a question answering machine that doesn&#x27;t give the websites the traffic they deserve (which could be its own blogpost series)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Both of these points are worth putting more through into them and a search engine should be transparent about them.&lt;&#x2F;p&gt;
&lt;p&gt;Secondary uses of the secondary index may be to have a reference dataset of texts that can be used to tune heuristics for the main index.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-dictionary-thesaurus&quot;&gt;A Dictionary&#x2F;Thesaurus&lt;&#x2F;h2&gt;
&lt;p&gt;The &quot;multiple words can mean the same thing&quot; can be solved pretty easily by a synonym lookup, for that one needs a &lt;strong&gt;dictionary&lt;&#x2F;strong&gt; of known (important: not all possible words are known) words, which can then be used to broaden a search with little results or uprank results that also mention synonyms.&lt;&#x2F;p&gt;
&lt;p&gt;This dictionary can also be used to improve language guessing. This is necessary because it is unfortunately not uncommon, that the language annotation of the website itself can&#x27;t be relied on, as it is often hard coded in templates or an overlooked setting. An important consideration here is that the same sequence of characters can be a valid word in multiple languages.&lt;&#x2F;p&gt;
&lt;p&gt;Another use for a dictionary is the classic of resolving spelling errors and also normalising alternate valid spellings (i.e. American English and British English).&lt;&#x2F;p&gt;
&lt;p&gt;Often the dictionary becomes visible as part of an autocompletion or query spellchecking feature.&lt;&#x2F;p&gt;
&lt;p&gt;Prominent examples that are usable for this purpose are &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiktionary.org&quot;&gt;Wiktionary&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wordnet.princeton.edu&quot;&gt;WordNet&lt;&#x2F;a&gt;. There is also &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;uni-tuebingen.de&#x2F;en&#x2F;faculties&#x2F;faculty-of-humanities&#x2F;departments&#x2F;modern-languages&#x2F;department-of-linguistics&#x2F;chairs&#x2F;general-and-computational-linguistics&#x2F;ressources&#x2F;lexica&#x2F;germanet&#x2F;&quot;&gt;GermaNet&lt;&#x2F;a&gt; for the german language.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-knowledge-graph&quot;&gt;A Knowledge Graph&lt;&#x2F;h2&gt;
&lt;p&gt;While words can mean a lot, word and dictionary definitions don&#x27;t make up reality. To &quot;solve&quot; the problem of what exactly a usually very short query is asking for the search engine needs a Knowledge Graph that can disambiguate a very short query string into a precise query that the index can give an answer for, resolving name and word collisions and determining the meaningful words in the query.&lt;&#x2F;p&gt;
&lt;p&gt;The knowledge graph is the most invisible of the additional components, though it powers things like query suggestions, infoboxes, may be intertwined with the secondary index and so on.&lt;&#x2F;p&gt;
&lt;p&gt;The most complete general purpose and freely licensed knowledge graph is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wikidata.org&quot;&gt;Wikidata&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;geocoding&quot;&gt;Geocoding&lt;&#x2F;h2&gt;
&lt;p&gt;A lot of things humans search for day to day are location related, which is why Geocoding is not only important for finding the place &quot;near me&quot;, but also as an useful extension to the dictionary and knowledge graph. This is useful even without a mapping feature.&lt;&#x2F;p&gt;
&lt;p&gt;Usable datasources are &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.geonames.org&quot;&gt;GeoNames&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nominatim.org&quot;&gt;OpenStreetMap Nominatim&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;cheating&quot;&gt;&quot;Cheating&quot;&lt;&#x2F;h2&gt;
&lt;p&gt;There are a few ways to &quot;cheat&quot; and omit some of those components, but those come with downsides.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;single-domain-single-language-index&quot;&gt;Single Domain, single language Index&lt;&#x2F;h3&gt;
&lt;p&gt;Limiting the index scope to a single knowledge domain and a single or a few related languages will greatly reduce the opportunities for word collisions and thereby the need for a dictionary.&lt;&#x2F;p&gt;
&lt;p&gt;This will limit the how large the index can grow.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;llm-embedding&quot;&gt;LLM Embedding&lt;&#x2F;h3&gt;
&lt;p&gt;Another common way to model meaning that is independent of the word used to express it is by generating embeddings using an LLM and building an index with a vector database.&lt;&#x2F;p&gt;
&lt;p&gt;This approach is very simple in terms of writing code as this a usecase that already has existing software (Often labelled as &quot;semantic search&quot;).&lt;&#x2F;p&gt;
&lt;p&gt;However, when using it one should acknowledge, that the LLM is a large blob, that is difficult or impossible to verify or reproduce. They have a lot of hidden and unchangeable assumptions baked in. Those assumptions will affect search results and aren&#x27;t really that great for a sovereign search index.&lt;&#x2F;p&gt;
&lt;p&gt;Another challenge with embeddings is the compute power needed to generate them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tl-dr&quot;&gt;TL;DR&lt;&#x2F;h2&gt;
&lt;p&gt;For building a modern search engine an index alone isn&#x27;t enough.&lt;&#x2F;p&gt;
&lt;p&gt;There are a few ways to cheat, but either with scaling limits or by giving up sovereignty.&lt;&#x2F;p&gt;
&lt;p&gt;One also needs curated secondary indices, dictionaries and a knowledge graph to resolve a query in the searchbox to an actual, better query that will generate the actual results.&lt;&#x2F;p&gt;
&lt;p&gt;There are already projects out there that solve part of the problem, search engines, dictionaries, knowledge graphs.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s build the future, together!&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Being a Polite Crawler on the Web</title>
        <published>2025-09-07T00:00:00+00:00</published>
        <updated>2025-09-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/being-a-polite-crawler-on-the-web/"/>
        <id>https://slatecave.net/blog/being-a-polite-crawler-on-the-web/</id>
        
        <summary type="text">What I&#x27;ve learned from running my own web search crawler</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/being-a-polite-crawler-on-the-web/">&lt;h2 id=&quot;why-be-polite-on-the-web&quot;&gt;Why be Polite on the Web?&lt;&#x2F;h2&gt;







	
		
		
		
		
		
			
			
		
	

	
		
	

	
		
	

	
		
	


	
&lt;div class=&quot;chat-box&quot;&gt;
	&lt;img src=&quot;&amp;#x2F;resources&amp;#x2F;emoji&amp;#x2F;slatian_bean&amp;#x2F;slatian_bean.png&quot; alt=&quot;Slatian notes&quot; class=&quot;chat-avatar&quot; width=&quot;64&quot; height=&quot;64&quot;&gt;
	&lt;blockquote&gt;
&lt;p&gt;You might be aware, that I&#x27;m building &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;unobtanium.rocks&quot;&gt;my own search engine&lt;&#x2F;a&gt; for general purpose web search. Most of these are learnings from efficiency improvements and experience from hosting my own web services.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Also there should be no need to convince anyone that a bit of politeness is a good thing.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;&#x2F;div&gt;



&lt;p&gt;Over politeness being a pretty good default there are a few other convincing reasons to be polite:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		Efficiency
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Being polite also means cooperating with the Server on the other end, which in almost all cases will result in your crawler being faster and more efficient.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Reputation
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		If your crawler gets known as the one who isn&#x27;t following basic rules crawling will become more difficult, as websites handle your crawler as an unwelcome visitor.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;what-is-scraping-and-what-does-a-crawler-do&quot;&gt;What is Scraping and what does a Crawler do?&lt;&#x2F;h2&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		Scraping
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Taking information that is intended for Human viewing (like an HTML page) and (trying to) extract machine readable information from it. Usually it is used to work around bad or non-existent interfaces for automatic information retrieval. Scraping is useful for something like a search tool or link previews.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Crawling
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Loop of fetching a document (i.e. a web page), extracting links to other documents from that and then repeating that for the newly discovered document links. The goal of crawling is to collect the found documents to extract data from them.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;The two are usually used together as the link extraction in a crawler is usually done by scraping, that is extracting the human readable links. The &quot;getting data from documents&quot; step after crawling will also involve scraping to some degree.&lt;&#x2F;p&gt;
&lt;p&gt;The two are independent: One can build a crawler without a scraper, by targeting an API or a scraper without a crawler if no document discovery mechanism is needed (i.e. a link preview).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;identify-your-crawler&quot;&gt;Identify Your Crawler&lt;&#x2F;h2&gt;
&lt;p&gt;Identify your crawler so that the Admin on the other side knows who is crawling what and why. Crude attempts at trying to look like a Browser probably won&#x27;t last long. This is mainly because your crawler has a very different goal from the average website visitor.&lt;&#x2F;p&gt;
&lt;p&gt;Set the HTTP &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Reference&#x2F;Headers&#x2F;User-Agent&quot;&gt;&lt;code&gt;User-Agent&lt;&#x2F;code&gt; header&lt;&#x2F;a&gt; to a name containing the name of your site or project and include a link to an information page that explains in more detail:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;who owns the crawler&lt;&#x2F;li&gt;
&lt;li&gt;contact information&lt;&#x2F;li&gt;
&lt;li&gt;why he crawler exists&lt;&#x2F;li&gt;
&lt;li&gt;a short explanation of how the crawler works&lt;&#x2F;li&gt;
&lt;li&gt;how to opt out of crawling&lt;&#x2F;li&gt;
&lt;li&gt;which control mechanisms are available&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;An example user agent could be: &lt;code&gt;ExampleBot (https:&#x2F;&#x2F;example.org&#x2F;about&#x2F;example-bot)&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;crawl-at-a-reasonable-speed&quot;&gt;Crawl at a Reasonable Speed&lt;&#x2F;h2&gt;
&lt;p&gt;Crawling too fast can — depending on what the Server is doing — degrade the Service for others because on smaller services your crawler could be responsible for a significant amount of load, even with what might seem like not a lot of requests to you.&lt;&#x2F;p&gt;
&lt;p&gt;Determining how fast is still okay can be difficult, treating every origin the same with a fixed delay of a few seconds between requests will work pretty well.&lt;&#x2F;p&gt;
&lt;p&gt;The best way to find out what is acceptable is to read the &lt;code&gt;Crawl-Delay&lt;&#x2F;code&gt; from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;robotstxt.org&#x2F;&quot;&gt;robots.txt&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Note though, that blindly trusting the server on this value might not be desirable either as the delay can end up being hours or even days this way. Capping this at a delay of 2 minutes or more should be a reasonable compromise though.&lt;&#x2F;p&gt;
&lt;p&gt;Slow down if your crawler encounters a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Reference&#x2F;Status&#x2F;429&quot;&gt;&lt;code&gt;429&lt;&#x2F;code&gt; (too many requests)&lt;&#x2F;a&gt; code. They sometimes come with a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Reference&#x2F;Headers&#x2F;Retry-After&quot;&gt;&lt;code&gt;Retry-After&lt;&#x2F;code&gt; header&lt;&#x2F;a&gt; that tells your crawler how long the server wants it to wait until the next request.&lt;&#x2F;p&gt;
&lt;p&gt;Another mechanism one can implement is a dynamic delay based on a multiple of the response time. This way, if the server responds slower, the crawler also slows down preventing it from overwhelming small or busy web hosts:&lt;&#x2F;p&gt;

	

&lt;ul class=&quot;link-list&quot;&gt;
&lt;li&gt;&lt;a class=&quot;decoration-destination-text&quot; rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stract.com&#x2F;webmasters&quot;&gt;Politeness mechanism in Stract&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;decoration-destination-text&quot; rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.unobtanium.rocks&#x2F;algorithm&#x2F;crawl-delay&#x2F;&quot;&gt;Adapted politeness mechanism in Unobtanium&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;don-t-scrape-what-you-could-get-through-an-api&quot;&gt;Don&#x27;t Scrape what You could get Through an API&lt;&#x2F;h2&gt;
&lt;p&gt;If you only need specific information that is available using a well documented API, strongly consider querying that API instead of scraping web pages.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;don-t-crawl-things-you-shouldn-t&quot;&gt;Don&#x27;t Crawl Things you shouldn&#x27;t&lt;&#x2F;h2&gt;
&lt;p&gt;While crawling the things you shouldn&#x27;t crawl seems interesting and appealing it really isn&#x27;t. In fact you probably want to crawl even less than you are allowed to.&lt;&#x2F;p&gt;
&lt;p&gt;Possible reasons for a Server to advise crawlers not to crawl certain sections are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;They don&#x27;t want to be indexed (which is their right and they&#x27;re politely asking you to leave)&lt;&#x2F;li&gt;
&lt;li&gt;There is a (near) infinite labyrinth of automatically generated pages somewhere. Crawling this would waste resources on both, the Server and your crawler.&lt;&#x2F;li&gt;
&lt;li&gt;They are crawler traps that will lock you out if you send a request to them.&lt;&#x2F;li&gt;
&lt;li&gt;They are access denied pages&lt;&#x2F;li&gt;
&lt;li&gt;They contain large files that will storage space without much benefit on the crawler side.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Your crawler can get the information of which paths it shouldn&#x27;t crawl from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;robotstxt.org&#x2F;robotstxt.html&quot;&gt;robots.txt&lt;&#x2F;a&gt;. For matching the user agent you should use the same crawler name you have set in the &lt;code&gt;User-Agent&lt;&#x2F;code&gt; header.&lt;&#x2F;p&gt;
&lt;p&gt;Same goes for the HTML &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTML&#x2F;Reference&#x2F;Elements&#x2F;meta&#x2F;name&#x2F;robots&quot;&gt;&lt;code&gt;&amp;lt;meta name=&quot;robots&quot; …&lt;&#x2F;code&gt; tag&lt;&#x2F;a&gt;, this one won&#x27;t tell you in advance, but may be a useful additional tool for detecting content that shouldn&#x27;t be indexed.&lt;&#x2F;p&gt;
&lt;p&gt;There is also the HTTP &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Reference&#x2F;Headers&#x2F;X-Robots-Tag&quot;&gt;&lt;code&gt;X-Robots-Tag&lt;&#x2F;code&gt; header&lt;&#x2F;a&gt; which works similar to the HTML robots meta element, but has slightly different syntax.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Opinion:&lt;&#x2F;b&gt; Implementing &lt;code&gt;X-Robots-Tag&lt;&#x2F;code&gt; is good for completeness but not a strict requirement for a new crawler that &lt;em&gt;only indexes HTML&lt;&#x2F;em&gt;. &lt;code&gt;robots.txt&lt;&#x2F;code&gt; and &lt;code&gt;&amp;lt;meta name=&quot;robots&quot; …&lt;&#x2F;code&gt; are the essential ones to support.&lt;&#x2F;p&gt;
&lt;p&gt;Refetching the &lt;code&gt;robots.txt&lt;&#x2F;code&gt; file after crawling for multiple minutes is a good idea, as that allows the admin of the target server to update the crawling preferences when they notice that the crawler is going where it shouldn&#x27;t.&lt;&#x2F;p&gt;
&lt;p&gt;See also:&lt;&#x2F;p&gt;

	

&lt;ul class=&quot;link-list&quot;&gt;
&lt;li&gt;&lt;a class=&quot;decoration-destination-text&quot; rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Glossary&#x2F;Robots.txt&quot;&gt;MDN on Robots.txt and other exclusion mechanisms&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;decoration-destination-text&quot; rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;datatracker.ietf.org&#x2F;doc&#x2F;html&#x2F;rfc9309&quot;&gt;RFC 9309: Robots Exclusion Protocol&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;only-crawl-what-you-need&quot;&gt;Only Crawl what You Need&lt;&#x2F;h2&gt;
&lt;p&gt;Do not fall for &quot;I might need it later …&quot;: no you don&#x27;t.&lt;&#x2F;p&gt;
&lt;p&gt;If you didn&#x27;t plan for it, you&#x27;ll probably save incomplete or the wrong information or likely have discarded what you didn&#x27;t need to make space for other things.&lt;&#x2F;p&gt;
&lt;p&gt;Crawling what you don&#x27;t need will cost you bandwidth, storage and time for information that will never get used or if it will be needed is out of date by then. Not crawling unnecessary information also means your crawler is faster because is has to do less work.&lt;&#x2F;p&gt;
&lt;p&gt;Best case is that you think about what information you need before setting up a crawler.&lt;&#x2F;p&gt;
&lt;p&gt;In case you do need historical information &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;commoncrawl.org&#x2F;&quot;&gt;Common Crawl&lt;&#x2F;a&gt; and the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;archive.org&quot;&gt;Internet Archive&lt;&#x2F;a&gt; are your friends.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;detect-what-your-crawler-already-knows&quot;&gt;Detect what Your Crawler Already Knows&lt;&#x2F;h2&gt;
&lt;p&gt;Requesting resources that the crawler already knows is wasting bandwidth and crawl time on stuff that you already have, remembering a few bits of information can help with avoiding that.&lt;&#x2F;p&gt;
&lt;p&gt;The first step is to split up crawling and analyzing data, this way you can iterate fast on working with the data while avoiding unnecessary network requests.&lt;&#x2F;p&gt;
&lt;p&gt;The continuation is remembering the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Reference&#x2F;Headers&#x2F;Last-Modified&quot;&gt;&lt;code&gt;Last-Modified&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Reference&#x2F;Headers&#x2F;ETag&quot;&gt;&lt;code&gt;ETag&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; headers and sending them back with the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Reference&#x2F;Headers&#x2F;If-Modified-Since&quot;&gt;&lt;code&gt;If-Modified-Since&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Reference&#x2F;Headers&#x2F;If-None-Match&quot;&gt;&lt;code&gt;If-None-Match&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;. If the resource didn&#x27;t change the Server will reply with a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Reference&#x2F;Status&#x2F;304&quot;&gt;&lt;code&gt;304&lt;&#x2F;code&gt; (not modified)&lt;&#x2F;a&gt; if the content didn&#x27;t change.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;use-head-requests&quot;&gt;Use &lt;code&gt;HEAD&lt;&#x2F;code&gt; Requests&lt;&#x2F;h2&gt;
&lt;p&gt;In case your crawler isn&#x27;t sure if it needs a resource or not, but could distinguish the two by the HTTP header information alone, use the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Reference&#x2F;Methods&#x2F;HEAD&quot;&gt;HTTP &lt;code&gt;HEAD&lt;&#x2F;code&gt; method&lt;&#x2F;a&gt;, which will return the headers like for a &lt;code&gt;GET&lt;&#x2F;code&gt; request, but won&#x27;t return the content.&lt;&#x2F;p&gt;
&lt;p&gt;This is useful if some heuristic tells the crawler that a file may not be interesting, but not for sure (i.e. a path ending in.png is likely an image, but could also be a page about the image file), after evaluating the &lt;code&gt;HEAD&lt;&#x2F;code&gt; the crawler can always use a &lt;code&gt;GET&lt;&#x2F;code&gt; request if the content seems interesting.&lt;&#x2F;p&gt;
&lt;p&gt;Relevant headers one might evaluate for head requests are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Reference&#x2F;Headers&#x2F;Content-Type&quot;&gt;&lt;code&gt;Content-Type&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Reference&#x2F;Headers&#x2F;Content-Length&quot;&gt;&lt;code&gt;Content-Length&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;be-smart-about-url-queries&quot;&gt;Be Smart about URL Queries&lt;&#x2F;h2&gt;
&lt;p&gt;URLs with the query part (after the first &lt;code&gt;?&lt;&#x2F;code&gt;) set often link slight variations of pages that simply aren&#x27;t useful to crawl.&lt;&#x2F;p&gt;
&lt;p&gt;In practice the sane default is to not crawl URLs with the query part set and define exceptions from that based on known site structure and carefully chosen heuristics.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;do-have-fun-do-be-curious&quot;&gt;Do have fun! Do be curious!&lt;&#x2F;h2&gt;
&lt;p&gt;To summarize:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Communicate who is crawling why&lt;&#x2F;li&gt;
&lt;li&gt;Crawl as fast as you can, but stay as slow as necessary&lt;&#x2F;li&gt;
&lt;li&gt;Read and respect &lt;code&gt;robots.txt&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Only crawl what you need&lt;&#x2F;li&gt;
&lt;li&gt;Split up crawling and analyzing&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you&#x27;ve read this far past a lot of &quot;don&#x27;t&quot;s, now is the time for the &quot;do&quot;s.&lt;&#x2F;p&gt;
&lt;p&gt;Writing a good crawler is hard, but writing an okay one is actually quite easy. The fact alone you have put a thought to how to make a polite crawler will probably lead to better code.&lt;&#x2F;p&gt;
&lt;p&gt;Be curious, mix in a bit of politeness and have fun with the web!&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>A Forgejo-Runner with Podman on Alpine</title>
        <published>2025-05-24T00:00:00+00:00</published>
        <updated>2026-01-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/runner-with-podman-on-alpine/"/>
        <id>https://slatecave.net/blog/runner-with-podman-on-alpine/</id>
        
        <summary type="text">A guide on how to install a Forgejo-runner using rootless Podman.</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/runner-with-podman-on-alpine/">&lt;h2 id=&quot;what-this-is-about&quot;&gt;What this is about&lt;&#x2F;h2&gt;
&lt;p&gt;This is a guide on how to install a Forgejo-runner on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alpinelinux.org&#x2F;&quot;&gt;alpine Linux&lt;&#x2F;a&gt; using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;containers&#x2F;podman&#x2F;blob&#x2F;main&#x2F;docs&#x2F;tutorials&#x2F;rootless_tutorial.md&quot;&gt;rootless Podman&lt;&#x2F;a&gt; running as its own user.&lt;&#x2F;p&gt;







	
		
		
		
		
		
			
			
		
	

	
		
	

	
		
	

	
		
	

	
		
	

	
		
	

	
		
	

	
		
	


	
&lt;div class=&quot;chat-box&quot;&gt;
	&lt;img src=&quot;&amp;#x2F;resources&amp;#x2F;emoji&amp;#x2F;slatian_bean&amp;#x2F;slatian_bean.png&quot; alt=&quot;Slatian explains&quot; class=&quot;chat-avatar&quot; width=&quot;64&quot; height=&quot;64&quot;&gt;
	&lt;blockquote&gt;
&lt;p&gt;&lt;b&gt;Don&#x27;t let the length of this post scare you.&lt;&#x2F;b&gt; most of it is explaining why this guide works which should make catching mistakes easier.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;I&#x27;ll assume you have root access to the machine to install dependencies, service scripts and for creating users.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The installation here will use the runit service supervisor, as it is available on every distribution and easy to learn.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;This guide is structured, with a future you, who has &lt;b&gt;multiple runners&lt;&#x2F;b&gt; (i.e. for another codeforge, organisation, friends, …) in mind. You&#x27;ll find hints for repeating this guide.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;&#x2F;div&gt;



&lt;p&gt;We will:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;runner-with-podman-on-alpine&#x2F;#installing-the-needed-dependencies&quot;&gt;Install the needed dependencies&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;runner-with-podman-on-alpine&#x2F;#creating-a-user-for-the-runner&quot;&gt;Add a user and configure subuid and subgid&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;runner-with-podman-on-alpine&#x2F;#podman-as-a-service&quot;&gt;Set up Podman as a service&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;runner-with-podman-on-alpine&#x2F;#configuring-the-forgejo-runner&quot;&gt;Configure the runner&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;runner-with-podman-on-alpine&#x2F;#the-runner-service&quot;&gt;Set up the runner as a service&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;runner-with-podman-on-alpine&#x2F;#testing-the-runner&quot;&gt;Test the runner&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;runner-with-podman-on-alpine&#x2F;#enabling-podman-in-podman&quot;&gt;Enable Podman in Podman (optional)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;installing-the-needed-dependencies&quot;&gt;Installing the Needed Dependencies&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;b&gt;Community repository needed:&lt;&#x2F;b&gt; Some of these packages come from the alpine community repository. Uncomment the relevant line in &lt;code&gt;&#x2F;etc&#x2F;apk&#x2F;repositories&lt;&#x2F;code&gt; and run &lt;code&gt;apk update&lt;&#x2F;code&gt; to enable it.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;apk&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; add&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; forgejo-runner&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; podman&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; slirp4netns&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; runit&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sudo&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; iptables&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;b&gt;If you want to use Podman inside your CI:&lt;&#x2F;b&gt; Add the &lt;code&gt;fuse&lt;&#x2F;code&gt; package if you want to enable Podman in Podman for your runner (i.e. for building containers).&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;slirp4netns&lt;&#x2F;code&gt; package is needed for networking with rootless Podman.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;sudo&lt;&#x2F;code&gt; is needed to switch users, we&#x27;ll also use it as part of the runit services to make configuration easier.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;iptables&lt;&#x2F;code&gt; is needed by Podman for networking.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;enabling-cgroups&quot;&gt;Enabling Cgroups&lt;&#x2F;h2&gt;
&lt;p&gt;Cgroups are needed by container runtimes like Podman, to enable the on alpine:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Start cgroups&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;&#x2F;etc&#x2F;init.d&#x2F;cgroups&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; start&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Make sure it gets started on every boot&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;rc-update&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; add&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; cgroups&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Should say that it is &amp;quot;started&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;&#x2F;etc&#x2F;init.d&#x2F;cgroups&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; status&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Cgroups are a kernel feature, but alpine treats them as if they were a service to make them more convenient to manage.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;starting-runit&quot;&gt;Starting runit&lt;&#x2F;h3&gt;
&lt;p&gt;After installation we have to tell OpenRC to start runit and do that on every boot:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Start runit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;&#x2F;etc&#x2F;init.d&#x2F;runitd&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; start&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Make sure it gets started on every boot&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;rc-update&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; add&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; runitd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Should say that it is &amp;quot;started&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;&#x2F;etc&#x2F;init.d&#x2F;runitd&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; status&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;creating-a-user-for-the-runner&quot;&gt;Creating a User for the Runner&lt;&#x2F;h2&gt;
&lt;p&gt;For the runner we&#x27;ll create a regular user account without any special access. For this example I&#x27;m calling the user &lt;code&gt;runner-for-someone&lt;&#x2F;code&gt;, you can of course use your own name, make sure you always replace &lt;code&gt;runner-for-someone&lt;&#x2F;code&gt; with that.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;adduser&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;D&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; runner-for-someone&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;-D&lt;&#x2F;code&gt; option will disable password login for the just created user, there will be no need to log into it directly.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Use a naming convention:&lt;&#x2F;b&gt; Using the &lt;code&gt;runner-for-…&lt;&#x2F;code&gt; naming scheme makes it easier to keep track of which user accounts are for runners and who the runners are for. It is not a requirement, but choosing a naming convention and sticking with it helps in the long run.&lt;&#x2F;p&gt;
&lt;p&gt;Now the important part is to add subuids and subgids.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;configure-subuids&quot;&gt;Configure SubUIDs&lt;&#x2F;h3&gt;
&lt;p&gt;In the file &lt;code&gt;&#x2F;etc&#x2F;subuid&lt;&#x2F;code&gt; add the following line to reserve &lt;code&gt;99999&lt;&#x2F;code&gt; user ids starting at user id &lt;code&gt;100000&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Important:&lt;&#x2F;b&gt; If there are already UIDs reserved as subuids in that range, pick a multiple of 100000 where the next 99999 UIDs aren&#x27;t reserved (i.e. 500000)).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Why multiples of 100000?&lt;&#x2F;b&gt; Reserving multiples of 100000 isn&#x27;t a requirement, but it makes it very easy to keep track of reservations.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;runner-for-someone:100000:99999&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;configure-subgids&quot;&gt;Configure SubGIDs&lt;&#x2F;h3&gt;
&lt;p&gt;And finally add the same line you added to &lt;code&gt;&#x2F;etc&#x2F;subuid&lt;&#x2F;code&gt; to &lt;code&gt;&#x2F;etc&#x2F;subgid&lt;&#x2F;code&gt; too.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Is reserving the same range a requirement?&lt;&#x2F;b&gt; Using the same range for SubUIDs and SubGIDs isn&#x27;t required, but again, it makes it easier to keep track of allocations.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;testing-podman&quot;&gt;Testing Podman&lt;&#x2F;h2&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;iu&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; runner-for-someone&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; podman&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; run&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-rm&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; alpine&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;It works!&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This should run the &lt;code&gt;echo &quot;It works!&quot;&lt;&#x2F;code&gt; command inside a rootless container that runs under the &lt;code&gt;runner-for-someone&lt;&#x2F;code&gt; user on the host.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note on the sudo invocation:&lt;&#x2F;b&gt; The &lt;code&gt;-i&lt;&#x2F;code&gt; flag means the command is ran as if it was for an interactive session, which sets up some extra environment variables that Podman needs. The &lt;code&gt;-u runner-for-someone&lt;&#x2F;code&gt; part tells sudo to switch to the given user instead of root.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;If you get errors about insufficient SubUIDs and SubGIDs and have ran Podman before reserving the SubUIDs&#x2F;SubGIDs the following command should make Podman aware of the change and fix it.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;iu&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; runner-for-someone&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; podman&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; system&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; migrate&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;b&gt;If you&#x27;re here after creating a Podman service:&lt;&#x2F;b&gt; Remember to restart Podman using &lt;code&gt;sv restart &#x2F;etc&#x2F;service&#x2F;runner-for-someone_podman&lt;&#x2F;code&gt; for the fix to take effect in the runner.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;podman-as-a-service&quot;&gt;Podman as a Service&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we know, that Podman is working we can start it as a service using runit.&lt;&#x2F;p&gt;
&lt;p&gt;Actually, to make your life easier should you want multiple runners (i.e. for yourself, an organisation, a second account, …), we&#x27;ll create an easy to use template and then turn it into a service. One Podman service per user.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;I though Podman doesn&#x27;t require a service?&lt;&#x2F;b&gt; The &lt;code&gt;forgejo-runner&lt;&#x2F;code&gt; wants to talk to Podman using a Unix socket, this is why wee need a Podman service.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-podman-service-template&quot;&gt;The Podman Service Template&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;b&gt;Note if you are creating your second runner:&lt;&#x2F;b&gt; You only have to set up the templates once, if you already have set it up correctly you can skip to creating the service.&lt;&#x2F;p&gt;
&lt;p&gt;Create the directory &lt;code&gt;&#x2F;etc&#x2F;sv&#x2F;generic-forgejo-runner-podman&#x2F;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;and the file &lt;code&gt;&#x2F;etc&#x2F;sv&#x2F;generic-forgejo-runner-podman&#x2F;run&lt;&#x2F;code&gt; with the following content:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#!&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;&#x2F;bin&#x2F;sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; This parses the username from the directory name&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;USERNAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; basename&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-builtin&quot;&gt;pwd&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; cut&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;_&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; )&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; And this switches the user and runs the actual podman command&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function z-builtin&quot;&gt;exec&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sudo&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;iu&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;USERNAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        sh&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;c&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;exec podman system service --time=0 &amp;quot;unix:&#x2F;&#x2F;${HOME}&#x2F;podman.sock&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then run the following commands to make the script executable:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;chmod&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; +x&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;etc&#x2F;sv&#x2F;generic-forgejo-runner-podman&#x2F;run&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;what-the-service-template-does&quot;&gt;What the Service Template Does&lt;&#x2F;h3&gt;
&lt;p&gt;To explain what&#x27;s going on (feel free to skip to the next headline):&lt;&#x2F;p&gt;
&lt;p&gt;This template will be usable without any configuration, any service created from it should have a name that is the username it runs as, the first component followed by an &lt;code&gt;_podman&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;USERNAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; basename&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-builtin&quot;&gt;pwd&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; cut&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;_&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; )&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This first line makes use of the fact that with runit we can rely on the working directory being the one that is named after out service. The &lt;code&gt;basename &quot;$(pwd)&quot;&lt;&#x2F;code&gt; outputs the name of the directory we are in without the path and the &lt;code&gt;cut -d &#x27;_&#x27; -f 1&lt;&#x2F;code&gt; cuts the name at the underscore (&lt;code&gt;_&lt;&#x2F;code&gt;) and outputs what was before the underscore.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function z-builtin&quot;&gt;exec&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sudo&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;iu&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;USERNAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;sudo -iu &quot;$USERNAME&quot;&lt;&#x2F;code&gt; switches into the user account whose name ended up in the &lt;code&gt;USERNAME&lt;&#x2F;code&gt; variable in the first line simulating an interactive login to get some extra environment variables that Podman expects to exist.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;exec&lt;&#x2F;code&gt; at the start tells the shell to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jvns.ca&#x2F;blog&#x2F;2016&#x2F;10&#x2F;04&#x2F;exec-will-eat-your-brain&#x2F;&quot;&gt;&quot;replace&quot; itself&lt;&#x2F;a&gt; with the command instead of starting it as a child process. This is the simplest way to ensure that stopping the service will actually stop it and not leave some detached processes running in the background.&lt;&#x2F;p&gt;
&lt;p&gt;The backslash (&lt;code&gt;\&lt;&#x2F;code&gt;) at the end indicates that the next line is a continuation of this one instead of a new command. It is just there for readability.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;        sh&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;c&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;exec podman system service --time=0 &amp;quot;unix:&#x2F;&#x2F;${HOME}&#x2F;podman.sock&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This looks like a new command, but it is actually an argument to and ran by sudo in the context of the runner user.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;sh -c&lt;&#x2F;code&gt; runs whatever comes next as if it was a little shell script, so the next argument is actually another command.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function z-builtin&quot;&gt;exec&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; podman&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; system&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; service&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-time=0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;unix:&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;HOME&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;podman.sock&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Again &lt;code&gt;exec&lt;&#x2F;code&gt; at the start so that the shell replaces itself instead of getting in the way.&lt;&#x2F;p&gt;
&lt;p&gt;And our actual &lt;code&gt;podman system service&lt;&#x2F;code&gt; command that makes Podman reachable using a Unix socket in the home directory.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;--time=0&lt;&#x2F;code&gt; tells Podman to not time out when there is nothing to do.&lt;&#x2F;p&gt;
&lt;p&gt;The variable &lt;code&gt;${HOME}&lt;&#x2F;code&gt; gets evaluated now instead of in the previous steps because the whole command was wrapped in single quotes. This is important because only now &lt;code&gt;$HOME&lt;&#x2F;code&gt; is set to the runner users home instead of &lt;code&gt;&#x2F;root&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;







	
		
		
		
		
		
			
			
		
	

	
		
	

	
		
	

	
		
			
&lt;div class=&quot;chat-box&quot;&gt;
	&lt;img src=&quot;&amp;#x2F;resources&amp;#x2F;emoji&amp;#x2F;neofox&amp;#x2F;neofox_think.png&quot; alt=&quot;A curious neofox asks&quot; class=&quot;chat-avatar&quot; width=&quot;64&quot; height=&quot;64&quot;&gt;
	&lt;blockquote&gt;
&lt;p&gt;Why do this detour instead of putting the socket inside &lt;code&gt;&#x2F;home&#x2F;$USERNAME&#x2F;podman.sock&lt;&#x2F;code&gt; even though that would have the same result?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;&#x2F;div&gt;

		
		
		
		
		
			
			
		
	

	
		
	

	
		
	

	
		
	

	
		
	

	
		
			
&lt;div class=&quot;chat-box&quot;&gt;
	&lt;img src=&quot;&amp;#x2F;resources&amp;#x2F;emoji&amp;#x2F;slatian_bean&amp;#x2F;slatian_bean.png&quot; alt=&quot;Slatian explains&quot; class=&quot;chat-avatar&quot; width=&quot;64&quot; height=&quot;64&quot;&gt;
	&lt;blockquote&gt;
&lt;p&gt;Because &lt;code&gt;&#x2F;home&#x2F;$USERNAME&lt;&#x2F;code&gt; is only correct in the usual case, if you want to put the runner homes somewhere else in the future this will save quite some time and headaches.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Sometimes, being lazy requires a bit of effort.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;&#x2F;div&gt;

		
		
		
		
		
			
			
		
	

	
		
	

	
		
	

	
		
			
&lt;div class=&quot;chat-box&quot;&gt;
	&lt;img src=&quot;&amp;#x2F;resources&amp;#x2F;emoji&amp;#x2F;neofox&amp;#x2F;neofox_think.png&quot; alt=&quot;Curious neofox asks again&quot; class=&quot;chat-avatar&quot; width=&quot;64&quot; height=&quot;64&quot;&gt;
	&lt;blockquote&gt;
&lt;p&gt;And why &lt;code&gt;$HOME&#x2F;podman.sock&lt;&#x2F;code&gt; instead of the default path?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;&#x2F;div&gt;

		
		
		
		
		
			
			
		
	

	
		
	

	
		
	

	
		
	


	
&lt;div class=&quot;chat-box&quot;&gt;
	&lt;img src=&quot;&amp;#x2F;resources&amp;#x2F;emoji&amp;#x2F;slatian_bean&amp;#x2F;slatian_bean.png&quot; alt=&quot;Slatian replies&quot; class=&quot;chat-avatar&quot; width=&quot;64&quot; height=&quot;64&quot;&gt;
	&lt;blockquote&gt;
&lt;p&gt;Because that puts it in plain sight, less &quot;magic&quot; to get confused by.&lt;&#x2F;p&gt;
&lt;p&gt;Any other writable file path owned by the runner user would have worked too.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;&#x2F;div&gt;



&lt;h3 id=&quot;creating-the-actual-service&quot;&gt;Creating the Actual Service&lt;&#x2F;h3&gt;
&lt;p&gt;Template done, now lets create a service:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Create the directory for the actual service. Note the USERNAME_podman pattern&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;mkdir&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;etc&#x2F;sv&#x2F;runner-for-someone_podman&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Take the run script from the template using a symbolic link&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ln&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;etc&#x2F;sv&#x2F;generic-forgejo-runner-podman&#x2F;run&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;etc&#x2F;sv&#x2F;runner-for-someone_podman&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; And create a symbolic link in &#x2F;etc&#x2F;service to &amp;quot;enable&amp;quot; the service&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ln&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;etc&#x2F;sv&#x2F;runner-for-someone_podman&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;etc&#x2F;service&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; The service should now be in an &amp;quot;up&amp;quot; state&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sv&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; status&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;etc&#x2F;service&#x2F;runner-for-someone_podman&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can control runit services using the &lt;code&gt;sv&lt;&#x2F;code&gt; command and through a path to the directory that contains the &lt;code&gt;run&lt;&#x2F;code&gt; script.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-text&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;manned.org&amp;#x2F;man&amp;#x2F;sv&quot; &gt;
		Have a look at the &lt;code&gt;sv&lt;&#x2F;code&gt; manual if you are not yet familiar with it
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;If you need it for debugging, you can always &lt;code&gt;sv stop&lt;&#x2F;code&gt; your service and run the &lt;code&gt;run&lt;&#x2F;code&gt; script like you would run any other shell script, make sure you are in the right directory so that you can run it as &lt;code&gt;.&#x2F;run&lt;&#x2F;code&gt; so that the username gets read correctly.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;configuring-the-forgejo-runner&quot;&gt;Configuring the Forgejo Runner&lt;&#x2F;h2&gt;
&lt;p&gt;For this step witch into the runner user using the following command:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;iu&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; runner-for-someone&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;getting-a-registration-token&quot;&gt;Getting a Registration Token&lt;&#x2F;h3&gt;
&lt;p&gt;Now is a good time to obtain a runner token …&lt;&#x2F;p&gt;
&lt;p&gt;… for yourself:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Log into the Forgejo instance using the website&lt;&#x2F;li&gt;
&lt;li&gt;Click on your avatar in the top right&lt;&#x2F;li&gt;
&lt;li&gt;Navigate to
&lt;ul&gt;
&lt;li&gt;&lt;kbd&gt;Settings&lt;&#x2F;kbd&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;kbd&gt;Actions&lt;&#x2F;kbd&gt; (in the Sidebar)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;kbd&gt;Runners&lt;&#x2F;kbd&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;kbd&gt;Create new runner&lt;&#x2F;kbd&gt; (Button in the top right)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Copy the registration token&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;… for your organisation:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Log into the Forgejo instance using the website&lt;&#x2F;li&gt;
&lt;li&gt;Navigate to the repository overview of your organisation&lt;&#x2F;li&gt;
&lt;li&gt;Click the &lt;kbd&gt;Settings&lt;&#x2F;kbd&gt; link in the to right (Might be hidden inside a 3-dot menu)&lt;&#x2F;li&gt;
&lt;li&gt;Navigate to
&lt;ul&gt;
&lt;li&gt;&lt;kbd&gt;Actions&lt;&#x2F;kbd&gt; (in the Sidebar)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;kbd&gt;Runners&lt;&#x2F;kbd&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;kbd&gt;Create new runner&lt;&#x2F;kbd&gt; (Button in the top right)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Copy the registration token&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Leave that tab open, we will later use it to observe our runner.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;registering-the-runner&quot;&gt;Registering the Runner&lt;&#x2F;h3&gt;
&lt;p&gt;Back in your shell run:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;forgejo-runner&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; register&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It will interactively ask a few questions.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example output of a runner registration for the user slatian on codeberg.org.&lt;&#x2F;figcaption&gt;
	&lt;samp&gt;
INFO Registering runner, arch=amd64, os=linux, version=6.3.1.&lt;br&gt;
WARN Runner in user-mode.&lt;br&gt;
INFO Enter the Forgejo instance URL (for example, https:&#x2F;&#x2F;next.forgejo.org&#x2F;):&lt;br&gt;
&lt;kbd&gt;https:&#x2F;&#x2F;codeberg.org&#x2F;&lt;&#x2F;kbd&gt;&lt;br&gt;
&lt;br&gt;
INFO Enter the runner token:&lt;br&gt;
&lt;kbd class=&quot;z-comment&quot;&gt;[… registration token redacted …]&lt;&#x2F;kbd&gt;&lt;br&gt;
&lt;br&gt;
INFO Enter the runner name (if set empty, use hostname: bad-wolf.slatecave.net):&lt;br&gt;
&lt;kbd&gt;runner-for-slatian.bad-wolf.slatecave.net&lt;&#x2F;kbd&gt;&lt;br&gt;
&lt;br&gt;
INFO Enter the runner labels, leave blank to use the default labels (comma-separated, for example, ubuntu-20.04:docker:&#x2F;&#x2F;node:20-bookworm,ubuntu-18.04:docker:&#x2F;&#x2F;node:20-bookworm):&lt;br&gt;
&lt;kbd class=&quot;z-comment&quot;&gt;[… this affects the `runs-on:` directive, leave it empty if you&#x27;re just starting out …]&lt;&#x2F;kbd&gt;&lt;br&gt;
&lt;br&gt;
INFO Registering runner, name=codeberg-runner-for-slatian.bad-wolf.slatecave.net, instance=https:&#x2F;&#x2F;codeberg.org&#x2F;, labels=[docker:docker:&#x2F;&#x2F;data.forgejo.org&#x2F;oci&#x2F;node:20-bullseye].
DEBU Successfully pinged the Forgejo instance server&lt;br&gt;
INFO Runner registered successfully.&lt;br&gt;
&lt;&#x2F;samp&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;The resulting registration will be store in the file &lt;code&gt;.runner&lt;&#x2F;code&gt;, you don&#x27;t have to do anything with it, but you should know where your configuration is.&lt;&#x2F;p&gt;
&lt;p&gt;If you refresh the browser window where you got the token from now, you should see a new runner that is marked a &quot;Offline&quot;. (Leave the tab open, we&#x27;ll need it again after creating the runner service)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;generating-the-runner-configuration-file&quot;&gt;Generating the Runner Configuration File&lt;&#x2F;h3&gt;
&lt;p&gt;Speaking of configuration, your runner needs a configuration file.&lt;&#x2F;p&gt;
&lt;p&gt;Generate one:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;forgejo-runner&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; generate-config&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ~&#x2F;config.yml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You don&#x27;t have to change this configuration file either, but you should have a look at it anyway.&lt;&#x2F;p&gt;
&lt;p&gt;The most interesting settings are:&lt;&#x2F;p&gt;




	


&lt;dl class=&quot;&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;enable_ipv6&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		If you know you have working IPv6 set this to &lt;code&gt;true&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;force_pull&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Set this to &lt;code&gt;true&lt;&#x2F;code&gt; to make Podman check if a new version of an image is available every time it is needed. Podman is smart enough to not download an image again if it is already up to date.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;container.docker_host&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Leave it set to &lt;code&gt;-&lt;&#x2F;code&gt; or an empty string, the runner will then make use of the &lt;code&gt;DOCKER_HOST&lt;&#x2F;code&gt; variable to find the socket, which we can set in the service file.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;privileged&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Be careful with this one, if enabled container isolation becomes almost non-existent and everything that runs on you CI will have full access to your runner user. Leave it off.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;&lt;b&gt;How to quickly find out if IPv6 is working?&lt;&#x2F;b&gt;
Test if &lt;code&gt;ping v6.echoip.slatecave.net&lt;&#x2F;code&gt; or pinging any other IPv6 only host works.&lt;&#x2F;p&gt;
&lt;p&gt;We are done with the runner user for now.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function z-builtin&quot;&gt;exit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;the-runner-service&quot;&gt;The Runner Service&lt;&#x2F;h2&gt;
&lt;p&gt;Like with the Podman service this will be split into a template and an actual service.&lt;&#x2F;p&gt;
&lt;p&gt;Again, resulting in one service per user.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-runner-service-template&quot;&gt;The Runner Service Template&lt;&#x2F;h3&gt;
&lt;p&gt;Create a directory at &lt;code&gt;&#x2F;etc&#x2F;sv&#x2F;generic-forgejo-runner-runner&#x2F;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Create a file &lt;code&gt;&#x2F;etc&#x2F;sv&#x2F;generic-forgejo-runner-runner&#x2F;run&lt;&#x2F;code&gt; with the following content:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#!&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;&#x2F;bin&#x2F;sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;USERNAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; basename&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-builtin&quot;&gt;pwd&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; cut&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;_&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; )&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Make sure the podman service is running&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sv&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; start&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;..&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;USERNAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;_podman&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Start the forgejo runner&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function z-builtin&quot;&gt;exec&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sudo&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;iu&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;USERNAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;	sh&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;c&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;DOCKER_HOST=&amp;quot;unix:&#x2F;&#x2F;${HOME}&#x2F;podman.sock&amp;quot; exec forgejo-runner daemon&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Make sure the file is executable:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;chmod&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; +x&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;etc&#x2F;sv&#x2F;generic-forgejo-runner-runner&#x2F;run&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;explaining-the-runner-service-template&quot;&gt;Explaining the Runner Service Template&lt;&#x2F;h3&gt;
&lt;p&gt;This works much like the template we used to start Podman, so I&#x27;ll only go over the parts that are different.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sv&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; start&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;..&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;USERNAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;_podman&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will make sure the corresponding Podman service has started before starting the runner, even if it was stopped for some reason. &lt;code&gt;sv start&lt;&#x2F;code&gt; will by default wait for up to 7 seconds which should be plenty of time for Podman to start.&lt;&#x2F;p&gt;
&lt;p&gt;And inside the &lt;code&gt;sh -c&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;DOCKER_HOST&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;unix:&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;HOME&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;podman.sock&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-builtin&quot;&gt; exec&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; forgejo-runner&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; daemon&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will set the &lt;code&gt;DOCKER_HOST&lt;&#x2F;code&gt; environment variable and run the &lt;code&gt;forgejo-runner&lt;&#x2F;code&gt; in its service mode. The &lt;code&gt;exec&lt;&#x2F;code&gt; again are there to get the shells out of the way as soon as they&#x27;ve done their work.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Not really a daemon:&lt;&#x2F;b&gt;
Historically the &quot;daemon&quot; mode made a program detach itself from the shells process tree and run in the background, which works great for unmanaged services, but actually gets in the way of supervisors like runit. The &lt;code&gt;forgejo-runner daemon&lt;&#x2F;code&gt; command doesn&#x27;t do that, contrary to what the name might suggest.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;creating-the-actual-runner-service&quot;&gt;Creating the Actual Runner Service&lt;&#x2F;h3&gt;
&lt;p&gt;Almost the same commands as with Podman to turn the template into a running service:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Create the directory for the actual service. Note the USERNAME_runner pattern&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;mkdir&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;etc&#x2F;sv&#x2F;runner-for-someone_runner&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Take the run script from the template using a symbolic link&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ln&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;etc&#x2F;sv&#x2F;generic-forgejo-runner-runner&#x2F;run&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;etc&#x2F;sv&#x2F;runner-for-someone_runner&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; And create a symbolic link in &#x2F;etc&#x2F;service to &amp;quot;enable&amp;quot; the service&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ln&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;etc&#x2F;sv&#x2F;runner-for-someone_runner&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;etc&#x2F;service&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; The service should now be in an &amp;quot;up&amp;quot; state&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sv&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; status&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;etc&#x2F;service&#x2F;runner-for-someone_runner&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;testing-the-runner&quot;&gt;Testing the Runner&lt;&#x2F;h2&gt;
&lt;p&gt;Refresh the tab with the runner overview, the status should have changed from &quot;Offline&quot; to &quot;Idle&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;Now create a new Forgejo repository, make sure the actions tab is enabled, if not click on &lt;kbd&gt;Settings&lt;&#x2F;kbd&gt; in the upper right, navigate to &lt;kbd&gt;Units&lt;&#x2F;kbd&gt;, &lt;&#x2F;kbd&gt;Overview&lt;&#x2F;kbd&gt;, enable the checkbox labelled &lt;kbd&gt;Actions&lt;&#x2F;kbd&gt; and click &lt;kbd&gt;Save&lt;&#x2F;kbd&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Create a file in &lt;code&gt;.forgejo&#x2F;workflows&#x2F;test.yaml&lt;&#x2F;code&gt; in the repository:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;---&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;on&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  p&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ush&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;j&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;obs&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  t&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;est&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;uns-on&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; d&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ocker&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    c&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ontainer&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;      i&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;mage&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;lpine&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    s&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;teps&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;un&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; e&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;cho &amp;quot;Hello World!&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Commit and push it.&lt;&#x2F;p&gt;
&lt;p&gt;In the &quot;Actions&quot; tab there should now be an action that is either waiting or already running. Don&#x27;t worry if it takes up to a minute for the runner to pick up the job.&lt;&#x2F;p&gt;
&lt;p&gt;After a few seconds everything should go green with a &quot;Hello World!&quot; in the logs. If it doesn&#x27;t there is usually a hint hidden in the logs as to what went wrong.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;If it fails with &quot;failed to create container&quot;:&lt;&#x2F;b&gt; This one is misleading, try to &lt;code&gt;sudo -iu runner-for-someone podman pull alpine&lt;&#x2F;code&gt;, it&#x27;ll output a more helpful error message. (Also have a look at &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;runner-with-podman-on-alpine&#x2F;#testing-podman&quot;&gt;testing Podman&lt;&#x2F;a&gt; again.)&lt;&#x2F;p&gt;







	
		
		
		
		
		
			
			
		
	

	
		
	

	
		
	

	
		
	


	
&lt;div class=&quot;chat-box&quot;&gt;
	&lt;img src=&quot;&amp;#x2F;resources&amp;#x2F;emoji&amp;#x2F;slatian_bean&amp;#x2F;slatian_bean_happy.png&quot; alt=&quot;Slatian congratulates&quot; class=&quot;chat-avatar&quot; width=&quot;64&quot; height=&quot;64&quot;&gt;
	&lt;blockquote&gt;
&lt;p&gt;🎉 Congratulations, you now have a working runner.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;You should keep the test repository, it&#x27;ll stay useful.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;&#x2F;div&gt;



&lt;p&gt;If you now want to try out some premade &lt;code&gt;using&lt;&#x2F;code&gt; actions remove the &lt;code&gt;image: alpine&lt;&#x2F;code&gt; line. Using alpine Linux as a base image is great if you know exactly what you need and want an efficient CI. But most premade actions unfortunately rely on the Ubuntu based nodejs image, which is the reason for that one being the default.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;enabling-podman-in-podman&quot;&gt;Enabling Podman in Podman&lt;&#x2F;h2&gt;
&lt;p&gt;To enable Podman in Podman functionality without having to fall back on dropping all isolation the containers need access to &lt;code&gt;fuse&lt;&#x2F;code&gt; (install and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;manned.org&#x2F;man&#x2F;alpine-3.23&#x2F;modprobe&quot;&gt;&lt;code&gt;modprobe&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; it if you haven&#x27;t done already) and some additional &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;manned.org&#x2F;man&#x2F;capabilities&quot;&gt;capabilities&lt;&#x2F;a&gt; to be able to create a container environment inside the container.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;What is Fuse?&lt;&#x2F;b&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.kernel.org&#x2F;filesystems&#x2F;fuse&#x2F;index.html&quot;&gt;&lt;code&gt;fuse&lt;&#x2F;code&gt; is a kernel subsystem&lt;&#x2F;a&gt; that allows mounting filesystems without any special privileges in a safe way. Podman uses this to assemble the filesystem visible from inside the container.&lt;&#x2F;p&gt;
&lt;p&gt;The Forgejo runner doesn&#x27;t directly support this way of running containers yet, but since we have a user just for the runner configuring Podman directly works too:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Install fuse&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;apk&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; add&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; fuse&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Make it useable immeadeately&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;modprobe&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; fuse&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Add fuse to the list of services to be started on boot&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;rc-update&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; add&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; fuse&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; switch to the runner user&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;iu&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; runner-for-someone&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; create the directory for the configuraion file&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;mkdir&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ~&#x2F;.config&#x2F;containers&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then create the file &lt;code&gt;~&#x2F;.config&#x2F;containers&#x2F;containers.conf&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;containers&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;devices&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;dev&#x2F;fuse&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;default_capabilities&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;SYS_ADMIN&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;MKNOD&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;        {&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;append&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;true&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And to test if that configuration file worked:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;podman&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; run&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-rm&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;	quay.io&#x2F;podman&#x2F;stable&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; podman&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; run&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; alpine&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; Hello&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; Podman&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If it didn&#x27;t work you&#x27;d either get a message that mentions the absence of fuse or &quot;potentially insufficient UIDs or GIDs&quot; if the &lt;code&gt;SYS_ADMIN&lt;&#x2F;code&gt; capability is missing.&lt;&#x2F;p&gt;
&lt;p&gt;To also enable this feature in the runner you have to restart the Podman service:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Back to root&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function z-builtin&quot;&gt;exit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Restart the podman serivce&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sv&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; restart&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;etc&#x2F;service&#x2F;runner-for-someone_podman&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you want to know what exactly why those are necessary I highly reading the ultimate &quot;Podman inside a container&quot; guide by Dan Walsh and Urvashi Mohnani on the Redhat blog:&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-action-next&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;www.redhat.com&amp;#x2F;en&amp;#x2F;blog&amp;#x2F;podman-inside-container&quot; &gt;
		How to use Podman inside of a container
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;maintenance&quot;&gt;Maintenance&lt;&#x2F;h2&gt;
&lt;p&gt;It is a good idea to get rid of old images from time to time, you can use the following commands to manage Podman images for your runner:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;iu&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; runner-for-someone&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; list images&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;podman&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; images&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; remove outdated or no longer needed images&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;podman&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; image&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; rm&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;image-id-goes-her&lt;&#x2F;span&gt;&lt;span&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Another thing to check for are stopped containers that never got cleaned up, this may happen because of sudden shutdowns or by forgetting the &lt;code&gt;--rm&lt;&#x2F;code&gt; on containers during testing.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;iu&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; runner-for-someone&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; list running AND stopped containers&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;podman&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ps&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-all&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; remove old containers&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;podman&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; rm&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;container-id-goes-her&lt;&#x2F;span&gt;&lt;span&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;other-resources&quot;&gt;Other Resources&lt;&#x2F;h2&gt;

	

&lt;ul class=&quot;link-list&quot;&gt;
&lt;li&gt;&lt;a class=&quot;decoration-action-next&quot; rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;forgejo.org&#x2F;docs&#x2F;latest&#x2F;user&#x2F;actions&#x2F;&quot;&gt;Forgejo Actions user guide&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;decoration-action-next&quot; rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;forgejo.org&#x2F;docs&#x2F;latest&#x2F;admin&#x2F;actions&#x2F;&quot;&gt;Forgejo Actions administrator guide&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;decoration-action-next&quot; rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;forgejo.org&#x2F;docs&#x2F;latest&#x2F;user&#x2F;actions&#x2F;&quot;&gt;Forgejo Runner installation guide&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;decoration-action-next&quot; rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.codeberg.org&#x2F;ci&#x2F;actions&#x2F;&quot;&gt;Using Forgejo Actions (Self-hosted) by Codeberg&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;the-beginning&quot;&gt;The Beginning …&lt;&#x2F;h2&gt;







	
		
		
		
		
		
			
			
		
	

	
		
	

	
		
	

	
		
	

	
		
	

	
		
	

	
		
	

	
		
	


	
&lt;div class=&quot;chat-box&quot;&gt;
	&lt;img src=&quot;&amp;#x2F;resources&amp;#x2F;emoji&amp;#x2F;slatian_bean&amp;#x2F;slatian_bean.png&quot; alt=&quot;Slatian encourages&quot; class=&quot;chat-avatar&quot; width=&quot;64&quot; height=&quot;64&quot;&gt;
	&lt;blockquote&gt;
&lt;p&gt;Now you have your own runner (hopefully 😅), it&#x27;s up to you what you want to with it.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;I highly encourage you to keep the test repository around, try some unconventional things, break it, fix it, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;shell-ci-checkout&quot;&gt;DIY some actions&lt;&#x2F;a&gt; instead of importing and learn what makes the actions run.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Another thing to try is building your own images.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Whatever you do, have fun and make the world a nicer place to be!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;&#x2F;div&gt;



&lt;h2 id=&quot;license-note&quot;&gt;License Note&lt;&#x2F;h2&gt;
&lt;p&gt;This article is licensed under &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;CreativeCommmons BY-SA 4.0 International&lt;&#x2F;a&gt; (this is different from the usual CC BY-NC-SA here).&lt;&#x2F;p&gt;
&lt;p&gt;If possible please link back to this article.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Fixing synapse 1.128.0 with SQLite</title>
        <published>2025-04-13T00:00:00+00:00</published>
        <updated>2025-04-13T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/synapse-sqlite-fix/"/>
        <id>https://slatecave.net/blog/synapse-sqlite-fix/</id>
        
        <summary type="text">Synapse 1.128.0 came with a unpleasant surprise, here is how I worked around it</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/synapse-sqlite-fix/">&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;&#x2F;h2&gt;
&lt;p&gt;On 2025-04-08 element HQ released the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;element-hq&#x2F;synapse&#x2F;releases&#x2F;tag&#x2F;v1.128.0&quot;&gt;update for synapse 1.128.0&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I running my server the way I do got the update via my package manger like most other people. Also like most other people I&#x27;m using the SQLite backend.&lt;&#x2F;p&gt;
&lt;p&gt;After the update my matrix clients reported that they were unable to connect and the synapse process used 100% of one CPU.&lt;&#x2F;p&gt;
&lt;p&gt;Not a great start for debugging, but fortunately I wasn&#x27;t alone, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;element-hq&#x2F;synapse&#x2F;issues&#x2F;18325&quot;&gt;other people had the same issue, also with SQLite&lt;&#x2F;a&gt;, which got me looking at the changelog in detail for anything that could be database related …&lt;&#x2F;p&gt;
&lt;p&gt;Long story short: I found out that the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;element-hq&#x2F;synapse&#x2F;pull&#x2F;18068&quot;&gt;&lt;q&gt;Add a column &lt;code&gt;participant&lt;&#x2F;code&gt; to &lt;code&gt;room_memberships&lt;&#x2F;code&gt; table.&lt;&#x2F;q&gt;&lt;&#x2F;a&gt; background migration caused the problem with a &lt;strong&gt;VERY&lt;&#x2F;strong&gt; slow SQL query. On other databases, this sucks up performance, but on SQLite, this locks up the database thread and the service grinds to a halt.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-solution&quot;&gt;The Solution&lt;&#x2F;h2&gt;
&lt;p&gt;Almost every SQL performance problem can be worked around by throwing enough indices at it. This is the classic, trade of storage space for compute time and here it&#x27;s worth it.&lt;&#x2F;p&gt;
&lt;p&gt;Also as an admin I also have the benefit of being able to inject indices with the &lt;code&gt;sqlite3&lt;&#x2F;code&gt; command without having to modify any application code.&lt;&#x2F;p&gt;
&lt;p&gt;So here are the 3 indices that speed the query fast enough to not only allow the server to function, but also speed the migration up to fit within a few minutes of a weekend:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;create&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; index&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; slatians_speedup_room_joins&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	ON&lt;&#x2F;span&gt;&lt;span&gt; current_state_events(room_id, state_key)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	where&lt;&#x2F;span&gt;&lt;span&gt; membership&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;join&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;create&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; index&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; slatians_speedup_event_messages&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	ON&lt;&#x2F;span&gt;&lt;span&gt; events(room_id, sender)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	where&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; type&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;m.room.message&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; OR&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; type&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;m.room.encrypted&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;create&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; index&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; slatians_speedup_room_membership_update_covering&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	ON&lt;&#x2F;span&gt;&lt;span&gt; room_memberships (event_stream_ordering, room_id, user_id);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can check the status of background jobs that synapse runs on the database by using the following SQL query, an empty output means all jobs have finished:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;select&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; from&lt;&#x2F;span&gt;&lt;span&gt; background_updates;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Whether or not you leave those indices in after the migration has finished is up to you, I&#x27;ve let them in.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-problem-in-detail&quot;&gt;The Problem in Detail&lt;&#x2F;h2&gt;
&lt;p&gt;As said above, the problem is that there is slow query running, I&#x27;ll split this up into why slow queries are bad in SQLite and then why the query is slow.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-slow-queries-are-bad-in-sqlite&quot;&gt;Why Slow Queries are bad in SQLite&lt;&#x2F;h3&gt;
&lt;p&gt;SQLite is a simple and performant database and unlike other databases one can include it as a library that stores the data in a single file, no extra service needed and very welcome for free time admins who don&#x27;t want to put in the time to keep a database service running.&lt;&#x2F;p&gt;
&lt;p&gt;Part of why SQLite is so simple, yet fast is that it runs in a single thread, meaning it does one thing start to finish and then does the next allowing it to mostly ignore the problem of multiple queries running into each other (Note: oversimplified). Problem with that is, if one query takes very long the others have to wait very long, which is a problem if that other query is for a request from a client with a timeout that is not &lt;i&gt;very long&lt;&#x2F;i&gt;, resulting in what looks like connectivity problems.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-slow-query&quot;&gt;The Slow Query&lt;&#x2F;h3&gt;
&lt;p&gt;Part of the adding another column to the &lt;code&gt;room_memberships&lt;&#x2F;code&gt; table is populating it with the correct data, which is what the &lt;code&gt;populate_participant_bg_update&lt;&#x2F;code&gt; background update is for. Searching the code for that one can quickly find out …&lt;&#x2F;p&gt;
&lt;p&gt;… it is mainly implemented using the following query:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;UPDATE&lt;&#x2F;span&gt;&lt;span&gt; room_memberships&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;SET&lt;&#x2F;span&gt;&lt;span&gt; participant &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; True&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    SELECT DISTINCT&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;state_key&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;e&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;room_id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    FROM&lt;&#x2F;span&gt;&lt;span&gt; current_state_events &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;AS&lt;&#x2F;span&gt;&lt;span&gt; c&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    INNER JOIN&lt;&#x2F;span&gt;&lt;span&gt; events &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;AS&lt;&#x2F;span&gt;&lt;span&gt; e &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;ON&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;room_id&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; e&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;room_id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    WHERE&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;membership&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;join&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        AND&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;state_key&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; e&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;sender&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        AND&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;            e&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;m.room.message&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            OR&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; e&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;m.room.encrypted&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;AS&lt;&#x2F;span&gt;&lt;span&gt; subquery&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; room_memberships&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;user_id&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; subquery&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;state_key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    AND&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; room_memberships&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;room_id&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; subquery&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;room_id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    AND&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; room_memberships&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;event_stream_ordering&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;=&lt;&#x2F;span&gt;&lt;span&gt; ?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    AND&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; room_memberships&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;event_stream_ordering&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; ?;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s a little monster, but nothing too bad, the subquery can be a potential source of slowness, but not in this case.&lt;&#x2F;p&gt;
&lt;p&gt;Speaking of the subquery, Asking the database about how it would do the select …&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;EXPLAIN QUERY PLAN&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;SELECT DISTINCT&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;state_key&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;e&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;room_id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; current_state_events &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;AS&lt;&#x2F;span&gt;&lt;span&gt; c&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;INNER JOIN&lt;&#x2F;span&gt;&lt;span&gt; events &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;AS&lt;&#x2F;span&gt;&lt;span&gt; e &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;ON&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;room_id&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; e&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;room_id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;membership&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;join&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    AND&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;state_key&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; e&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;sender&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    AND&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;        e&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;m.room.message&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        OR&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; e&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;m.room.encrypted&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Result:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;QUERY PLAN&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|--SCAN c&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|--SEARCH e USING INDEX events_room_stream (room_id=?)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;`--USE TEMP B-TREE FOR DISTINCT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;… tells us, that it is:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Looking at all entries in the &lt;code&gt;current_state_events&lt;&#x2F;code&gt; table (alias &lt;code&gt;c&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Querying the &lt;code&gt;events&lt;&#x2F;code&gt; table using an index that only contains the room id&lt;&#x2F;li&gt;
&lt;li&gt;Collecting all of that into a binary tree to return each result only once&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;For this the context, that the &lt;code&gt;current_state_events&lt;&#x2F;code&gt; contains round 64K entries and the &lt;code&gt;events&lt;&#x2F;code&gt; table about 1 million entries. (Yes, small server)&lt;&#x2F;p&gt;
&lt;p&gt;The two problems here are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The scan on &lt;code&gt;current_state_events&lt;&#x2F;code&gt; happens without using an index, meaning the database has to look at every single entry&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;events&lt;&#x2F;code&gt; are only indexed by their &lt;code&gt;room_id&lt;&#x2F;code&gt; leaving a lot of comparisons for the database to do on the fly (remember the high CPU usage?)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;speeding-things-up&quot;&gt;Speeding Things up&lt;&#x2F;h3&gt;
&lt;p&gt;The most efficient way to get this sped up is to custom tailor two indices on those queries so that the database has to do as little as possible when doing the actual work, meaning we need covering indices.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;What&#x27;s a covering index?&lt;&#x2F;b&gt; A covering index is an index, that contains all data the database needs for a given query. this has the benefit, that the database never has to leave the index saving a lookup in the actual table. This is less code running and also easier to predict and cache, making it quite fast.&lt;&#x2F;p&gt;
&lt;p&gt;The first index is for the &lt;code&gt;current_state_events&lt;&#x2F;code&gt; table, it needs the &lt;code&gt;state_key&lt;&#x2F;code&gt; and the &lt;code&gt;room_id&lt;&#x2F;code&gt; and only the rows where the &lt;code&gt;membership&lt;&#x2F;code&gt; column contains the string &lt;code&gt;join&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;create&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; index&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; slatians_speedup_room_joins&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	ON&lt;&#x2F;span&gt;&lt;span&gt; current_state_events(room_id, state_key)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	where&lt;&#x2F;span&gt;&lt;span&gt; membership&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;join&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I&#x27;ve put &lt;code&gt;room_id&lt;&#x2F;code&gt; first, because that&#x27;s needed for doing the joining before the &lt;code&gt;DISTINCT&lt;&#x2F;code&gt; part happens. Maybe the other way around could have been optimozed even better by SQLite, but the main point of this index is saving the comparison against the &lt;code&gt;membership&lt;&#x2F;code&gt; column.&lt;&#x2F;p&gt;
&lt;p&gt;The second index is for the &lt;code&gt;events&lt;&#x2F;code&gt; table, it needs the &lt;code&gt;room_id&lt;&#x2F;code&gt; for the join (which is why it comes first), the &lt;code&gt;sender&lt;&#x2F;code&gt; for filtering immediately after the join and it should only contain the entries that comparison against the &lt;code&gt;type&lt;&#x2F;code&gt; is checking for:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;create&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; index&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; slatians_speedup_event_messages&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	ON&lt;&#x2F;span&gt;&lt;span&gt; events(room_id, sender)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	where&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; type&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;m.room.message&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; OR&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; type&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;m.room.encrypted&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Those two indices speed the query up enough for synapse to do it&#x27;s job as a server again, but of course there is the outer query, which can be sped up too.&lt;&#x2F;p&gt;
&lt;p&gt;Looking at the schema of the &lt;code&gt;room_memberships&lt;&#x2F;code&gt; table reveals …&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;schema&lt;&#x2F;span&gt;&lt;span&gt; room_memberships&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Output: (reformatted for readability)&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;CREATE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; TABLE&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; room_memberships&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	event_id &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;TEXT&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; NOT NULL&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	user_id &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;TEXT&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; NOT NULL&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	sender &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;TEXT&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; NOT NULL&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	room_id &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;TEXT&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; NOT NULL&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	membership &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;TEXT&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; NOT NULL&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	forgotten &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;INTEGER&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-modifier&quot;&gt; DEFAULT&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	display_name &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;TEXT&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	avatar_url &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;TEXT&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	event_stream_ordering &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;BIGINT&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-modifier&quot;&gt; REFERENCES&lt;&#x2F;span&gt;&lt;span&gt; events(stream_ordering),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	participant &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;BOOLEAN&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-modifier&quot;&gt; DEFAULT&lt;&#x2F;span&gt;&lt;span&gt; FALSE,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	UNIQUE&lt;&#x2F;span&gt;&lt;span&gt; (event_id)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;CREATE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; INDEX&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; room_memberships_room_id&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ON&lt;&#x2F;span&gt;&lt;span&gt; room_memberships (room_id);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;CREATE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; INDEX&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; room_memberships_user_id&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ON&lt;&#x2F;span&gt;&lt;span&gt; room_memberships (user_id);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;CREATE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; INDEX&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; room_memberships_user_room_forgotten&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ON&lt;&#x2F;span&gt;&lt;span&gt; room_memberships (user_id, room_id) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span&gt; forgotten &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;CREATE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; TRIGGER&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; room_memberships_bad_event_stream_ordering&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	[… left out …]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;CREATE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; INDEX&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; room_membership_user_room_idx&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ON&lt;&#x2F;span&gt;&lt;span&gt; room_memberships (user_id, room_id) ;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;CREATE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; INDEX&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; room_memberships_stream_ordering_idx&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ON&lt;&#x2F;span&gt;&lt;span&gt; room_memberships (event_stream_ordering) ;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;quite some columns, a trigger we&#x27;re not interested in and a lot of indices, but none that combine &lt;code&gt;event_stream_ordering&lt;&#x2F;code&gt; with the needed &lt;code&gt;user_id&lt;&#x2F;code&gt; and &lt;code&gt;room_id&lt;&#x2F;code&gt;, so to create one:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;create&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; index&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; slatians_speedup_room_membership_update_covering&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	ON&lt;&#x2F;span&gt;&lt;span&gt; room_memberships (event_stream_ordering, room_id, user_id);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now one can watch the background job eating through the remaining work within a few minutes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;closing-notes&quot;&gt;Closing Notes&lt;&#x2F;h2&gt;







	
		
		
		
		
		
			
			
		
	

	
		
	

	
		
	

	
		
	

	
		
	

	
		
			
&lt;div class=&quot;chat-box&quot;&gt;
	&lt;img src=&quot;&amp;#x2F;resources&amp;#x2F;emoji&amp;#x2F;slatian_bean&amp;#x2F;slatian_bean.png&quot; alt=&quot;Slatian lists&quot; class=&quot;chat-avatar&quot; width=&quot;64&quot; height=&quot;64&quot;&gt;
	&lt;blockquote&gt;
&lt;p&gt;This was the first time the synapse server brought a surprise with an update, so far every other update just worked™.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;This little exercise in database performance allowed me to avoid a downgrade to an older version and get my chat working again, although I wish that it wouldn&#x27;t have been necessary.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;&#x2F;div&gt;

		
		
		
		
		
			
			
		
	

	
		
	


	
&lt;div class=&quot;chat-box&quot;&gt;
	&lt;img src=&quot;&amp;#x2F;resources&amp;#x2F;emoji&amp;#x2F;slatian_bean&amp;#x2F;slatian_bean_happy.png&quot; alt=&quot;Slatian happily recommends&quot; class=&quot;chat-avatar&quot; width=&quot;64&quot; height=&quot;64&quot;&gt;
	&lt;blockquote&gt;
&lt;p&gt;If you&#x27;re now interested in where to get the knowledge to speed up SQLite I recommend reading the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sqlite.org&#x2F;queryplanner.html&quot;&gt;SQLite Indexing Tutorial&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sqlite.org&#x2F;optoverview.html&quot;&gt;The SQLite Query Optimizer Overview&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;&#x2F;div&gt;



</content>
    </entry>
    <entry xml:lang="en">
        <title>You should read: Outrage Warps Reality</title>
        <published>2025-03-01T00:00:00+00:00</published>
        <updated>2025-03-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/recommendation-outrage-warps-reality/"/>
        <id>https://slatecave.net/blog/recommendation-outrage-warps-reality/</id>
        
        <summary type="text">Steffo and Finnley on how mass paranoia and uninformed outrage hurt the Fediverse.</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/recommendation-outrage-warps-reality/">&lt;h2 id=&quot;an-article-i-think-is-worth-reading&quot;&gt;An Article I Think is Worth Reading&lt;&#x2F;h2&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-action-next&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;steffo.blog&amp;#x2F;outrage-warps-reality&amp;#x2F;&quot; &gt;
		Outrage Warps Reality
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;How mass paranoia and uninformed outrage will hurt the Fediverse, and what we can learn about the drama regarding VLC&#x27;s AI and Google&#x27;s newest service.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;About a good 10 minute read.&lt;&#x2F;p&gt;







	
		
		
		
		
		
			
			
		
	

	
		
	

	
		
	

	
		
	

	
		
	

	
		
	


	
&lt;div class=&quot;chat-box&quot;&gt;
	&lt;img src=&quot;&amp;#x2F;resources&amp;#x2F;emoji&amp;#x2F;slatian_bean&amp;#x2F;slatian_bean_happy.png&quot; alt=&quot;Note from Slatian&quot; class=&quot;chat-avatar&quot; width=&quot;64&quot; height=&quot;64&quot;&gt;
	&lt;blockquote&gt;
&lt;p&gt;Don&#x27;t let the note at the start of the post fool you. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;steffo.blog&quot;&gt;Steffo&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;finnley.dev&quot;&gt;Finnley&lt;&#x2F;a&gt; did a very good job writing it!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Given the current context this article has become even more relevant since it was published &lt;time datetime=&quot;2025-02-15&quot; title=&quot;2025-02-15&quot;&gt;around 2 weeks ago&lt;&#x2F;time&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Why are you still reading here anyway? &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;steffo.blog&#x2F;outrage-warps-reality&#x2F;&quot;&gt;Go and read the post!&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;&#x2F;div&gt;



</content>
    </entry>
    <entry xml:lang="en">
        <title>Today I learned: OpenLDAP</title>
        <published>2025-02-17T00:00:00+00:00</published>
        <updated>2025-02-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/today-i-learned-openldap/"/>
        <id>https://slatecave.net/blog/today-i-learned-openldap/</id>
        
        <summary type="text">Things I wish I knew before starting with OpenLDAP.</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/today-i-learned-openldap/">&lt;h2 id=&quot;who-is-this-for&quot;&gt;Who is this for?&lt;&#x2F;h2&gt;
&lt;p&gt;This is for people who know how to configure their LDAP clients, but now want some knowledge on how to configure a server.&lt;&#x2F;p&gt;







	
		
		
		
		
		
			
			
		
	

	
		
	

	
		
	

	
		
	


	
&lt;div class=&quot;chat-box&quot;&gt;
	&lt;img src=&quot;&amp;#x2F;resources&amp;#x2F;emoji&amp;#x2F;slatian_bean&amp;#x2F;slatian_bean.png&quot; alt=&quot;Quick not from Slatian&quot; class=&quot;chat-avatar&quot; width=&quot;64&quot; height=&quot;64&quot;&gt;
	&lt;blockquote&gt;
&lt;p&gt;This is actually a time delayed &quot;Today&quot; as finding all the links again took a bit.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;And don&#x27;t worry, configuring LDAP isn&#x27;t &lt;strong&gt;that&lt;&#x2F;strong&gt; hard, most if it is because of difficult to find documentation and a missing birds eye view.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;&#x2F;div&gt;



&lt;h2 id=&quot;how-to-configure&quot;&gt;How to configure&lt;&#x2F;h2&gt;
&lt;p&gt;While the developers of OpenLDAP are really pushing their online configuration (olc) the textfile based legacy configuration is still fully functional and more readable as well as accessible for testing (Especially if this is your fist own LDAP server).&lt;&#x2F;p&gt;
&lt;p&gt;Note, that whatever whenever you are configuring a database or overlay, you probably have to load the corresponding modules (see your distributions example configuration for where exactly the modules are, usually somewhere around &lt;code&gt;&#x2F;usr&#x2F;lib&#x2F;ldap&#x2F;&lt;&#x2F;code&gt; or a similar path.) The backend modules (database types) are all called &lt;code&gt;back_&amp;lt;type&amp;gt;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;As for documentation: The OpenLDAP Admin Guide is your friend here:&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-action-next&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;www.openldap.org&amp;#x2F;doc&amp;#x2F;&quot; &gt;
		OpenLDAP Documentation
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;If you can&#x27;t find what you are looking for there, see if there is a &lt;code&gt;slapd-&lt;&#x2F;code&gt; or &lt;code&gt;slapo-&lt;&#x2F;code&gt; manpage on the module you need help with.&lt;&#x2F;p&gt;
&lt;p&gt;The Samba Wiki contains some useful configuration templates if your goal is to build a some kind of proxy or mirror Server:&lt;&#x2F;p&gt;

	

&lt;ul class=&quot;link-list&quot;&gt;
&lt;li&gt;&lt;a class=&quot;decoration-action-next&quot; rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.samba.org&#x2F;index.php&#x2F;2.2.1_slapd.conf_Slave_syncrepl_Openldap2.2&quot;&gt;slapd.conf Slave syncrepl OpenLDAP 2.2&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;decoration-action-next&quot; rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.samba.org&#x2F;index.php&#x2F;OpenLDAP_as_proxy_to_AD&quot;&gt;OpenLDAP as proxy to AD&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; OpenLDAP doesn&#x27;t really care what it is proxying, so while those example might be for a SAMBA AD, they work for something like an addressbook too.&lt;&#x2F;p&gt;
&lt;p&gt;If you have any problems with your configuration set the debug&#x2F;log level to &lt;code&gt;64&lt;&#x2F;code&gt; (log configuration errors) or &lt;code&gt;-1&lt;&#x2F;code&gt; (all errors, warnings, debug and tracing, very verbose.)&lt;&#x2F;p&gt;
&lt;p&gt;See the admin guide for a full list of log levels.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Log Level Hint:&lt;&#x2F;b&gt; The log levels double, because that is a bitfield, one can combine them with a binary or. That&#x27;s why &lt;code&gt;-1&lt;&#x2F;code&gt; (the binary representation of -1 has all bits set) turns on all log messages.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;proxy-helpers&quot;&gt;Proxy helpers&lt;&#x2F;h2&gt;
&lt;p&gt;If you want to proxy or synchronise from another server you want to use the &lt;code&gt;ldap&lt;&#x2F;code&gt; backend with an optional &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.openldap.org&#x2F;doc&#x2F;admin26&#x2F;overlays.html#The%20Proxy%20Cache%20Engine&quot;&gt;&lt;code&gt;pcache&lt;&#x2F;code&gt; (proxy cache)&lt;&#x2F;a&gt; overlay or an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.openldap.org&#x2F;doc&#x2F;admin26&#x2F;slapdconfig.html#MDB%20Database%20Directives&quot;&gt;&lt;code&gt;mdb&lt;&#x2F;code&gt; (older configurations use its predecessor &lt;code&gt;bdb&lt;&#x2F;code&gt;) backend&lt;&#x2F;a&gt; with the &lt;code&gt;syncrepl&lt;&#x2F;code&gt; directive.&lt;&#x2F;p&gt;
&lt;p&gt;Rewriting the requests from one suffix to another search for &lt;code&gt;massage&lt;&#x2F;code&gt;. If the module you want to use doesn&#x27;t support &lt;code&gt;massage&lt;&#x2F;code&gt;,  (i.e. serving from an &lt;code&gt;ldap&lt;&#x2F;code&gt; backend) there is the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;manned.org&#x2F;man&#x2F;slapo-rwm#head5&quot;&gt;&lt;code&gt;rwm-suffixmassage&lt;&#x2F;code&gt; directive&lt;&#x2F;a&gt; that can be configured on an &lt;code&gt;rwm&lt;&#x2F;code&gt; overlay.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;loadmodule rwm&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[… regular db config goes here …]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;overlay rwm&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;rwm-suffixmassage ou=new,dc=suffix,dc=to,dc=export,dc=as&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Rewriting may be necessary to support legacy systems or to &quot;mount&quot; a subtree under a different name.&lt;&#x2F;p&gt;
&lt;p&gt;For combining multiple backends there is the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;manned.org&#x2F;man&#x2F;slapd-meta&quot;&gt;&lt;code&gt;meta&lt;&#x2F;code&gt; backend&lt;&#x2F;a&gt;, that can combine requests from multiple &lt;code&gt;uri&lt;&#x2F;code&gt; directives (the suffix to mount as is the URL path), the meta backend also supports a &lt;code&gt;suffixmassage&lt;&#x2F;code&gt; directive that can be placed after each &lt;code&gt;uri&lt;&#x2F;code&gt; directive.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note on &lt;code&gt;meta&lt;&#x2F;code&gt; and &lt;code&gt;suffixmassage&lt;&#x2F;code&gt;:&lt;&#x2F;b&gt; The path in the URL is the suffix that the backend should be served under, not the path on the backend! This confused me the first time because it combines to seemingly unrelated settings in one configuration value, but it makes sense when seen without the &lt;code&gt;suffixmassage&lt;&#x2F;code&gt; directive&lt;&#x2F;p&gt;
&lt;p&gt;Chaining up multiple, more complex configurations into a &lt;code&gt;meta&lt;&#x2F;code&gt; backend is easiest when you define the individual sub-LDAP directories as if they weren&#x27;t part of the &lt;code&gt;meta&lt;&#x2F;code&gt; backend under a dummy suffix and then letting OpenLDAP access itself via the &lt;code&gt;ldapi:&#x2F;&#x2F;&#x2F;&lt;&#x2F;code&gt; scheme using Unix sockets (Unix sockets to avoid involving the network stack, faster).&lt;&#x2F;p&gt;
&lt;p&gt;Access rules have to be redefined on the &lt;code&gt;meta&lt;&#x2F;code&gt; backend as it can&#x27;t communicate who is actually accessing the frontend, for the backends the requests are coming from the LDAP server.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;manually-running-slapd&quot;&gt;Manually running &lt;code&gt;slapd&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Manually running &lt;code&gt;slapd&lt;&#x2F;code&gt; can be useful for testing or building containers.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;slapd&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;h&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ldapi:&#x2F;&#x2F; ldap:&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;path&#x2F;to&#x2F;slapd.conf&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 64&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Where:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;-h&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Space separated list of active sockets as URIs
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;-f&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		path to (&quot;legacy&quot;) configuration file
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;-d&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Debug&#x2F;Log level
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-text&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;manned.org&amp;#x2F;man&amp;#x2F;slapd&quot; &gt;
		man slapd
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Understanding Lua Expressions - Live Posts</title>
        <published>2025-02-01T00:00:00+00:00</published>
        <updated>2025-02-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/lua-expressions-live-posts-1/"/>
        <id>https://slatecave.net/blog/lua-expressions-live-posts-1/</id>
        
        <summary type="text">Posts I made while trying to understand how lua expressions work.</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/lua-expressions-live-posts-1/">&lt;h2 id=&quot;what-is-this-about&quot;&gt;What is this about?&lt;&#x2F;h2&gt;
&lt;p&gt;This is a collection of live posts I made on the Fediverse while diving into Lua expressions. I&#x27;m releasing these in the hopes that they&#x27;ll be useful, not just to future me, but also to other people. The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;site-source.slatecave-net&#x2F;src&#x2F;branch&#x2F;main&#x2F;content&#x2F;blog&#x2F;2024-12-05_lua_expressions.md&quot;&gt;corresponding proper blog post is stuck in its draft stage&lt;&#x2F;a&gt; until I pick the project back up again.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;on-jumplists&quot;&gt;On Jumplists&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pleroma.envs.net&#x2F;notice&#x2F;AoXvlNznkWkThmIzqa&quot;&gt;Thread started on 2024-11-29&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Okay, time to take those jumplists the lua 5.4 compiler talks about all the time apart:&lt;&#x2F;p&gt;
&lt;p&gt;They seem to be for patching jump instructions to their proper destination, until then the destination of a jump instruction is either the address of another instruction (linked list) or &lt;code&gt;NO_JUMP&lt;&#x2F;code&gt; (= &lt;code&gt;-1&lt;&#x2F;code&gt;, jump to self) to end the list. The entry point is the intermediate expression descriptor (which represents an expression that isn’t fully compiled yet), which hosts a &lt;code&gt;t&lt;&#x2F;code&gt; (true jump) and &lt;code&gt;f&lt;&#x2F;code&gt; (false jump) index, they have the same semantics as the unpatched destination addresses.&lt;&#x2F;p&gt;
&lt;p&gt;Now I’m curious about the functions that manipulate those lists so I have them on my cheatsheet for quick lookup.&lt;&#x2F;p&gt;
&lt;p&gt;Also I want to find out what “true jump” and “false jump” mean exactly, where are  they patched with which addresses.&lt;&#x2F;p&gt;
&lt;p&gt;And I want to find out if that mechanism interacts or is reused outside of expressions.&lt;&#x2F;p&gt;
&lt;p&gt;Update: I also want to find out which part generates the &lt;code&gt;TESTSET&lt;&#x2F;code&gt; instructions, they seem important.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Just had an idea … searching noises … looks like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;floss.social&#x2F;@alcinnz&quot;&gt;@alcinnz@floss.social&lt;&#x2F;a&gt; was in there before me: https:&#x2F;&#x2F;adrian.geek.nz&#x2F;gnu_docs&#x2F;scripting-langs.html#lua&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note from future Slatian:&lt;&#x2F;b&gt; Adrian explains Lua on a higher level than I do here, which is &lt;strong&gt;very useful&lt;&#x2F;strong&gt; for getting an overview.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Lua makes pretty important split between jumplists that need a value and jumplists that are entirely controlled by &lt;code&gt;TESTSET&lt;&#x2F;code&gt; instructions and therefore bring their own value (this seems to be an optimization for or and and).&lt;&#x2F;p&gt;
&lt;p&gt;There are a lot of places in the code that treat &lt;code&gt;TESTSET&lt;&#x2F;code&gt; specially.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;As a little context: Each jump instruction is indirectly connected to its corresponding testcase as the testcase, if present always is the previous instruction in the bytecode array (Lua has a lookup to know which instructions are testcases), if the testcase succeeds, it skips the jump, if not the jump is executed.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;There is also the &lt;code&gt;patchlistaux()&lt;&#x2F;code&gt; function that is at the core of a lot of the compiler inner workings. It patches destination addresses and destination registers simply based on whether a value is provided (&lt;code&gt;TESTSET&lt;&#x2F;code&gt;) or not. Looks quite intimidating when one cones across it, but its actually a very peaceful critter.&lt;&#x2F;p&gt;
&lt;p&gt;The comment helps understanding it, but doesn’t make the &lt;code&gt;TESTSET&lt;&#x2F;code&gt; part obvious.&lt;&#x2F;p&gt;
&lt;p&gt;It’ll probably also be used to convert &lt;code&gt;TESTSET&lt;&#x2F;code&gt; to regular &lt;code&gt;TEST&lt;&#x2F;code&gt; Instructions (if the value isn’t needed), because the underlying helpers are capable of that.&lt;&#x2F;p&gt;
&lt;p&gt;Update: &lt;code&gt;luaK_patchlist()&lt;&#x2F;code&gt; does exactly that, patch a list to a destination address discarding the values in potential &lt;code&gt;TESTSET&lt;&#x2F;code&gt;s.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;This seems like one of those optimizations the Lua people like to write papers and even more papers about, why not about this one ???&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Another important function I didn’t have  look at until now is &lt;code&gt;luaK_getlabel&lt;&#x2F;code&gt;, this returns the index (&lt;code&gt;pc&lt;&#x2F;code&gt; — program counter) of the next to be generated instruction for use as a jump target and stores that address in the function state as the last instruction that was a jump target, so that when soon after the jump logic the code that tries to merge multiple instructions runs, it knows that there is a good reason for two instructions.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;There is a possibility for multiple expression descriptions (and therefore no limit on how many jumplists are active at any given time) as indicated by there being two expression descriptions needed for many kinds of table operations.&lt;&#x2F;p&gt;
&lt;p&gt;Which means I have to update my rewrite code to reflect that, fun!&lt;&#x2F;p&gt;
&lt;p&gt;For the rewrite I’ll opt to attach a Vec directly to the expression description to help with readability, even if it costs a few bytes extra.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Onto where the &lt;code&gt;TESTSET&lt;&#x2F;code&gt;s come from:&lt;&#x2F;p&gt;
&lt;p&gt;They are generated using &lt;code&gt;condjump()&lt;&#x2F;code&gt; in the &lt;code&gt;jumpcond()&lt;&#x2F;code&gt; (“jump_on_cond”) function, which I haven’t fully read yet, it takes an expression, generates a jump, adds the jump to the jumplists ….&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;jumpcond()&lt;&#x2F;code&gt; takes an expression, a thruthyness boolean and generatios a jump if the expression is truthy (evaluates to something not false and not nil). It is used in &lt;code&gt;luaK_goiftrue()&lt;&#x2F;code&gt; and &lt;code&gt;luaK_goiffalse()&lt;&#x2F;code&gt; they are a “fall through if given expression is {bool_from_name}, otherwise jump somewhere”. These are used by &lt;code&gt;and&lt;&#x2F;code&gt;, &lt;code&gt;or&lt;&#x2F;code&gt;, &lt;code&gt;if&lt;&#x2F;code&gt; (why is that in the parser file ???), &lt;code&gt;for&lt;&#x2F;code&gt; and when parsing conditions (&lt;code&gt;cond()&lt;&#x2F;code&gt;) in general (I guess other kinds of loop).&lt;&#x2F;p&gt;
&lt;p&gt;What makes that hard to read is, that the jumplists are all indirectly passed by modifying expressions by pointer and it isn’t really obvious where that happens …&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Maybe relevant: The &lt;code&gt;exp2reg&lt;&#x2F;code&gt; function, which compiles an expression description to bytecode that makes sure a value ends up in a specified register assumes that a &lt;code&gt;VJMP&lt;&#x2F;code&gt; (jump&#x2F;test) expression is a true jump, which gets patched to a &lt;code&gt;LOADTRUE&lt;&#x2F;code&gt; instruction.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;code&gt;exp2reg&lt;&#x2F;code&gt; also, after letting &lt;code&gt;discharge2reg&lt;&#x2F;code&gt; take care of any data shuffling uses the jump lists to patch all “true jumps” to an instruction that loads &lt;code&gt;true&lt;&#x2F;code&gt; into the given destination register and all “false jumps” to an instruction that loads &lt;code&gt;false&lt;&#x2F;code&gt;, all tests that bring their own value are patched  straight to the end of the expressions bytecode, independent of the jumplist they are in.&lt;&#x2F;p&gt;
&lt;p&gt;So “true jumps” are all jumps that go to a &lt;code&gt;LOADTRUE&lt;&#x2F;code&gt; just before the end of the expression and “false jumps” go to a &lt;code&gt;LOADFALSESKIP&lt;&#x2F;code&gt;¹ just before the end of the expression.&lt;&#x2F;p&gt;
&lt;p&gt;This also explains why &lt;code&gt;need_value&lt;&#x2F;code&gt; returns &lt;code&gt;false&lt;&#x2F;code&gt; for empty lists: An empty list has nothing to be patched to a &lt;code&gt;LOAD…&lt;&#x2F;code&gt; instruction.&lt;&#x2F;p&gt;
&lt;p&gt;I’m curious how that is used to implement &lt;code&gt;if&lt;&#x2F;code&gt; and loops, because those probably reuse the lists with slightly different semantics.&lt;&#x2F;p&gt;
&lt;p&gt;¹: The SKIP part skips the LOADTRUE directly after.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;on-re-implementing-jumplists&quot;&gt;On Re-Implementing jumplists&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pleroma.envs.net&#x2F;notice&#x2F;AoZELkyBnpLLiR0INs&quot;&gt;Thread started on 2024-11-30&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;I’ll try to implement what I know so far about jumplists in Lua as long as that knowlege is cached.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Step one: Make my &lt;code&gt;ExpressionDescription&lt;&#x2F;code&gt; a struct that contains the enum for the different kinds of expression description+Jumplists (In my code that will be a &lt;code&gt;Vec&amp;lt;usize&amp;gt;&lt;&#x2F;code&gt;, each item being an offset in the bytecode array, for readablility reasons).
And then I have to fix the code I’ve already written that relies on &lt;code&gt;ExpressionDescription&lt;&#x2F;code&gt; being an enum …&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;My &lt;code&gt;luaK_getlabel&lt;&#x2F;code&gt; equivalent fits neatly into the instruction builder struct, less code in the compiler to confuse myself.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;code&gt;patchlistaux&lt;&#x2F;code&gt; is also very cute when written down in rust with my readability optimization:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Patches all offsets in the given `list` using the [patch_testset_register] and [patch_jump_target] functions.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; If `patch_testset_register` returns true the destintion jump is patched to `vtarget`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; (&amp;quot;value target&amp;quot;), which handels paths that bring their own value via testset.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; All other test result jumps are patched to `dtarget` (&amp;quot;default target&amp;quot;), they don&amp;#39;t&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; bring a value along.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; There is also the possibility to use this with `to_reg` set to `None` and `vtarget` equal&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; to `dtartget` to make all jump sources equal in that they don&amp;#39;t bring a value.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Equivalent to `patchlistaux` in the original Lua source.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; patch_jumplist_aux&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;	&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-modifier&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	list&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;usize&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	vtarget&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	to_reg&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;u8&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	dtarget&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; usize&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; CompileError&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;	for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; pc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; list&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;		if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;patch_testset_register&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;pc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; to_reg&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;			self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;patch_jump_target&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;pc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; vtarget&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;		}&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; else&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;			self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;patch_jump_target&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;pc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; dtarget&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;		}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;	Ok&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Yep, most of that is documentation :P.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;code&gt;exp2reg&lt;&#x2F;code&gt; is implemented. Without support for variables right now, lets see if I can now connect the compiler frontend to the backend and compile a first expression. Maybe even something non-trivial like:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;lua&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-logical&quot;&gt;not&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Yes, that looks ridiculously simple, but it requires unary operator parsing, hooking that true up to the not, reversing the true and then discharging everything into a register. (Plus some jumpliist bookkeeping for more complex expressions later on)&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;I happen to know (because I tried earlier) that compiling a &lt;code&gt;not&lt;&#x2F;code&gt; is implemented in &lt;code&gt;codenot&lt;&#x2F;code&gt;, which:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;takes some shortcuts for constants, easy I already have that&lt;&#x2F;li&gt;
&lt;li&gt;for a Jump&#x2F;Test expression it calls &lt;code&gt;negatecondition&lt;&#x2F;code&gt; (probably missing in my re-implementation right now)&lt;&#x2F;li&gt;
&lt;li&gt;for Relocatable (&lt;code&gt;VRELOC&lt;&#x2F;code&gt;) and NonRelocatable (&lt;code&gt;VNONRELOC&lt;&#x2F;code&gt;) results it makes sure they end up in a free register, generates a &lt;code&gt;NOT&lt;&#x2F;code&gt; opcode and stores the instructions address as a relocateable expression.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In any case it swaps the jumplists, which now that I know their purpose makes perfect sense. It also removes all values that would be brought by the jump tables as the original value is indeed useless when one just needs a &lt;code&gt;true&lt;&#x2F;code&gt; or &lt;code&gt;false&lt;&#x2F;code&gt; that is the exact opposite of the original truthiness.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;negatecondiotion&lt;&#x2F;code&gt; simply toggles the &lt;code&gt;k&lt;&#x2F;code&gt; flag that is attached to all test instruction which negates the outcome of the test. Except for the excluded by assertion &lt;code&gt;TESTSET&lt;&#x2F;code&gt; and &lt;code&gt;TEST&lt;&#x2F;code&gt; instructions? I guess they don&#x27;t become Jump (&lt;code&gt;VJMP&lt;&#x2F;code&gt;) expressions.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; NonRelocatable and Relocatable expressions are two kinds of variable expressions where the bytecode that generates the expression result has already been generated. For NonRelocatable ones the expression result is already assigned to a fixed register. For Relocatable ones the offset of the last instruction that generates the final result is stored so it can be patched to point to any desirable register.&lt;&#x2F;p&gt;
&lt;p&gt;Conveniently the destination register always has the same representation in bytecode, so one doesn&#x27;t have to care which instruction is being patched.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note from future Slatian:&lt;&#x2F;b&gt; At this point I went onto a small sidequest to fix number parsing and then decided to get a connection to binary operators.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Almost overlooked the &lt;code&gt;freeexp&lt;&#x2F;code&gt; function, which has nothing to do with C memory management, but releases the register a &lt;code&gt;NonRelocateable&lt;&#x2F;code&gt; expression description might be holding onto.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;The &lt;code&gt;discharge2…&lt;&#x2F;code&gt; family apparently never really does anything but turn an expression into a &lt;code&gt;NonRelocatable&lt;&#x2F;code&gt; (they also don’t touch the jump tables) just for the C code to naively read the register … changed that to non mutable borrows of the expression descriptions in the rust code.&lt;&#x2F;p&gt;
&lt;p&gt;I hope that doesn’t come back to haunt me when I get around to implementing &lt;code&gt;luaK_dischargevars&lt;&#x2F;code&gt;, but for now I got less stuff to keep track of in my brain, which is good.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;code&gt;codenot&lt;&#x2F;code&gt; implemented, this one is called from &lt;code&gt;luaK_prefix&lt;&#x2F;code&gt;, which gets called from &lt;code&gt;subexpr&lt;&#x2F;code&gt; (function for unary and binary expressions, recursively calls itself) which I already have a sleleton of, getting close!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;on-binary-operands&quot;&gt;On Binary Operands&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pleroma.envs.net&#x2F;notice&#x2F;AoZw6Ev94exLxMzW2i&quot;&gt;Thread started on 2024-11-30&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As in operationm that involve two arguments.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Okay new thread for binary operands in lua 5.4.7:&lt;&#x2F;p&gt;
&lt;p&gt;I’m trying to understand how the Lua compiler translates binary operands into bytecode.&lt;&#x2F;p&gt;
&lt;p&gt;I’ve just connected the frontend with the backend of the lua compiler using the not unary operator and some simple expressions (&lt;code&gt;true&lt;&#x2F;code&gt;, &lt;code&gt;false&lt;&#x2F;code&gt;, &lt;code&gt;nil&lt;&#x2F;code&gt;, numbers).&lt;&#x2F;p&gt;
&lt;p&gt;For now to connect with the jumptable thread I’ll focus on how the and and or operators work.&lt;&#x2F;p&gt;
&lt;p&gt;My main questions are:&lt;&#x2F;p&gt;
&lt;p&gt;What exactly does the parsing loop in &lt;code&gt;subexpr&lt;&#x2F;code&gt; (which can recursively parse binary, unary and simple expressions) do?&lt;&#x2F;p&gt;
&lt;p&gt;How do &lt;code&gt;luaK_goiftrue&lt;&#x2F;code&gt; and &lt;code&gt;luaK_goiffalse&lt;&#x2F;code&gt; help in generating valid bytecode for and and or expressions.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;One of the challanges is that subexpr when calling itself returns the next operator, I already return an expression and want to avoid returning a tuple, so I also wonder if the recursive self calling could be flattened into some loops. Or the operator determined via some other way.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Wrote a little script (in lua of course) to visualize what subexpr is doing and it turns out that I can just grab the next operation where op gets assigned, to nextop, though that isn’t very efficient, which is why the lua devs chose to do it the way they did.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;ASCII Art Diagram that would be produced for the expression `1 * 2 + 3 * 4`&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 1   v = simpleexp()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; *   op = getbinopr(peek())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |   while (op.is_some and 11 &amp;gt; 0)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; `-    next()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       nextop, v2 = subexp(11)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;vvv  vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 2   v = simpleexp()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; +   op = getbinopr(peek())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |   while (X)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |     *skip*&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |   return +, 2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;^^^  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |     v = postfix(op, v, v2) &#x2F;&#x2F; *, 1, 2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |     op = nextop &#x2F;&#x2F; +&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |   while (op.is_some and 10 &amp;gt; 0)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; `-    next()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       nextop, v2 = subexp(10)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;vvv  vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 3   v = simpleexp()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; *   op = getbinopr(peek())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |   while (op.is_some and 11 &amp;gt; 10)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; `-    next()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       nextop, v2 = subexp(11)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;vvv  vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; 4   v = simpleexp()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[ ]  op = getbinopr(peek())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |   while (X)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |     *skip*&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |   return [ ], 4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;^^^  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |     v = postfix(op, v, v2) &#x2F;&#x2F; *, 3, 4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |     op = nextop &#x2F;&#x2F; [ ]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |   while (X)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |     *skip*&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |   return [ ], (* 3 4)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;^^^  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |     v = postfix(op, v, v2) &#x2F;&#x2F; +, (* 1 2), (* 3 4)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |     op = nextop &#x2F;&#x2F; [ ]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |   while (X)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |     *skip*&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |   return [ ], (+ (* 1 2) (* 3 4))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;When running it with something like &lt;code&gt;1 + 2 + 3 + 4&lt;&#x2F;code&gt; one can see that it is already flattened as far as possible (we are trying to parse a tree structure after all) for the usual left associate functions and is fully recursive for the right associative ones (like &lt;code&gt;1 ^ 2 ^ 3 ^ 4&lt;&#x2F;code&gt;), so I’ll keep the lua implementation.&lt;&#x2F;p&gt;
&lt;p&gt;Someone probably already fried their brain over this, Thank You!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; This also pretty stupidly follows the EBNF syntax notation, so a beautiful solution too, just not obvious in the C code.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Now that its sorted out that the loop and recursion simply follow the grammar notation, what do &lt;code&gt;luaK_infix&lt;&#x2F;code&gt; and &lt;code&gt;luaK_posfix&lt;&#x2F;code&gt; do?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;luaK_infix&lt;&#x2F;code&gt; does whatever needs to be done with the first operand before the second operand is involved, setting up jump logic for and and or or making sure that the operand is in a register or the constant array, depending on available opcodes.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;luaK_posfix&lt;&#x2F;code&gt; does … almost everything else … folding constants (evaluating expressions that only involve constants t compile time), merging the operands jumplists, making sure the second operand is in a register and dispatching operator specific implmenetations (like codenot, but for binary operations)&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Thunderbird: Use S&#x2F;MIME Certificates from LDAP</title>
        <published>2025-01-28T00:00:00+00:00</published>
        <updated>2025-02-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/thunderbird-ladp-and-smime/"/>
        <id>https://slatecave.net/blog/thunderbird-ladp-and-smime/</id>
        
        <summary type="text">How to get loading public certificates from an LDAP Server to work on Thunderbird.</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/thunderbird-ladp-and-smime/">&lt;h2 id=&quot;why-combine-s-mime-and-ldap&quot;&gt;Why combine S&#x2F;MIME and LDAP?&lt;&#x2F;h2&gt;
&lt;p&gt;Signing E-Mail with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;S&#x2F;MIME&quot;&gt;S&#x2F;MIME&lt;&#x2F;a&gt; has some big advantages if you are part of a bigger organisation, network or government. One specialised Team can take care of generating all the Certificates from a Certificate Authority. Everyone trusts that Team to handle giving out certificates correctly and by extension that Certificate Authority and with one Certificate import one can verify signatures from everyone in the organisation, great.&lt;&#x2F;p&gt;
&lt;p&gt;But if one has data that is worth signing it is probably also worth encrypting, which is where there is one little problem for the less technical users: One needs the public key of the receiver. Which can be imported from a file or obtained from a signed message.&lt;&#x2F;p&gt;







	
		
		
		
		
		
			
			
		
	

	
		
	

	
		
	

	
		
	

	
		
	

	
		
	


	
&lt;div class=&quot;chat-box&quot;&gt;
	&lt;img src=&quot;&amp;#x2F;resources&amp;#x2F;emoji&amp;#x2F;neofox&amp;#x2F;neofox_laptop_notice.png&quot; alt=&quot;Neofox asks critically&quot; class=&quot;chat-avatar&quot; width=&quot;64&quot; height=&quot;64&quot;&gt;
	&lt;blockquote&gt;
&lt;p&gt;You are telling me that I have to get a certificate file and import it every time I write someone new?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Do you have any idea of how many people I have to write every day?!&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t have time for your little encrypting toy!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;&#x2F;div&gt;



&lt;p&gt;Importing certificates is hard.&lt;&#x2F;p&gt;
&lt;p&gt;But so is importing contacts, some organisations have an LDAP Address book to distribute their contact lists. Conveniently S&#x2F;MIME certificates can also be distributed via an LDAP Address book (using the &lt;code&gt;userCertificate&lt;&#x2F;code&gt; field to be specific). This way the people minding their daily business can rely on the Mail client automatically figuring out who can receive encrypted Mail and with which Certificate.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Trusting a central authority for encrypted communication isn&#x27;t ideal, but it is good enough for within organisations where different departments already have to rely on each other to do their jobs and a &lt;strong&gt;huge&lt;&#x2F;strong&gt; improvement over not encrypting at all.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-to-set-it-up-with-thunderbird&quot;&gt;How to set it up with Thunderbird?&lt;&#x2F;h2&gt;
&lt;p&gt;With Thunderbird one can either manually add an LDAP Address book and install the certificates (there are enough guides for this already).&lt;&#x2F;p&gt;
&lt;p&gt;Or automate the process:&lt;&#x2F;p&gt;
&lt;p&gt;For installing the CAs Certificates as an admin the best way is to use the Enterprise Policies with the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;thunderbird&#x2F;policy-templates&#x2F;tree&#x2F;master&#x2F;templates&#x2F;central#certificates--install&quot;&gt;&lt;code&gt;Certificates -&amp;gt; Install&lt;&#x2F;code&gt; rule&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For the address book an admin can preconfigure a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kb.mozillazine.org&#x2F;User.js_file&quot;&gt;&lt;code&gt;user.js&lt;&#x2F;code&gt; file&lt;&#x2F;a&gt; with the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;enterprise.thunderbird.net&#x2F;manage-updates-policies-and-customization&#x2F;thunderbird-enterprise-tips#setup-users-to-access-ldap-information-from-the-address-book&quot;&gt;appropriate LDAP settings&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;i-have-the-address-book-but-thunderbird-doesn-t-find-the-s-mime-certificates&quot;&gt;I have the Address book, but Thunderbird doesn&#x27;t find the S&#x2F;MIME Certificates?&lt;&#x2F;h2&gt;
&lt;p&gt;If you have the LDAP Address book working, but the S&#x2F;MIME Certificates seemingly can&#x27;t be read from them:&lt;&#x2F;p&gt;
&lt;p&gt;Thunderbird doesn&#x27;t automatically download certificates from Directory Servers that aren&#x27;t the autocomplete directory Server (this is either a global setting or one bound to the E-Mail account).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; This is probably a privacy feature to not leak addresses to LDAP Servers one doesn&#x27;t already trust, but this seems like black magic if one comes from the the &quot;Why is S&#x2F;MIME broken?&quot; angle and not really explained anywhere.&lt;&#x2F;p&gt;
&lt;p&gt;If you are configuring via JavaScript those two are the important settings:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;defaultPref(&amp;#39;ldap_2.autoComplete.directoryServer&amp;#39;,&amp;#39;ldap_2.servers.AutoGEN-CompanyNameLDAP&amp;#39;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;defaultPref(&amp;#39;ldap_2.autoComplete.useDirectory&amp;#39;,true);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;(The example is taken from the LDAP settings tutorial linked in the previous section.)&lt;&#x2F;p&gt;
&lt;p&gt;In the global GUI settings you are looking for &lt;samp&gt;Composition&lt;&#x2F;samp&gt; &amp;gt; &lt;samp&gt;Addressing&lt;&#x2F;samp&gt; &amp;gt; &lt;samp&gt;When addressing messages, look for matching entries in:&lt;&#x2F;samp&gt; &amp;gt; &lt;samp&gt;Directory Server:&lt;&#x2F;samp&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There can only be &lt;strong&gt;one&lt;&#x2F;strong&gt; Server configured for the Autocomplete role, You might have to ask your LDAP Server admin to merge some address books if you have the need to configure multiple of these.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Outdated information:&lt;&#x2F;b&gt; Some places on the internet suggest setting &lt;code&gt;ldap_2.servers.&amp;lt;addressbook&amp;gt;.enable_autocomplete&lt;&#x2F;code&gt; to &lt;code&gt;true&lt;&#x2F;code&gt; for configuring multiple Servers as autocomplete providers. This no longer works.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;If you want multiple addressbooks:&lt;&#x2F;b&gt; Set up an OpenLDAP Server and merge the addressbooks using its &lt;code&gt;meta&lt;&#x2F;code&gt; backend. See my &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;today-i-learned-openldap&#x2F;&quot;&gt;Today I learned OpenLDAP&lt;&#x2F;a&gt; post, if setting up OpenLDAP seems daunting.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;As one may have been able to guess: I was stuck on that part for quite bit as the LDAP-Server with the S&#x2F;MIME Certificates was a secondary Address book and therefore not configures as the autocompleting one.&lt;&#x2F;p&gt;
&lt;p&gt;How did I find out? If a general web search isn&#x27;t helping, look at the bug tracker! Search for LDAP and S&#x2F;MIME and apparently someone who registered under the name kevin not only reported that they had the same issue, but also &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bugzilla.mozilla.org&#x2F;show_bug.cgi?id=1121450#c3&quot;&gt;found and wrote down the cause of the issue&lt;&#x2F;a&gt;! Huge Thank You!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;i-hope-that-was-useful&quot;&gt;I hope that was useful&lt;&#x2F;h2&gt;
&lt;p&gt;Tanks for reading, I hope that piece of information saved you some time and avoided a headache.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;You know what also avoids headaches?&lt;&#x2F;p&gt;
&lt;p&gt;Democracy and Human Rights: They allow people to worry about unimportant things like LDAP Address books instead of being on the wrong side of some crapheads mood that might ruin ones live.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Vote for keeping Democracy and Human Rights!&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>xdg-settings: Setting a default browser isn&#x27;t that simple</title>
        <published>2025-01-08T00:00:00+00:00</published>
        <updated>2025-01-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/xdg-settings/"/>
        <id>https://slatecave.net/blog/xdg-settings/</id>
        
        <summary type="text">A dive into how the xdg-settings script works</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/xdg-settings/">&lt;h2 id=&quot;what-is-xdg-settings&quot;&gt;What is xdg-settings?&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;xdg-settings&lt;&#x2F;code&gt; is part of the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;&quot;&gt;xdg-utils&lt;&#x2F;a&gt; and according to its &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;manned.org&#x2F;man&#x2F;xdg-settings&quot;&gt;manpage&lt;&#x2F;a&gt; it can query, check for and set different properties of an XDG-desktop.&lt;&#x2F;p&gt;
&lt;p&gt;Running &lt;code&gt;xdg-settings --list&lt;&#x2F;code&gt; reveals that it only ever got implementations for setting the default web browser and for setting URL-scheme handlers (which is the more generic case of setting a default web browser).&lt;&#x2F;p&gt;







	
		
		
		
		
		
			
			
		
	

	
		
	

	
		
	

	
		
	

	
		
	

	
		
	


	
&lt;div class=&quot;chat-box&quot;&gt;
	&lt;img src=&quot;&amp;#x2F;resources&amp;#x2F;emoji&amp;#x2F;slatian_bean&amp;#x2F;slatian_bean.png&quot; alt=&quot;Slatian Notes&quot; class=&quot;chat-avatar&quot; width=&quot;64&quot; height=&quot;64&quot;&gt;
	&lt;blockquote&gt;
&lt;p&gt;The reason I started looking into this is because someone &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;issues&#x2F;266&quot;&gt;opened an issue&lt;&#x2F;a&gt; about &lt;code&gt;xdg-open&lt;&#x2F;code&gt; ignoring the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; variable, which turned out to be &lt;code&gt;xdg-settings&lt;&#x2F;code&gt; giving out misinformation about the order of variables.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This is good by the way!&lt;&#x2F;strong&gt; if you notice something broken with a tool open an issue, even if you are wrong about the part that is broken.&lt;&#x2F;p&gt;
&lt;p&gt;Someone will help figuring out the actual issue, especially in systems with multiple components where a weirdness in one place can be the symptom of an error somewhere else.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;&#x2F;div&gt;



&lt;p&gt;I&#x27;ll take a look at the current git HEAD with the commit hash &lt;code&gt;0f6385262417f1c0c4d13bc05d95c32578272b64&lt;&#x2F;code&gt;, though &lt;code&gt;xdg-settings&lt;&#x2F;code&gt; hasn&#x27;t been touched for years, so this also applies to the &lt;code&gt;1.2.1&lt;&#x2F;code&gt; version you probably still have. (except for Lxqt, that one is new)&lt;&#x2F;p&gt;
&lt;p&gt;xdg-settings makes use of some common functionality I already &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;xdg-open-under-the-hood&#x2F;&quot;&gt;described when taking apart xdg-open&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;overview-over-xdg-settings&quot;&gt;Overview over xdg-settings&lt;&#x2F;h2&gt;
&lt;p&gt;Inside xdg-settings, it is split up into multiple sections. That is helper functions for browser setting, MIME related functionality, desktop specific functionality.&lt;&#x2F;p&gt;
&lt;p&gt;Desktops that have their own sections are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;KDE&lt;&#x2F;li&gt;
&lt;li&gt;Deepin&lt;&#x2F;li&gt;
&lt;li&gt;GNOME (Gnome 2 that is)&lt;&#x2F;li&gt;
&lt;li&gt;GNOME 3.x&lt;&#x2F;li&gt;
&lt;li&gt;Lxqt&lt;&#x2F;li&gt;
&lt;li&gt;Xfce&lt;&#x2F;li&gt;
&lt;li&gt;generic&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;While browsing the sections one will also notice common function prefixes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;get_browser_&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;check_browser_&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;set_browser_&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;get_url_scheme_handler_&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;check_url_scheme_handler_&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;set_url_scheme_handler_&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There is also a big dispatch function (&lt;code&gt;dispatch_specific&lt;&#x2F;code&gt;) that calls the appropriate function based on the given handler and the requested command. The handler is derived based on the detected desktop environment (&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;xdg-open-under-the-hood&#x2F;#desktops&quot;&gt;see the xdg-open article on desktops to learn how that works&lt;&#x2F;a&gt;) in a switch statement right at the end of the file.&lt;&#x2F;p&gt;
&lt;p&gt;The desktops are translated as follows:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Desktop&lt;&#x2F;th&gt;&lt;th&gt;Handler&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;kde&lt;&#x2F;td&gt;&lt;td&gt;kde&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;deepin&lt;&#x2F;td&gt;&lt;td&gt;deepin&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;gnome&lt;&#x2F;td&gt;&lt;td&gt;gnome&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;gnome3&lt;&#x2F;td&gt;&lt;td&gt;gnome3&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;cinnamon&lt;&#x2F;td&gt;&lt;td&gt;gnome3&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;lxde&lt;&#x2F;td&gt;&lt;td&gt;lxde&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;mate&lt;&#x2F;td&gt;&lt;td&gt;mate&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;lxqt&lt;&#x2F;td&gt;&lt;td&gt;lxqt&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;xfce&lt;&#x2F;td&gt;&lt;td&gt;xfce&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;generic&lt;&#x2F;td&gt;&lt;td&gt;generic&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;enlightenment&lt;&#x2F;td&gt;&lt;td&gt;generic&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;code&gt;wsl&lt;&#x2F;code&gt;, &lt;code&gt;cygwin&lt;&#x2F;code&gt;, &lt;code&gt;darwin&lt;&#x2F;code&gt; and &lt;code&gt;flatpak&lt;&#x2F;code&gt; error with &quot;unknown desktop environment&quot;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;listing-the-supported-settings&quot;&gt;Listing the supported settings&lt;&#x2F;h2&gt;
&lt;p&gt;Listing the supported settings is implemented in an interesting way.&lt;&#x2F;p&gt;
&lt;p&gt;The dispatch function contains multiple switch-case statements for the supported parameters, one of each line is annotated with  comment, &lt;code&gt;#PROP: {description goes here}&lt;&#x2F;code&gt;. The list function greps the file for line containing that comment and then uses some regex magic to format that into a human readable list.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-helper-functions&quot;&gt;The helper functions&lt;&#x2F;h2&gt;




	


&lt;dl class=&quot;&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;get_browser_mime &amp;lt;mimetype&amp;gt;&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		A wrapper around &lt;code&gt;xdg-mime query default $1&lt;&#x2F;code&gt; that defaults to &lt;code&gt;text&#x2F;html&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;fix_local_desktop_file &amp;lt;desktop-file-name&amp;gt; &amp;lt;mimetype&amp;gt;&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		If a desktop file exists locally for the user make sure it has a given mime-type in its list of supported mime types.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;set_browser_mime &amp;lt;desktop-file-name&amp;gt; &amp;lt;mimetype&amp;gt;&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This attempts to undo any desktop changes using &lt;code&gt;fix_local_desktop_file&lt;&#x2F;code&gt; and then uses &lt;code&gt;xdg-mime default&lt;&#x2F;code&gt; to set the default browser, checks if the change happened and sets it back if the change didn&#x27;t have the desired effect.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;generic-browser-configuration&quot;&gt;Generic Browser configuration&lt;&#x2F;h2&gt;
&lt;p&gt;The generic browser configuration makes use of the already mentioned helper functions.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;getting-the-default-browser&quot;&gt;Getting the default Browser&lt;&#x2F;h3&gt;
&lt;p&gt;This is implemented in &lt;code&gt;get_browser_generic&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; environment variable is set, the script tries resolving the command to a desktop file, and if successful outputs that as the default browser. If not the script continues.&lt;&#x2F;p&gt;
&lt;p&gt;If the mimetype &lt;code&gt;x-scheme-handler&#x2F;http&lt;&#x2F;code&gt; resolves to a desktop file that is returned as the default browser. If not the script continues.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Bug Note:&lt;&#x2F;b&gt; The order of first checking the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; variable and then checking the mimetype is reversed from how &lt;code&gt;xdg-open&lt;&#x2F;code&gt; reads them. It also puts a less granular configuration mechanism over a more granular one. I consider this a bug.&lt;&#x2F;p&gt;
&lt;p&gt;If the binary &lt;code&gt;x-www-browser&lt;&#x2F;code&gt; resolves to a desktop file that is returned.&lt;&#x2F;p&gt;
&lt;p&gt;Otherwise the script exits wit exit code &lt;code&gt;4&lt;&#x2F;code&gt; (Action failed)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;checking-the-default-browser&quot;&gt;Checking the default Browser&lt;&#x2F;h3&gt;
&lt;p&gt;Checking the default Browser is distinct from getting and comparing the default browser in that instead of checking the first setting that gives a sensible value it checks all relevant settings and only returns true if all of those settings apply.&lt;&#x2F;p&gt;
&lt;p&gt;This is implemented in &lt;code&gt;check_browser_generic&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There are three functions that have to resolve to the given desktop file before the check function says &lt;code&gt;yes&lt;&#x2F;code&gt;, that is the default browser:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The browser getting function &lt;code&gt;get_browser_generic&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Resolving the handler for the &lt;code&gt;text&#x2F;html&lt;&#x2F;code&gt; mimetype&lt;&#x2F;li&gt;
&lt;li&gt;Resolving the scheme handler for &lt;code&gt;https&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;setting-the-default-browser&quot;&gt;Setting the default Browser&lt;&#x2F;h3&gt;
&lt;p&gt;This is implemented in &lt;code&gt;set_browser_generic&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Bug Note:&lt;&#x2F;b&gt; This function errors if the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; environment variable is set, this is related to &lt;code&gt;xdg-settings&lt;&#x2F;code&gt; considering &lt;code&gt;BROWSER&lt;&#x2F;code&gt; to be higher priority than the more granular &lt;code&gt;x-scheme-handler&#x2F;&lt;&#x2F;code&gt; mimetypes.&lt;&#x2F;p&gt;
&lt;p&gt;After testing for the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; environment variable the set function tests if the desktop file resolves to a binary, this makes sure that the requested file exists and has a non-empty &lt;code&gt;Exec&lt;&#x2F;code&gt; key which is the minimum needed to actually open URLs.&lt;&#x2F;p&gt;
&lt;p&gt;It the n uses the &lt;code&gt;set_browser_mime&lt;&#x2F;code&gt; to set the following mimetypes to be handled by the requested desktop file:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;text&#x2F;html&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;x-scheme-handler&#x2F;http&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;x-scheme-handler&#x2F;https&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;x-scheme-handler&#x2F;about&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;x-scheme-handler&#x2F;unknown&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;generic-url-scheme-configuration&quot;&gt;Generic URL scheme configuration&lt;&#x2F;h2&gt;
&lt;p&gt;Generic URL scheme configuration is mostly wrapping around the already described helpers with some special treatment for the &lt;code&gt;http&lt;&#x2F;code&gt; and &lt;code&gt;https&lt;&#x2F;code&gt; schemes when the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; environment variables are set.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;getting-the-scheme-handler&quot;&gt;Getting the scheme handler&lt;&#x2F;h3&gt;
&lt;p&gt;This is implemented in &lt;code&gt;get_url_scheme_handler_generic&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Passes the call through to the &lt;code&gt;get_browser_mime&lt;&#x2F;code&gt; helper with the mimetype &lt;code&gt;x-scheme-handler&#x2F;{scheme}&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Except when &lt;code&gt;BROWSER&lt;&#x2F;code&gt; is set and the scheme is either &lt;code&gt;http&lt;&#x2F;code&gt; or &lt;code&gt;https&lt;&#x2F;code&gt;, then it calls &lt;code&gt;get_browser_generic&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;checking-the-scheme-handler&quot;&gt;Checking the scheme handler&lt;&#x2F;h3&gt;
&lt;p&gt;This is implemented in &lt;code&gt;check_url_scheme_handler_generic&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Tests if the desktop file resolves to a binary and if so it tests if it is returned by &lt;code&gt;get_url_scheme_handler_generic&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;setting-the-urls-scheme-handler&quot;&gt;Setting the URLs scheme handler&lt;&#x2F;h3&gt;
&lt;p&gt;This is implemented in &lt;code&gt;set_url_scheme_handler_generic&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Bug Note:&lt;&#x2F;b&gt; This function again has special handling for &lt;code&gt;BROWSER&lt;&#x2F;code&gt; being set the scheme being &lt;code&gt;http&lt;&#x2F;code&gt; or &lt;code&gt;https&lt;&#x2F;code&gt; that it refuses to work with an error message that it can&#x27;t change the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; variable.&lt;&#x2F;p&gt;
&lt;p&gt;If the desktop file resolves to a command it uses &lt;code&gt;set_browser_mime&lt;&#x2F;code&gt; with &lt;code&gt;x-scheme-handler&#x2F;{scheme}&lt;&#x2F;code&gt; as the mimetype.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;to-be-continued&quot;&gt;To be continued …&lt;&#x2F;h2&gt;
&lt;p&gt;This post won&#x27;t cover the special cases for each desktop. I&#x27;m taking a slow start to 2025 and will describe those in a followup post.&lt;&#x2F;p&gt;
&lt;p&gt;Reading through &lt;code&gt;xdg-settings&lt;&#x2F;code&gt; really shows that it hasn&#x27;t been maintained for a while (which isn&#x27;t surprising given that the xdg-utils were effectively abandoned for multiple years).&lt;&#x2F;p&gt;
&lt;p&gt;This is a call to action and for help: See if your favourite desktop is implemented correctly in &lt;code&gt;xdg-settings&lt;&#x2F;code&gt; and if not open an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;issues&quot;&gt;issue&lt;&#x2F;a&gt; or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;merge_requests&quot;&gt;merge request&lt;&#x2F;a&gt;. &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;about&#x2F;me&#x2F;#contact&quot;&gt;I&#x27;ll help you if you want&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks for reading, I hope you had an incident free calendar rollover!&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Why not use &amp;&amp; as a shortcut in shell scripting</title>
        <published>2024-10-07T00:00:00+00:00</published>
        <updated>2024-10-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/why-i-do-not-use-amp-amp-in-shell-scripting/"/>
        <id>https://slatecave.net/blog/why-i-do-not-use-amp-amp-in-shell-scripting/</id>
        
        <summary type="text">Ever seen some shell code that seems double negated, here is why.</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/why-i-do-not-use-amp-amp-in-shell-scripting/">&lt;h2 id=&quot;what-am-i-talking-about&quot;&gt;What am I Talking About&lt;&#x2F;h2&gt;
&lt;p&gt;If you&#x27;ve been shell scripting for some time you have most probably seen the following construct:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-logical-expression&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-logical&quot;&gt; -n&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;something&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-logical-expression&quot;&gt; ]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; foo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This runs the &lt;code&gt;foo&lt;&#x2F;code&gt; command, but only if &lt;code&gt;$something&lt;&#x2F;code&gt; isn&#x27;t empty, a very useful and common thing to check for in some scripts. So far no problem.&lt;&#x2F;p&gt;
&lt;p&gt;Another useful thing in scripts is &lt;code&gt;set -e&lt;&#x2F;code&gt; which aborts the whole script if some &quot;line&quot; (as in collection of commands) returns with a non-zero exit code. It makes sure that a shell script exits instead of causing havoc.&lt;&#x2F;p&gt;
&lt;p&gt;The problem now arises when one combines the two.&lt;&#x2F;p&gt;
&lt;p&gt;Now if &lt;code&gt;$something&lt;&#x2F;code&gt; isn&#x27;t empty everything works fines as before, but if it is empty, the test returns an error code, which gets treated as a signal to stop the script. Not good.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-to-fix-it-the-verbose-way&quot;&gt;How to Fix it the Verbose Way&lt;&#x2F;h2&gt;
&lt;p&gt;One place a command is allowed to fail is inside the condition of an &lt;code&gt;if&lt;&#x2F;code&gt; statement, pretty obvious when one says it out loud, because that is literally the job of the &lt;code&gt;if&lt;&#x2F;code&gt;, redirect the flow of execution based on whether a command was successful or not.&lt;&#x2F;p&gt;
&lt;p&gt;So the above can be rewritten to:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function z-builtin&quot;&gt;set&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;e&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; …&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;if&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-logical-expression&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-logical&quot;&gt; -n&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;something&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-logical-expression&quot;&gt; ]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; ;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;	foo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which works as intended again, but is kind of bulky.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-working-shortcut&quot;&gt;The Working Shortcut&lt;&#x2F;h2&gt;
&lt;p&gt;A way of using a shortcut with &lt;code&gt;set -e&lt;&#x2F;code&gt; is rephrasing the problem so that the test returning false gets caught.&lt;&#x2F;p&gt;
&lt;p&gt;For this to happen one has to invert the condition and catch when it fails, luckily most commands used in such shortcut condition usually also support testing for the opposite.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-text&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;man.voidlinux.org&amp;#x2F;test&quot; &gt;
		Manual for &lt;code&gt;test&lt;&#x2F;code&gt; and &lt;code&gt;[&lt;&#x2F;code&gt;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;So that the initial construct can be rewritten as:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-logical-expression&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-logical&quot;&gt; -z&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;something&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-logical-expression&quot;&gt; ]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;|&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; foo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;-z&lt;&#x2F;code&gt; tests is a string is empty, so the exact opposite of &lt;code&gt;-n&lt;&#x2F;code&gt;, if the test for emptiness fails the command gets run.&lt;&#x2F;p&gt;
&lt;p&gt;An easier way to read this is:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Either &lt;code&gt;$something&lt;&#x2F;code&gt; is empty (happy) or we run &lt;code&gt;foo&lt;&#x2F;code&gt; (also happy)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;that-s-it&quot;&gt;That&#x27;s it!&lt;&#x2F;h2&gt;
&lt;p&gt;This of course depends on &lt;code&gt;foo&lt;&#x2F;code&gt; succeeding, which I assumed the whole time for readability. If your shellscript also makes that assumption it is usually a very good idea to use &lt;code&gt;set -e&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Also this doesn&#x27;t affect what you use inside the condition of an &lt;code&gt;if&lt;&#x2F;code&gt; statement, there &lt;code&gt;&amp;amp;&amp;amp;&lt;&#x2F;code&gt; is completely fine.&lt;&#x2F;p&gt;







	
		
		
		
		
		
			
			
		
	

	
		
	

	
		
	

	
		
	


	
&lt;div class=&quot;chat-box&quot;&gt;
	&lt;img src=&quot;&amp;#x2F;resources&amp;#x2F;emoji&amp;#x2F;slatian_bean&amp;#x2F;slatian_bean_happy.png&quot; alt=&quot;A happy Slatian notes:&quot; class=&quot;chat-avatar&quot; width=&quot;64&quot; height=&quot;64&quot;&gt;
	&lt;blockquote&gt;
&lt;p&gt;Thanks for reading! I know it has been a while.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want to learn more, see &lt;a href=&quot;&#x2F;topics&#x2F;shell&quot;&gt;my other shell articles&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;&#x2F;div&gt;




	

&lt;ul class=&quot;link-list&quot;&gt;
&lt;li&gt;&lt;a class=&quot;decoration-action-next&quot; href=&quot;&#x2F;notebook&#x2F;portable-shell&#x2F;&quot;&gt;Portable Shell-Scripting&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;decoration-action-next&quot; href=&quot;&#x2F;notebook&#x2F;shell-null-termination&#x2F;&quot;&gt;Shell Null Termination &#x2F; Separation&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

</content>
    </entry>
    <entry xml:lang="en">
        <title>Fixing PWM flicker on the Arduino UNO R3</title>
        <published>2024-04-08T00:00:00+00:00</published>
        <updated>2024-04-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/fixing-pwm-flicker/"/>
        <id>https://slatecave.net/blog/fixing-pwm-flicker/</id>
        
        <summary type="text">Hooked up an RGB LED to an Arduino and it started to flicker when combining colours? This is how I found the solution.</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/fixing-pwm-flicker/">&lt;h2 id=&quot;what-happened&quot;&gt;What happened?&lt;&#x2F;h2&gt;
&lt;p&gt;Since I&#x27;m trying to tell a story here, let&#x27;s start at the beginning.&lt;&#x2F;p&gt;
&lt;p&gt;I have an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;arduino.clock_os&quot;&gt;Alarm clock&lt;&#x2F;a&gt; that reliably wakes me up every workday, featuring a piezo beeper and an RGB backlit LCD connected to an ATmega328P Microcontroller that cosplays as an Arduino UNO R3.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.arduino.cc&#x2F;reference&#x2F;en&#x2F;language&#x2F;functions&#x2F;advanced-io&#x2F;tone&#x2F;&quot;&gt;&lt;code&gt;tone()&lt;&#x2F;code&gt; function&lt;&#x2F;a&gt; messes up the PWM on the Arduino Pins 3 and 11. (They are using the same timer that also generates the beeper frequency.)&lt;&#x2F;p&gt;
&lt;p&gt;No problem I though, I&#x27;ll hook up my RGB backlight up to Pins 5, 9 and 10 …&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; My previous setup had one LED on pin 3 instead of pin 5 because I wasn&#x27;t aware of the &lt;code&gt;tone()&lt;&#x2F;code&gt; functions side effects when building the hardware.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;&#x2F;h2&gt;
&lt;p&gt;The problem was now that the display seemed to be flickering, turning up one colour at a time to the maximum doesn&#x27;t result in flicker.&lt;&#x2F;p&gt;
&lt;p&gt;My first suspicion was a software bug … Nope, cant be — setting the LEDs just once a second also makes it flicker.&lt;&#x2F;p&gt;
&lt;p&gt;Also the flicker only occurs when the LED on pin 5 was involved, mixing the LEDs on pins 9 and 10 worked fine.&lt;&#x2F;p&gt;
&lt;p&gt;So looking up the &lt;a href=&quot;&#x2F;notebook&#x2F;atmega328p&#x2F;#pinout-table&quot;&gt;Pinout&lt;&#x2F;a&gt; which I was reminded that there are three timers inside the ATmega328P, each capable of driving two PWM channels. Pins 9 and 10 are on timer 1, while pin 5 is on timer 0 … suspicious.&lt;&#x2F;p&gt;
&lt;p&gt;The obvious difference was that one uses an 8-bit counter and the other 16 bit one, so maybe some thing wrong with the 16-bit timer? (Nope.)&lt;&#x2F;p&gt;
&lt;p&gt;At this point I found an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;electronics.stackexchange.com&#x2F;questions&#x2F;687041&#x2F;fluctuating-brightness-in-rgb-led-strand-controlled-by-arduino&quot;&gt;answer on the Electronics stack exchange&lt;&#x2F;a&gt; pointing out that what I already knew as timer 0 runs at a different PWM frequency.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Second Theory: They are not on the same clock and I see some fancy interference pattern …
So will the flickering when I reprogram timer 0 to the same setting as the others?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Some digging in the Arduino code I &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arduino&#x2F;ArduinoCore-avr&#x2F;blob&#x2F;63092126a406402022f943ac048fa195ed7e944b&#x2F;cores&#x2F;arduino&#x2F;wiring.c#L247&quot;&gt;found where it configures the timers&lt;&#x2F;a&gt; with a helpful comment:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;on the ATmega168, timer 0 is also used for fast hardware PWM
(using phase-correct PWM would mean that timer 0 overflowed half as often
resulting in different millis() behaviour on the ATmega8 and ATmega168)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;and a bit further down the code:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;timers 1 and 2 are used for phase-correct hardware PWM
this is better for motors as it ensures an even waveform&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The ATmega168 is the predecessor and quite similar to the ATmega328P so I guessed that it still applies. With timer 0 being responsible for the correct behaviour of &lt;code&gt;millis()&lt;&#x2F;code&gt; reconfiguring it isn&#x27;t a path I want to go down.&lt;&#x2F;p&gt;
&lt;p&gt;But can I switch timer 1 to that non-phase correct mode that timer 0 is using?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;If you want to follow the code:&lt;&#x2F;b&gt; The &lt;code&gt;cbi()&lt;&#x2F;code&gt; and &lt;code&gt;sbi()&lt;&#x2F;code&gt; functions clear and set bits in values, in this case the processors registers. All the cryptic symbols of upper case characters and numbers are the names of registers and bits and explained in the Datasheet.&lt;&#x2F;p&gt;
&lt;p&gt;Consulting the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.microchip.com&#x2F;DS40002061B&quot;&gt;ATmega328P Datasheet (PDF)&lt;&#x2F;a&gt; to find out what all the options mean:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Both timers are on the System clock divided by 64&lt;&#x2F;li&gt;
&lt;li&gt;timer 1 is in Phase correct mode (already knew that)&lt;&#x2F;li&gt;
&lt;li&gt;timer 1 is using only 8 of its 16 bits&lt;&#x2F;li&gt;
&lt;li&gt;timer 0 is in a mode called fast PWM, which just means really simple and barebones PWM&lt;&#x2F;li&gt;
&lt;li&gt;The Fast-PWM effectively doubles the PWM frequency&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;the-solution&quot;&gt;The Solution&lt;&#x2F;h2&gt;
&lt;p&gt;So the &lt;strong&gt;only&lt;&#x2F;strong&gt; difference is one Timer is in &lt;i&gt;Fast PWM&lt;&#x2F;i&gt; and the other is in &lt;i&gt;Phase Correct PWM&lt;&#x2F;i&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;If you skipped here:&lt;&#x2F;b&gt; The ATmega328P in the UNO R3 has three timers, PWM pins 5 and 6 are hooked up to timer 0 which is in a different mode than the other timers for the &lt;code&gt;millis()&lt;&#x2F;code&gt; function to work.&lt;&#x2F;p&gt;
&lt;p&gt;So lets try the pretty obvious and switch timer 1 to Fast PWM, which is just one bit according to the Datasheet.&lt;&#x2F;p&gt;
&lt;p&gt;So I add the following code to my &lt;code&gt;setup()&lt;&#x2F;code&gt; function:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Set timer&#x2F;counter 1 from 8-bit phase correct to 8-bit fast&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;bitWrite&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;TCCR1B&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; WGM12&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And … the flickering is gone. Problem solved.&lt;&#x2F;p&gt;
&lt;p&gt;If you are now interested in more details on how PWM works and what those timers actually do:
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-action-next&quot; href=&quot;&amp;#x2F;notebook&amp;#x2F;atmega328p&amp;#x2F;#pwm-and-timer-counters&quot; &gt;
		ATmega328P: PWM and Timer&#x2F;Counters
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-it-flickers&quot;&gt;Why it flickers?&lt;&#x2F;h2&gt;
&lt;p&gt;Two PWM signals one running at double the frequency of the other shouldn&#x27;t noticeably interfere when the slower one is running at 490Hz.&lt;&#x2F;p&gt;
&lt;p&gt;The problem is that they aren&#x27;t really double with how the two involved modes work.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;b&gt;Fast PWM&lt;&#x2F;b&gt; mode simply counts up and wraps from 255 to 0, making one iteration &lt;b&gt;256 cycles&lt;&#x2F;b&gt; long.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;b&gt;Phase Correct PWM&lt;&#x2F;b&gt; mode counts up until 255 and then starts counting down again, but to arrive at 0 it takes &lt;b&gt;510 cycles&lt;&#x2F;b&gt;. An off by two error resulting in not quite double the frequency.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;The problem can be illustrated with a 2 bit counter. The second time the Fast mode finishes a full iteration the Phase Correct Mode is already two cycle into the third.&lt;&#x2F;figcaption&gt;
	&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Fast&lt;&#x2F;th&gt;         &lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;td&gt;3&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;td&gt;3&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;th&gt;Phase Correct&lt;&#x2F;th&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;td&gt;3&lt;&#x2F;td&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
	&lt;figcaption&gt;(Count the steps between the numbers, those are the cycles, not the numbers themselves)&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;So the actual frequencies are:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Mode&lt;&#x2F;th&gt;&lt;th&gt;Frequency&lt;&#x2F;th&gt;&lt;th&gt;Calculation&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Fast&lt;&#x2F;td&gt;&lt;td&gt;976.6Hz&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;16Mhz &#x2F; 64 &#x2F; 256&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Phase correct&lt;&#x2F;td&gt;&lt;td&gt;490.2Hz&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;16MHz &#x2F; 64 &#x2F; 510&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;If you were to plot those two frequencies overlapping each other you will notice that the interference matches a 3.8Hz wave, which should roughly match the low frequency flicker you were observing.&lt;&#x2F;p&gt;

&lt;a href=&quot;.&amp;#x2F;interference-plot.png&quot;&gt;
&lt;picture class=&quot;content-block&quot;&gt;


	
		
		
		
			
			
	&lt;source srcset=&quot;.&#x2F;interference-plot-dark.png&quot; media=&quot;(prefers-color-scheme: dark)&quot;&gt;
			
		
	

	
		
		
		
			
	&lt;source srcset=&quot;.&#x2F;interference-plot-light.png&quot; media=&quot;(prefers-color-scheme: light)&quot;&gt;
			
			
		
	

	
		
		
		
	

	

	

	

	


	&lt;img src=&quot;.&amp;#x2F;interference-plot.png&quot; alt=&quot;Graph that shows, that a 490.2Hz wave added to a 976.6Hz wave generates an interference pattern where the peeks match an overlaid 3.8Hz wave on a 5 second sample.&quot;&#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;a&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Group based Permissions using polkit</title>
        <published>2024-01-09T00:00:00+00:00</published>
        <updated>2024-04-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/networkmanager-group-using-polkit/"/>
        <id>https://slatecave.net/blog/networkmanager-group-using-polkit/</id>
        
        <summary type="text">Access things like NetworkManager based on UNIX groups.</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/networkmanager-group-using-polkit/">&lt;h2 id=&quot;what-and-why&quot;&gt;What and why?&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m the kind of creature that really likes NetworkManager and avoids systemd when possible.&lt;&#x2F;p&gt;
&lt;p&gt;The problem arises that, when one isn&#x27;t using (e)logind things like being able to configure networks don&#x27;t work anymore. Reason being that polkit is configured to only allow these actions for logged in sessions, but nothing told it that a session is active.&lt;&#x2F;p&gt;
&lt;p&gt;Luckily those rules are not set in stone and one can reconfigure polkit however they like.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; NetworkManager only supports polkit for configuring who can do what. This is okay because it is supposed to manage networks, not permissions.&lt;&#x2F;p&gt;
&lt;p&gt;For my purposes I want to be able to send basic commands to NetworkManager because I&#x27;m in the &lt;code&gt;wheel&lt;&#x2F;code&gt; (aka. &lt;code&gt;sudo&lt;&#x2F;code&gt;) group, independent of me being logged in.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Update on 2024-04-18:&lt;&#x2F;b&gt; Previously I forgot to include the &lt;code&gt;org.freedesktop.NetworkManager.settings.modify.system&lt;&#x2F;code&gt; action which prevented connecting to new wireless networks.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;understanding-polkit&quot;&gt;Understanding polkit&lt;&#x2F;h2&gt;
&lt;p&gt;This was the first time I messed with polkit which is very well documented in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.archlinux.org&#x2F;man&#x2F;polkit.8&quot;&gt;polkit(8) manpage&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Polkit is a service with its only task being to answer one question&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Is &lt;i&gt;subject&lt;&#x2F;i&gt; allowed to do &lt;i&gt;action&lt;&#x2F;i&gt;?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The &lt;i&gt;subject&lt;&#x2F;i&gt; is roughly equivalent to a user.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;i&gt;action&lt;&#x2F;i&gt; is a more or less rough description of what the subject wants to do identified by an &lt;i&gt;id&lt;&#x2F;i&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Actions are described by XML-Files in &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;polkit-1&#x2F;actions&#x2F;&lt;&#x2F;code&gt;. These contain localised names, icon-names and some sane default permissions for each &lt;i&gt;action&lt;&#x2F;i&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In addition to actions and the default permissions, polkit supports rules written in JavaScript. These live in files in &lt;code&gt;&#x2F;etc&#x2F;polkit-1&#x2F;rules.d&#x2F;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; There is also &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;polkit-1&#x2F;rules.d&#x2F;&lt;&#x2F;code&gt; but that one is intended for packages. As an admin &lt;code&gt;&#x2F;etc&lt;&#x2F;code&gt; is the preferred place.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;networkmanager-rules&quot;&gt;NetworkManager rules&lt;&#x2F;h2&gt;
&lt;p&gt;Before writing the rule I need the action ids that are used to configure NetworkManager. These can be found by searching the &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;polkit-1&#x2F;actions&#x2F;org.freedesktop.NetworkManager.policy&lt;&#x2F;code&gt; file for all rules that work without any extra authentication given an active session.&lt;&#x2F;p&gt;
&lt;p&gt;The results are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.enable-disable-network&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.enable-disable-wifi&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.enable-disable-wimax&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.enable-disable-wwan&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.network-control&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.settings.modify.own&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.settings.modify.system&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.wifi.scan&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.wifi.share.open&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.wifi.share.protected&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;b&gt;Note on inactive sessions:&lt;&#x2F;b&gt; Even if an action is allowed for inactive sessions it isn&#x27;t allowed when that session state can&#x27;t be determined which is the case when (e)logind is missing.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;polkit-debug-mode&quot;&gt;Polkit debug mode&lt;&#x2F;h2&gt;
&lt;p&gt;To save you some frustration with debugging: Polkit has a debug mode which is probably turned off when launched through your service manager of choice using the &lt;code&gt;--no-debug&lt;&#x2F;code&gt; option.&lt;&#x2F;p&gt;
&lt;p&gt;To get the debug output.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Stop the &lt;code&gt;polkit&lt;&#x2F;code&gt; service&lt;&#x2F;li&gt;
&lt;li&gt;Run &lt;code&gt;&#x2F;usr&#x2F;lib&#x2F;polkit-1&#x2F;polkitd&lt;&#x2F;code&gt; as root in a terminal&lt;&#x2F;li&gt;
&lt;li&gt;Start the service again when done with debugging&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Polkit will drop privileges automatically after setting itself up.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;adding-a-rule&quot;&gt;Adding a rule&lt;&#x2F;h2&gt;
&lt;p&gt;According to the manual I should be able to just place a &lt;code&gt;.rules&lt;&#x2F;code&gt; file in &lt;code&gt;&#x2F;etc&#x2F;polkit&#x2F;rules.d&#x2F;&lt;&#x2F;code&gt; and polkit will pick it up automatically. If you have polkit in debug mode you&#x27;ll even get feedback whether your rules are valid JavaScript or not.&lt;&#x2F;p&gt;
&lt;p&gt;For the NetworkManager scenario my script adds a rule function that first checks if the action id is in an array of allowed actions, then checks if the &lt;i&gt;subject&lt;&#x2F;i&gt; is in the &lt;code&gt;wheel&lt;&#x2F;code&gt; group and if so returns that the action is allowed.&lt;&#x2F;p&gt;
&lt;p&gt;If the rule doesn&#x27;t return a result polkit will fall back to other rules and eventually the defaults.&lt;&#x2F;p&gt;
&lt;p&gt;Translated to JavaScript the result is the following.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;polkit&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;addRule&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;function&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;action&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt; subject&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;	var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; allowed_actions&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;		&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;org.freedesktop.NetworkManager.enable-disable-network&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;		&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;org.freedesktop.NetworkManager.enable-disable-wifi&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;		&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;org.freedesktop.NetworkManager.enable-disable-wimax&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;		&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;org.freedesktop.NetworkManager.enable-disable-wwan&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;		&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;org.freedesktop.NetworkManager.network-control&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;		&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;org.freedesktop.NetworkManager.settings.modify.own&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;		&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;org.freedesktop.NetworkManager.settings.modify.system&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;		&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;org.freedesktop.NetworkManager.wifi.scan&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;		&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;org.freedesktop.NetworkManager.wifi.share.open&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;		&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;org.freedesktop.NetworkManager.wifi.share.protected&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;	if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;allowed_actions&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;indexOf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;action&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;		if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;subject&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;isInGroup&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;wheel&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;			return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; polkit&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;YES&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;		}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;other-things-polkit-rules-could-do&quot;&gt;Other Things polkit Rules could do&lt;&#x2F;h2&gt;
&lt;p&gt;By testing for an action id prefix and (non-)membership of a group you could also deny access to a service by returning &lt;code&gt;polkit.Result.NO&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Polkit can also call external commands and make a decision based on success or failure of a command.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-text&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;man.archlinux.org&amp;#x2F;man&amp;#x2F;polkit.8#Authorization_Rules_Examples&quot; &gt;
		The manual has very good examples.
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bye&quot;&gt;Bye&lt;&#x2F;h2&gt;
&lt;p&gt;I hope that was a useful bit of information about a thing that mostly stays out of the way and comes with an extra bit of suprise when it doesn&#x27;t work.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Pretty Atom-Feed Previews with XSLT</title>
        <published>2023-12-22T00:00:00+00:00</published>
        <updated>2023-12-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/atom-xslt/"/>
        <id>https://slatecave.net/blog/atom-xslt/</id>
        
        <summary type="text">Adding some XSLT to my Atom-Feeds to make them behave like Webpages in Browsers</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/atom-xslt/">&lt;h2 id=&quot;atom-and-xslt-what&quot;&gt;Atom and XSLT what?&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ll assume you already know what &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Atom_(web_standard)&quot;&gt;Atom-Feeds&lt;&#x2F;a&gt; and XML are and that you have some HTML skills.&lt;&#x2F;p&gt;
&lt;p&gt;As an Atom-Feed is simply an XML-File on the web-server it makes sense &lt;a href=&quot;&#x2F;about&#x2F;feeds&quot;&gt;linking to it&lt;&#x2F;a&gt;, unfortunately the Atom-XML in the Browser isn&#x27;t pretty to look at.&lt;&#x2F;p&gt;
&lt;p&gt;This is where &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;XSLT&quot;&gt;XSLT (Extensible Stylesheet Language Transformation)&lt;&#x2F;a&gt; steps in. XSLT is a standardised way to turn an XML document into another and, as it turns out it is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.w3schools.com&#x2F;xml&#x2F;xsl_transformation.asp&quot;&gt;built into every major Web-Browser&lt;&#x2F;a&gt;, meaning it works perfectly well with my static site setup.&lt;&#x2F;p&gt;
&lt;p&gt;This way one can deliver a standards compliant Atom-Feed that gets rendered into a preview for humans when viewed with a Browser.&lt;&#x2F;p&gt;
&lt;p&gt;Awesome, lets do that!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; If you&#x27;ve ever seen the message &lt;q&gt;This XML file does not appear to have any style information associated with it. The document tree is shown below.&lt;&#x2F;q&gt; the Browser means XSLT, not CSS.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;building-an-atom-feed-with-a-stylesheet&quot;&gt;Building an Atom-Feed with a Stylesheet&lt;&#x2F;h2&gt;
&lt;p&gt;To get the Browser to load a stylesheet one has to add tell it where it is by adding a line like following:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&amp;lt;!--&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; &amp;lt;?xml … header here &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;--&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;xml-stylesheet&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;text&#x2F;xsl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;assets&#x2F;site_slatecave&#x2F;atom_to_html.xslt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&amp;lt;!--&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Rest of feed here &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;--&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For the slatecave I&#x27;ve added the line to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;site-source.slatecave-net&#x2F;src&#x2F;commit&#x2F;a7ffd694630a425bec04c35b09adcd7461dc9929&#x2F;templates&#x2F;atom.xml#L2&quot;&gt;my atom feed template&lt;&#x2F;a&gt; and put a very minimalistic stylesheet into the static files.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; I didn&#x27;t have this template before this project so I customised &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;getzola&#x2F;zola&#x2F;blob&#x2F;38199c125501e9ff0e700e96adaca72cc3f25d2b&#x2F;components&#x2F;templates&#x2F;src&#x2F;builtins&#x2F;atom.xml&quot;&gt;Zola&#x27;s builtin template&lt;&#x2F;a&gt; a little.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; If you want to use XSLT 2.0 functions (mainly &lt;code&gt;replace()&lt;&#x2F;code&gt; and &lt;code&gt;tokenize()&lt;&#x2F;code&gt;) you have to set &lt;code&gt;version&lt;&#x2F;code&gt; to &lt;code&gt;2.0&lt;&#x2F;code&gt; on the &lt;code&gt;xsl:stylesheet&lt;&#x2F;code&gt; tag.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;A very minimal XSLT-Sheet that results in a hello-world message.&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;xml&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; version&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; encoding&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;UTF-8&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;stylesheet&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; version&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;xmlns&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;http:&#x2F;&#x2F;www.w3.org&#x2F;1999&#x2F;XSL&#x2F;Transform&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;output&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; method&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;html&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; encoding&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;utf-8&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; indent&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;yes&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt; &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;template&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; match&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;html&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; lang&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;en&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;	&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;body&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;		&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Hello from the XSLT-Sheet!&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;	&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;body&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;html&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;template&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;stylesheet&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;getting-content-from-the-atom-feed&quot;&gt;Getting Content from the Atom-Feed&lt;&#x2F;h2&gt;
&lt;p&gt;To get content from the original XML one can use an &lt;code&gt;&amp;lt;xsl:value-of select=&quot;&#x2F;xpath&#x2F;goes&#x2F;here&quot; &#x2F;&amp;gt;&lt;&#x2F;code&gt; tag.&lt;&#x2F;p&gt;
&lt;p&gt;This tag replaces itself with the content of the first tag returned by the XPath in the &lt;code&gt;select&lt;&#x2F;code&gt; attribute.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;What is XPath?&lt;&#x2F;b&gt; XPath is a way to select tags from an XML Document, it works a bit like a Filepath and a bit like a CSS Selector.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-text&quot; href=&quot;&amp;#x2F;notebook&amp;#x2F;xpath&quot; &gt;
		See my XPath Cheatsheet.
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;To spare you the headache: Don&#x27;t forget to declare and use the &lt;code&gt;atom&lt;&#x2F;code&gt; namespace on the XSLT-Sheet.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;The new opening tag of the XSLT-Sheet.&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;stylesheet&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; version&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;xmlns&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;http:&#x2F;&#x2F;www.w3.org&#x2F;1999&#x2F;XSL&#x2F;Transform&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;xmlns&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;atom&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;http:&#x2F;&#x2F;www.w3.org&#x2F;2005&#x2F;Atom&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;Nodes in the Atom feed can then be accessed like the following:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;value-of&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; select&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;atom:feed&#x2F;atom:title&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt; &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With that out of the way you follow the the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.w3schools.com&#x2F;xml&#x2F;xsl_intro.asp&quot;&gt;w3schools XSLT Tutorial&lt;&#x2F;a&gt; to create such a sheet yourself.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-code&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;codeberg.org&amp;#x2F;slatian&amp;#x2F;site-source.slatecave-net&amp;#x2F;src&amp;#x2F;commit&amp;#x2F;a7ffd694630a425bec04c35b09adcd7461dc9929&amp;#x2F;static&amp;#x2F;assets&amp;#x2F;site_slatecave&amp;#x2F;atom_to_html.xslt&quot; &gt;
		The slatecave.net Atom to HTML XSLT-Sheet
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;content-security-policy&quot;&gt;Content Security Policy&lt;&#x2F;h2&gt;
&lt;p&gt;After Uploading I noticed that it isn&#x27;t working. A quick look at the debugging tools reveals that an XSLT sheet is treated like a script which was disabled, because the slatecave doesn&#x27;t use any JavaScript.&lt;&#x2F;p&gt;
&lt;p&gt;Setting &lt;code&gt;script-src &#x27;self&#x27;;&lt;&#x2F;code&gt; allows loading and executing the XSLT-Sheet resulting in some &lt;a href=&quot;&#x2F;atom.xml&quot;&gt;pretty Atom-Feeds&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;that-s-it&quot;&gt;That&#x27;s it!&lt;&#x2F;h2&gt;
&lt;p&gt;I hope that information was useful or at least interesting to you!&lt;&#x2F;p&gt;
&lt;p&gt;This is more a braindump style of post, let me know how that went.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>xdg-mime: Mapping Files to Applications taken apart</title>
        <published>2023-10-03T00:00:00+00:00</published>
        <updated>2023-10-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/xdg-mime/"/>
        <id>https://slatecave.net/blog/xdg-mime/</id>
        
        <summary type="text">How the machinery mapping files to applications works on your free desktop</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/xdg-mime/">&lt;p&gt;last time we took apart &lt;code&gt;xdg-open&lt;&#x2F;code&gt;, the freedesktop file-opener and found out that &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; provides the mapping from file to application. So let&#x27;s take it apart and see what&#x27;s inside …&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-can-xdg-mime-do&quot;&gt;What can xdg-mime do?&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;xdg-mime&lt;&#x2F;code&gt; is a shell-script that helps with mapping files to the applications that open them. It implements querying a files mime-type, setting and getting the default application for a given mime-type as well as installing and uninstalling mime-type descriptions.&lt;&#x2F;p&gt;
&lt;p&gt;The code is developed in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&quot;&gt;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;You can use &lt;code&gt;xdg-mime --help&lt;&#x2F;code&gt; or &lt;code&gt;man xdg-mime&lt;&#x2F;code&gt; yourself to get more details.&lt;&#x2F;p&gt;




	


&lt;dl class=&quot;&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;xdg-mime query filetype &amp;lt;file&amp;gt;&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This sub-command prints the detected mime-type of a given file. (i.e. &lt;code&gt;text&#x2F;plain&lt;&#x2F;code&gt;)
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;xdg-mime query default &amp;lt;mimetype&amp;gt;&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This prints out the name of the desktop file that is configured as the default opener for the given mime-type. (i.e. &lt;code&gt;slatectx-app-kakoune.desktop&lt;&#x2F;code&gt;)
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;xdg-mime default &amp;lt;applications&amp;gt; &amp;lt;mimetype&amp;gt; ...&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Attempts to set the default opener for the one or more given mime-types to the given application which is identified by the name of its desktop file.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;xdg-mime install mimetypes-file &amp;lt;file&amp;gt;&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Installs a mime-type description file that follows the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;specifications.freedesktop.org&#x2F;shared-mime-info-spec&#x2F;shared-mime-info-spec-latest.html&quot;&gt;freedesktop mime associations specification&lt;&#x2F;a&gt; where it can be used to identify files. Can be put in &lt;code&gt;user&lt;&#x2F;code&gt; or &lt;code&gt;system&lt;&#x2F;code&gt; mode using the &lt;code&gt;--mode&lt;&#x2F;code&gt; flag.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;xdg-mime uninstall mimetypes-file &amp;lt;file&amp;gt;&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		As it says on the tin: It uninstalls a previously installed mime-type file.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; This article ended up being quite long, since it should still be understandable when read in a non-linear manner feel free to skip sections that don&#x27;t seem interesting.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-in-the-script&quot;&gt;What is in the Script?&lt;&#x2F;h2&gt;
&lt;p&gt;Like the other xdg-utils &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; - in addition to a lot of custom logic - imports functionality from the &lt;code&gt;xdg-utils-common.in&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Noteworthy imports from the common file are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a way to detect which desktop one is currently using&lt;&#x2F;li&gt;
&lt;li&gt;mapping the desktop file to the binary it calls and vice versa&lt;&#x2F;li&gt;
&lt;li&gt;helpers for command-line and UI&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Unique to &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; is:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a callback to update desktop specific mime database caches.&lt;&#x2F;li&gt;
&lt;li&gt;querying and setting of default applications from gnome, KDE and generic desktops&lt;&#x2F;li&gt;
&lt;li&gt;a function for getting desktop files matching a given mime-type.&lt;&#x2F;li&gt;
&lt;li&gt;functionality for figuring out an application when no explicit default has been set&lt;&#x2F;li&gt;
&lt;li&gt;functionality for opening the default application for KDE, gnome and generic desktops&lt;&#x2F;li&gt;
&lt;li&gt;command line parsing and validating&lt;&#x2F;li&gt;
&lt;li&gt;XML parsing in awk … okay&lt;&#x2F;li&gt;
&lt;li&gt;lots of parsing and updating files with awk&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So this script seems to mostly do the work of parsing and writing the mime to application mapping database, which is to be expected, at first glance mime-type detection seems to be delegated to another command and is in here to provide a stable interface that applications can rely on while &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; does the figuring out how to derive the mime-type.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;xdg-mime&lt;&#x2F;code&gt; integrates with the KDE and Gnome mime databases, because those have been there before &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;freedesktop.org&quot;&gt;freedesktop.org&lt;&#x2F;a&gt; and the xdg-utils were a thing. More recently Lxqt made their own tool called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lxqt&#x2F;qtxdg-tools&quot;&gt;&lt;code&gt;qtxdg-mat&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; that has been &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;merge_requests&#x2F;48&quot;&gt;integrated into xdg-utils&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;actions-and-cli-plumbing&quot;&gt;Actions and CLI Plumbing&lt;&#x2F;h3&gt;
&lt;p&gt;Since calling &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; is based on sub-commands, some with their own custom options, this results the parser being a lot of nested &lt;code&gt;case&lt;&#x2F;code&gt; statements and loops for parsing the extra options and arguments.&lt;&#x2F;p&gt;
&lt;p&gt;To keep the logic from becoming an indentation mess &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; has a concept of actions. That is, that the command gets translated into an action-name that is written to the &lt;code&gt;action&lt;&#x2F;code&gt; variable. Which is then reused for deciding what to do.&lt;&#x2F;p&gt;
&lt;p&gt;Potential values for the &lt;code&gt;action&lt;&#x2F;code&gt; variable are:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;info&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		for the filename to mime-type query. with the &lt;code&gt;xdg-mime query filetype&lt;&#x2F;code&gt; sub-command.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;makedefault&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		for setting default mime-handlers with the &lt;code&gt;xdg-mime default&lt;&#x2F;code&gt; sub-command.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;defapp&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		for querying the default application with the &lt;code&gt;xdg-mime query default&lt;&#x2F;code&gt; sub-command.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;install&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		for installing mime descriptions with the &lt;code&gt;xdg-mime install&lt;&#x2F;code&gt; sub-command.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;uninstall&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		for uninstalling mime descriptions with the &lt;code&gt;xdg-mime uninstall&lt;&#x2F;code&gt; sub-command.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;getting-the-mimetype-of-a-file&quot;&gt;Getting the MimeType of a File&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;code&gt;info&lt;&#x2F;code&gt; action triggers a desktop detection and then decides which mime-type detection method to use. On success the output must be the mime-type of the file followed by a newline without any extra information.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;If the desktop is KDE and &lt;code&gt;&#x2F;usr&#x2F;bin&#x2F;file&lt;&#x2F;code&gt; is not an executable use &lt;code&gt;info_kde&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;If the desktop belongs to the gnome gio family (Gnome,Cinnamon,Lxde,Mate,Xfce) use &lt;code&gt;info_gnome&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;In case of Lxqt use &lt;code&gt;info_lxqt&lt;&#x2F;code&gt; (new in the beta 1.2.0 release)&lt;&#x2F;li&gt;
&lt;li&gt;Otherwise use &lt;code&gt;info_generic&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;lxqt-mimetype-query&quot;&gt;Lxqt MimeType Query&lt;&#x2F;h3&gt;
&lt;p&gt;For Lxqt &lt;code&gt;qtxdg-mat mimetype &amp;lt;file&amp;gt;&lt;&#x2F;code&gt; is used to query the mime information of a given file.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;kde-mimetype-query&quot;&gt;KDE MimeType Query&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;info_kde&lt;&#x2F;code&gt; checks the &lt;code&gt;KDE_SESSION_VERSION&lt;&#x2F;code&gt; environment variable.&lt;&#x2F;p&gt;
&lt;p&gt;For KDE 4 and 6 it uses the &lt;code&gt;kmimetypefinder&lt;&#x2F;code&gt; command, for KDE 5 &lt;code&gt;kmimetypefinder5&lt;&#x2F;code&gt; and for older versions it gets the mime-type from &lt;code&gt;kfile&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Extra information is cut away using POSIX tools.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;gnome-mimetype-query&quot;&gt;Gnome MimeType Query&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;info_gnome&lt;&#x2F;code&gt; uses a mechanism similar to file opening where it uses a fallback cascade of multiple methods for detecting the mime type, using the first one that is available.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;gio info &amp;lt;file&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;gvfs-info &amp;lt;file&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;gnomevfs-info --slow-mime &amp;lt;file&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Like with the KDE method any extra information is discarded using standard POSIX tools.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;generic-mimetype-query&quot;&gt;Generic MimeType query&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;info_generic&lt;&#x2F;code&gt; also uses a little fallback cascade.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;mimetype --brief --dereference &amp;lt;file&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&#x2F;usr&#x2F;bin&#x2F;file --brief --dereference --mime-type &amp;lt;file&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;For both commands cutting away extra information is not needed.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note on &lt;code&gt;&#x2F;usr&#x2F;bin&#x2F;file&lt;&#x2F;code&gt;:&lt;&#x2F;b&gt; It seems strange that &lt;code&gt;&#x2F;usr&#x2F;bin&#x2F;file&lt;&#x2F;code&gt; is used here instead of just calling the &lt;code&gt;file&lt;&#x2F;code&gt; command. After finding no information on it and asking the original developers (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;commit&#x2F;76e2c9cf157606f73b297f9a3dbd53125fc82510#286a7a0cd84d236c319bbc342526456a380e56a0_0_51&quot;&gt;this quirk has been around since 2006&lt;&#x2F;a&gt;) it turns out that nobody knows why the absolute path was used here, it would most probably not make a difference to change it back.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-xdg-mime-figures-out-the-default-application&quot;&gt;How xdg-mime figures out the Default Application&lt;&#x2F;h2&gt;
&lt;p&gt;This functionality hides behind the &lt;code&gt;defapp&lt;&#x2F;code&gt; action and can be triggered using the &lt;code&gt;xdg-mime query default &amp;lt;mimetype&amp;gt;&lt;&#x2F;code&gt; command. As a reminder this returns the name of the desktop-file describing the application configured as the default opener or an application that seems the best fit according to some simple heuristics in case no default is was explicitly set.&lt;&#x2F;p&gt;
&lt;p&gt;This time there are only three implementations, a generic one, a new one for Lxqt and one for KDE below version 6.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;issues&#x2F;147&quot;&gt;application returned by &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; is not necessarily the one that will open when calling &lt;code&gt;xdg-open&lt;&#x2F;code&gt; on the same file&lt;&#x2F;a&gt;, mostly because &lt;code&gt;xdg-open&lt;&#x2F;code&gt; delegates to openers which can have custom logic.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;kde-default-application-querying&quot;&gt;KDE Default Application Querying&lt;&#x2F;h3&gt;
&lt;p&gt;KDE has a subsystem called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;techbase.kde.org&#x2F;Development&#x2F;Tutorials&#x2F;Services&#x2F;Traders&quot;&gt;Trader&lt;&#x2F;a&gt; which handles delegation of actions between various KDE components (&lt;i&gt;services&lt;&#x2F;i&gt;). To use it &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; invokes &lt;code&gt;ktraderclient&lt;&#x2F;code&gt;, &lt;code&gt;ktraderclient5&lt;&#x2F;code&gt; or &lt;code&gt;ktradertest&lt;&#x2F;code&gt; (for older than KDE 4) as &lt;code&gt;$KTRADER&lt;&#x2F;code&gt;. Asking it for a matching &lt;code&gt;Application&lt;&#x2F;code&gt; &lt;i&gt;service&lt;&#x2F;i&gt; for the given mime-type, extracting the result from the returned text.&lt;&#x2F;p&gt;
&lt;p&gt;If no ktrader command appropriate to the current KDE version can be found it falls back to the generic implementation.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;lxqt-default-application-querying&quot;&gt;Lxqt Default Application Querying&lt;&#x2F;h3&gt;
&lt;p&gt;Again &lt;code&gt;qtxdg-mat&lt;&#x2F;code&gt; is used:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;qtxdg-mat&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; defapp&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;fil&lt;&#x2F;span&gt;&lt;span&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;generic-default-application-querying&quot;&gt;Generic Default Application Querying&lt;&#x2F;h3&gt;
&lt;p&gt;For where there is no desktop specific mechanism, &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; has a generic, specification compliant 🥳 way to get the default application using the &lt;code&gt;defapp_generic&lt;&#x2F;code&gt; function.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;desktop-prefixes&quot;&gt;Desktop Prefixes&lt;&#x2F;h4&gt;
&lt;p&gt;Before getting into querying default applications, first imagine someone switches between multiple desktops, maybe &lt;code&gt;mate&lt;&#x2F;code&gt; and &lt;code&gt;kde&lt;&#x2F;code&gt;, while you ave some applications you want to be the default everywhere (i.e. you preferred web-browser) other preferences might depend on the desktop you are currently using like the image- or PDF-viewer.&lt;&#x2F;p&gt;
&lt;p&gt;Instead of the desktops stepping on each others toes, every desktop that identifies itself via the &lt;code&gt;XDG_CURRENT_DESKTOP&lt;&#x2F;code&gt; environment variable gets its own configuration file, prefixed by the lowercased name that is read from that variable. This way different preferences on different desktops can be kept separate.&lt;&#x2F;p&gt;
&lt;p&gt;This prefix will be referred to as &lt;code&gt;${prefix}&lt;&#x2F;code&gt; further down. Examples of its contents may be: &lt;code&gt;mate-&lt;&#x2F;code&gt;, &lt;code&gt;kde-&lt;&#x2F;code&gt;, etc. or it may be empty.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; The &lt;code&gt;XDG_CURRENT_DESKTOP&lt;&#x2F;code&gt; works completely independent of the desktop detection (in fact: the desktop detection uses it). So anyone can set this to get their own configuration space for their fresh new custom desktop.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; &lt;code&gt;XDG_CURRENT_DESKTOP&lt;&#x2F;code&gt;, like many other XDG variables is a colon separated list that is handled with a fallback cascade. i.e. if your custom &lt;code&gt;xfce&lt;&#x2F;code&gt; based rice identifies itself as &lt;code&gt;my-xfce-rice:xfce&lt;&#x2F;code&gt;, then the application choice will be pulled from &lt;code&gt;my-xfce-rice&lt;&#x2F;code&gt; and automatically fall back to the &lt;code&gt;xfce&lt;&#x2F;code&gt; defaults, even when other desktops are installed.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;reading-the-association-files&quot;&gt;Reading the Association Files&lt;&#x2F;h4&gt;
&lt;p&gt;This is a multistage search that …&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;checks the users &lt;code&gt;$XDG_CONFIG_HOME&#x2F;${prefix}mimeapps.list&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;checks the system wide &lt;code&gt;$XDG_CONFIG_DIRS&#x2F;${prefix}mimeapps.list&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;checks again for an &lt;code&gt;applications&#x2F;${prefix}mimeapps.list&lt;&#x2F;code&gt; in both system and user data directories (usually &lt;code&gt;~&#x2F;.local&#x2F;share&#x2F;&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;searches both user and system data directories for a prefixed with the current desktops name &lt;code&gt;applications&#x2F;${prefix}defaults.list&lt;&#x2F;code&gt; and &lt;code&gt;applications&#x2F;${prefix}mimeinfo.cache&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;falls back to searching the application files for the one that sees itself most fit for opening a file with the given mime-type using the &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;xdg-mime&#x2F;#fallback-application-finding&quot;&gt;&lt;code&gt;defapp_fallback&lt;&#x2F;code&gt; function&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The files are searched in the order of highest priority to lowest and when the first result is found it is returned.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;While the filenames are different those files share the same ini syntax.&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;mimtype&amp;gt;=&amp;lt;desktop-file-name&amp;gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;&amp;lt;another-desktop-file-name&amp;gt;...][;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;For the &lt;code&gt;mimeapps.list&lt;&#x2F;code&gt; files, the &lt;code&gt;check_mimeapps_list&lt;&#x2F;code&gt; function searches the &lt;code&gt;[Default Applications]&lt;&#x2F;code&gt; section for an entry with the given mime-type. If such an entry exists, all mentioned desktop-file names are extracted and the first one with a valid executable is returned. If not the search continues.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Bug Note:&lt;&#x2F;b&gt; Apparently there is a bug in &lt;code&gt;check_mimeapps_list&lt;&#x2F;code&gt; that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;merge_requests&#x2F;53&quot;&gt;discards any names after the first semicolon&lt;&#x2F;a&gt;, making the loop that should handle fallback useless.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;defaults.list&lt;&#x2F;code&gt; and &lt;code&gt;mimeinfo.cache&lt;&#x2F;code&gt; get treated simpler than the &lt;code&gt;mimeapps.list&lt;&#x2F;code&gt; files. The first result that mates the mime-type is returned, done. Sections are ignored for these files.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;fallback-application-finding&quot;&gt;Fallback Application Finding&lt;&#x2F;h4&gt;
&lt;p&gt;In case no desktop application is found using the configuration file &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; will start searching one itself with the &lt;code&gt;defapp_fallback&lt;&#x2F;code&gt; function.&lt;&#x2F;p&gt;
&lt;p&gt;This iterates over all desktop files that match the given mime-type, picking the one with the highest value in &lt;code&gt;InitialPreference&lt;&#x2F;code&gt; (default is 0 when unset).
In case multiple applications have the same &lt;code&gt;InitialPreference&lt;&#x2F;code&gt;, the one first found or in the higher priority location wins.&lt;&#x2F;p&gt;
&lt;p&gt;If a file is found this way it gets returned.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; For the &lt;code&gt;InitialPreference&lt;&#x2F;code&gt;, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;specifications.freedesktop.org&#x2F;desktop-entry-spec&#x2F;desktop-entry-spec-1.1.html#kde-items&quot;&gt;desktop file specification only says that it is reserved for KDE&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-default-applications&quot;&gt;Setting Default Applications&lt;&#x2F;h2&gt;
&lt;p&gt;Setting a default application for a given mime-type happens in one of two ways:&lt;&#x2F;p&gt;
&lt;p&gt;For Lxqt only the &lt;code&gt;make_default_lxqt&lt;&#x2F;code&gt; function gets called.&lt;&#x2F;p&gt;
&lt;p&gt;For everything else there are two functions, the &lt;code&gt;make_default_kde&lt;&#x2F;code&gt; one where the KDE database gets updated and &lt;code&gt;make_default_generic&lt;&#x2F;code&gt; which updates the users &lt;code&gt;mimeapps.list&lt;&#x2F;code&gt; file.
Weirdly both are always called. (&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;xdg-mime&#x2F;#desktop-prefixes&quot;&gt;Remember the whole prefix thing?&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;generic-default-application-setting&quot;&gt;Generic Default Application Setting&lt;&#x2F;h3&gt;
&lt;p&gt;Surprisingly unsurprising the generic case is the simpler one here.&lt;&#x2F;p&gt;
&lt;p&gt;It derives the path for &lt;code&gt;$XDG_CONFIG_HOME&#x2F;mimeapps.list&lt;&#x2F;code&gt; and then runs an awk script on it. This script adds or updates the &lt;code&gt;&amp;lt;mimetype&amp;gt;=&amp;lt;desktop-file-name&amp;gt;;&lt;&#x2F;code&gt; association entry in the &lt;code&gt;[Default Applications]&lt;&#x2F;code&gt; section that belongs to the given mime-type.&lt;&#x2F;p&gt;
&lt;p&gt;What is a bit surprising is that the desktop prefix isn&#x27;t handled here.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;for-lxqt&quot;&gt;For Lxqt&lt;&#x2F;h3&gt;
&lt;p&gt;Again &lt;code&gt;qtxdg-mat&lt;&#x2F;code&gt; has a sub-command that is a prefect fit and doesn&#x27;t need any wrapping:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;qtxdg-mat&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; defapp&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-set&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;fil&lt;&#x2F;span&gt;&lt;span&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;mimetyp&lt;&#x2F;span&gt;&lt;span&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;for-kde-3-and-4&quot;&gt;For KDE 3 and 4&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;make_default_kde&lt;&#x2F;code&gt; function is a lot of awk code that seems to do patching on two files.
Unfortunately it &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;issues&#x2F;12&quot;&gt;hasn&#x27;t been updated after KDE 4&lt;&#x2F;a&gt; …&lt;&#x2F;p&gt;
&lt;h4 id=&quot;kde-4&quot;&gt;KDE 4&lt;&#x2F;h4&gt;
&lt;p&gt;The KDE 4 specific one modifies a &lt;code&gt;mimeapps.list&lt;&#x2F;code&gt; file in a directory returned by &lt;code&gt;kde4-config --path xdgdata-apps 2&amp;gt; &#x2F;dev&#x2F;null | cut -d &#x27;:&#x27; -f 1&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It adds the mime-type to application mapping to the &lt;code&gt;[Added Associations]&lt;&#x2F;code&gt; section if it is missing.&lt;&#x2F;p&gt;
&lt;p&gt;My personal guess without doing further research is that this ensures that the application to mime-type mapping exists because something in KDE actually validates that an application can open a given mime-type. I&#x27;m also pretty sure that this mechanism relies on the generic setter running immediately after for setting the entry in the &lt;code&gt;[Default Applications]&lt;&#x2F;code&gt; section. Maybe someone can confirm or deny this.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;kde-3&quot;&gt;KDE 3&lt;&#x2F;h4&gt;
&lt;p&gt;For KDE 3 (the default case if the version isn&#x27;t 4), the &lt;code&gt;profilerc&lt;&#x2F;code&gt; file in a directory returned by &lt;code&gt;kde-config --path config 2&amp;gt; &#x2F;dev&#x2F;null | cut -d &#x27;:&#x27; -f 1&lt;&#x2F;code&gt; gets modified.&lt;&#x2F;p&gt;
&lt;p&gt;There with ini-like syntax, a section gets added:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;&amp;lt;mimetype&amp;gt; - 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;Application&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;desktop-file-name&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;AllowAsDefault&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;GenericServiceType&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;Application&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;Preference&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;ServiceType&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;mimetype&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Sections that act as duplicates will get removed.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;installing-and-uninstalling-mimetype-descriptions&quot;&gt;Installing and Uninstalling MimeType Descriptions&lt;&#x2F;h2&gt;
&lt;p&gt;Now that everything from getting the mime-type to the application is covered one may ask:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;How does the mime detection tooling know about all the file-types out there?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;On a freedesktop system this information resides in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;wiki&#x2F;Specifications&#x2F;shared-mime-info-spec&#x2F;&quot;&gt;XDG Shared MIME-info Database&lt;&#x2F;a&gt; which is a binary compiled from XML files that describe how to detect a given mime-type, and what its icon, names and descriptions (in different languages) are.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;xdg-mime&lt;&#x2F;code&gt; makes it easier to install those XML descriptions. (When packaging follow the target distributions guidelines first!)&lt;&#x2F;p&gt;
&lt;p&gt;For the general case installing puts&#x2F;deletes the given file in the desired &lt;code&gt;mime&#x2F;packages&lt;&#x2F;code&gt; directory.&lt;&#x2F;p&gt;
&lt;p&gt;For user mode that is &lt;code&gt;$XDG_DATA_HOME&#x2F;mime&#x2F;packages&#x2F;&lt;&#x2F;code&gt;, defaulting to &lt;code&gt;~&#x2F;.local&#x2F;share&#x2F;mime&#x2F;packages&#x2F;&lt;&#x2F;code&gt;. For the system mode &lt;code&gt;$XDG_DATA_DIRS&#x2F;mime&#x2F;packages&#x2F;&lt;&#x2F;code&gt;, defaulting to &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;mime&#x2F;packages&#x2F;&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;usr&#x2F;local&#x2F;share&#x2F;mime&#x2F;packages&#x2F;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There is also a fallback for a KDE3 mechanism called &quot;mimelink&quot; but I will skip that one for now.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Don&#x27;t confuse the files in the &lt;code&gt;packages&lt;&#x2F;code&gt; directory with other XML files named like the mime-types themselves. The ones in &lt;code&gt;packages&lt;&#x2F;code&gt; really are packages containing the descriptions for multiple mime-types while the other ones are already unpacked for better discovery.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hooks-on-the-mime-database&quot;&gt;Hooks on the Mime Database&lt;&#x2F;h3&gt;
&lt;p&gt;After (un)installing the &lt;code&gt;update_mime_database&lt;&#x2F;code&gt; function takes care of making sure that the mime database and caches get rebuilt.&lt;&#x2F;p&gt;
&lt;p&gt;When in user mode, the currently running desktop is KDE and a display is present &lt;code&gt;kbuildsycoca&lt;&#x2F;code&gt;, &lt;code&gt;kbuildsycoca4&lt;&#x2F;code&gt; or &lt;code&gt;kbuildsycoca5&lt;&#x2F;code&gt; is called, which rebuilds KDE configuration caches.&lt;&#x2F;p&gt;
&lt;p&gt;After that every directory in &lt;code&gt;$PATH&lt;&#x2F;code&gt; with the addition of &lt;code&gt;&#x2F;opt&#x2F;gnome&#x2F;bin&lt;&#x2F;code&gt; is searched for a &lt;code&gt;update-mime-database&lt;&#x2F;code&gt; program. &lt;em&gt;All&lt;&#x2F;em&gt; found binaries are ran with the mime database folder that was just updated as the first argument.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; &lt;code&gt;update-mime-database&lt;&#x2F;code&gt; is the official program for rebuilding the mime database.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;relevant-specifications&quot;&gt;Relevant Specifications&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;wiki&#x2F;Specifications&#x2F;shared-mime-info-spec&#x2F;&quot;&gt;XDG Shared MIME-info Database&lt;&#x2F;a&gt; which allows storing information about mime-types themselves.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;wiki&#x2F;Specifications&#x2F;mime-apps-spec&#x2F;&quot;&gt;XDG MIME Applications Associations&lt;&#x2F;a&gt; which are responsible for mapping applications to mime-types.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;until-next-time&quot;&gt;Until Next Time …&lt;&#x2F;h2&gt;
&lt;p&gt;On expectations: When starting to write this article I didn&#x27;t expect it getting this long and technical, but researching as well as finding and fixing bugs ended up producing a lot of information which I didn&#x27;t want to hide.&lt;&#x2F;p&gt;
&lt;p&gt;A huge thank you goes to Kevin Krammer and Jeremy White for starting the &lt;code&gt;xdg-utils&lt;&#x2F;code&gt; project, to Simon Lees who is currently the de-facto maintainer and everyone who contributed in any way. And while the &lt;code&gt;xdg-utils&lt;&#x2F;code&gt; might be a mess, they are a bloody awesome and quite functional mess!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Recently &lt;code&gt;xdg-utils&lt;&#x2F;code&gt; has received a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;releases&#x2F;v1.2.0-beta1&quot;&gt;beta release&lt;&#x2F;a&gt; after a very long time.&lt;&#x2F;p&gt;
&lt;p&gt;You can help the project by testing it, reporting and researching issues, giving context and writing fixes or documentation.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Next up on this mini-series will probably be a dive into &lt;code&gt;xdg-settings&lt;&#x2F;code&gt;, but please don&#x27;t quote me on that.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>&quot;It just opens files&quot; - xdg-open under the hood</title>
        <published>2023-09-02T00:00:00+00:00</published>
        <updated>2023-09-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/xdg-open-under-the-hood/"/>
        <id>https://slatecave.net/blog/xdg-open-under-the-hood/</id>
        
        <summary type="text">How the logic behind the freedesktop file opener works</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/xdg-open-under-the-hood/">&lt;h2 id=&quot;what-is-xdg-open&quot;&gt;What is xdg-open ?&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;portland.freedesktop.org&#x2F;doc&#x2F;xdg-open.html&quot;&gt;&lt;code&gt;xdg-open&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, part of the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;freedesktop.org&#x2F;wiki&#x2F;Software&#x2F;xdg-utils&#x2F;&quot;&gt;xdg-utils&lt;&#x2F;a&gt; package is a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;freedesktop.org&quot;&gt;freedesktop.org&lt;&#x2F;a&gt; tool that aims to give you (and everyone else) a desktop independent interface for opening a file or URL, taking care of all the environments quirks and settings so the application or script that &quot;just wants to open a file&quot; doesn&#x27;t have to.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Examples for opening a file, a URI and a &lt;code&gt;file:&#x2F;&#x2F;&lt;&#x2F;code&gt; URI with &lt;code&gt;xdg-open&lt;&#x2F;code&gt;&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;xdg-open&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; example.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;xdg-open&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; https:&#x2F;&#x2F;slatecave.net&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;xdg-open&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; file:&#x2F;&#x2F;&#x2F;usr&#x2F;bin&#x2F;xdg-open&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;This article is based on version &lt;code&gt;1.1.3&lt;&#x2F;code&gt;, the version that is currently in development is called &lt;code&gt;1.1.3+&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;questions&quot;&gt;Questions&lt;&#x2F;h2&gt;
&lt;p&gt;Before starting out I was trying to find answers to the following questions:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;How does &lt;code&gt;xdg-open&lt;&#x2F;code&gt; know my favourite applications?&lt;&#x2F;li&gt;
&lt;li&gt;Which ways are there to configure it?&lt;&#x2F;li&gt;
&lt;li&gt;Is there an interface to extending it?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This is why this article is about high level logic rather than little tricks.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;first-look-at-the-file&quot;&gt;First look at the file&lt;&#x2F;h2&gt;
&lt;p&gt;To analyse any tool one should try to get it&#x27;s source code.&lt;&#x2F;p&gt;
&lt;p&gt;A quick &lt;code&gt;command -v xdg-open&lt;&#x2F;code&gt; reveals the binary to be under &lt;code&gt;&#x2F;usr&#x2F;bin&#x2F;xdg-open&lt;&#x2F;code&gt; for me, &lt;code&gt;file &#x2F;usr&#x2F;bin&#x2F;xdg-open&lt;&#x2F;code&gt; tells me that it looks like a &lt;q&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;POSIX&quot;&gt;POSIX&lt;&#x2F;a&gt; shell script&lt;&#x2F;q&gt; and a &lt;code&gt;shellcheck &#x2F;usr&#x2F;bin&#x2F;xdg-open&lt;&#x2F;code&gt; command reveals that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;file&quot;&gt;&lt;code&gt;file&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; was not quite right about the POSIX part.&lt;&#x2F;p&gt;
&lt;p&gt;But it is a shellscript that anyone can just open in their favourite text file viewer&#x2F;editor.&lt;&#x2F;p&gt;
&lt;p&gt;The file contains the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;UI code to make it easier to use&lt;&#x2F;li&gt;
&lt;li&gt;Helpers for querying &lt;code&gt;.desktop&lt;&#x2F;code&gt; files&lt;&#x2F;li&gt;
&lt;li&gt;Lots of checks for a lot of things&lt;&#x2F;li&gt;
&lt;li&gt;cli parser code&lt;&#x2F;li&gt;
&lt;li&gt;Heuristics for detecting desktop environments&lt;&#x2F;li&gt;
&lt;li&gt;Workarounds for tools that seem to misbehave&lt;&#x2F;li&gt;
&lt;li&gt;Wrappers for various desktop specific openers&lt;&#x2F;li&gt;
&lt;li&gt;Heuristics spaghetti with lots of &quot;xdg&quot; and &quot;generic&quot; in them (I assume those are the really interesting parts)&lt;&#x2F;li&gt;
&lt;li&gt;One giant switch that decides what to do&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In case you don&#x27;t have a local copy of a recent xdg-open: Its source lives in the freedesktop gitlab in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;blob&#x2F;v1.1.3&#x2F;scripts&#x2F;xdg-open.in&quot;&gt;xdg-open.in&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;blob&#x2F;v1.1.3&#x2F;scripts&#x2F;xdg-utils-common.in&quot;&gt;xdg-utils-common.in&lt;&#x2F;a&gt; which is reused across the entire &lt;code&gt;xdg-urils&lt;&#x2F;code&gt; package. This article will link to the &lt;code&gt;1.1.3&lt;&#x2F;code&gt; tag instead of the latest version.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-does-it-do&quot;&gt;What does it do?&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;xdg-open&lt;&#x2F;code&gt; really only does one thing, open whatever one throws at it and therefore has a pretty linear flow.&lt;&#x2F;p&gt;
&lt;p&gt;It only has options to query help and version information, I&#x27;ll ignore those code parts.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Those options are checked for near the start of the script so a &lt;code&gt;xdg-open --help&lt;&#x2F;code&gt; doesn&#x27;t even have to load all the logic.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; There is an undocumented &lt;code&gt;XDG_UTILS_DEBUG_LEVEL&lt;&#x2F;code&gt; environment variable that makes &lt;code&gt;xdg-open&lt;&#x2F;code&gt; more chatty when set to &lt;code&gt;1&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;get one URL or filename from the cli arguments&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;xdg-open-under-the-hood&#x2F;#detecting-the-desktop&quot;&gt;try to find out which desktop is currently in use using &lt;code&gt;detectDE&lt;&#x2F;code&gt;.&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;fall back to a &lt;code&gt;generic&lt;&#x2F;code&gt; desktop if detection wasn&#x27;t successful.&lt;&#x2F;li&gt;
&lt;li&gt;remove itself from the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; environment variable&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;xdg-open-under-the-hood&#x2F;#desktop-specific-openers&quot;&gt;based on the detected desktop call the appropriate helper for opening the file.&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;desktops&quot;&gt;Desktops&lt;&#x2F;h2&gt;
&lt;p&gt;The &quot;desktops&quot; &lt;code&gt;xdg-open&lt;&#x2F;code&gt; is aware of are (in order of them appearing):&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;kde&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Both modern day and very old &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kde.org&quot;&gt;KDE&lt;&#x2F;a&gt; desktops.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;deepin&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.deepin.org&#x2F;en&#x2F;dde&#x2F;&quot;&gt;Deepin Desktop Environment&lt;&#x2F;a&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;gnome3&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gnome.org&quot;&gt;GNOME desktop&lt;&#x2F;a&gt; in versions 3 (and 4)
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;cinnamon&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;projects.linuxmint.com&#x2F;cinnamon&#x2F;&quot;&gt;Cinnamon&lt;&#x2F;a&gt;, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;linuxmint.com&#x2F;&quot;&gt;Linux Mint&lt;&#x2F;a&gt; Desktop, it is treated as if it was &lt;code&gt;gnome3&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;gnome&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The GNOME desktop in version 2
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;mate&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mate-desktop.org&#x2F;&quot;&gt;MATE Desktop Environment&lt;&#x2F;a&gt; that continues as a modern version of GNOME 2, but gets treated differently.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;xfce&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;xfce.org&quot;&gt;Xfce Desktop Environment&lt;&#x2F;a&gt;.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;lxde&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lxde.org&quot;&gt;Lightweight X Desktop Environment&lt;&#x2F;a&gt;.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;lxqt&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lxqt-project.org&#x2F;&quot;&gt;Lightweight Qt Desktop Environment&lt;&#x2F;a&gt;, a qt remake of Lxde, but not related enough to share code here.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;enlightenment&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.enlightenment.org&#x2F;&quot;&gt;Enlightenment&lt;&#x2F;a&gt; Desktop.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;Some of them aren&#x27;t even desktops:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;cygwin&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		We are running under Windows in a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.cygwin.com&#x2F;&quot;&gt;Cygwin&lt;&#x2F;a&gt; environment.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;wsl&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		We are running in a WSL environment. Note that this is not yet present in version &lt;code&gt;1.1.3&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;darwin&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		We are running on MacOS.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;flatpak&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		We are in a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.flatpak.org&#x2F;&quot;&gt;flatpak&lt;&#x2F;a&gt; container.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;generic&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This is a desktop that doesn&#x27;t require any special treatment 🥳
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;Good news: many of those seem to map to the &lt;code&gt;open_generic&lt;&#x2F;code&gt; function, but for some desktops assumptions are made.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Update note regarding Deepin:&lt;&#x2F;b&gt; Previously this article listed &lt;code&gt;deepin&lt;&#x2F;code&gt; as &lt;code&gt;dde&lt;&#x2F;code&gt;, which is icorrect. The XDG-utils use the string &lt;code&gt;deepin&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;detecting-the-desktop&quot;&gt;Detecting the desktop&lt;&#x2F;h3&gt;
&lt;p&gt;Desktop detection is done by the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;blob&#x2F;v1.1.3&#x2F;scripts&#x2F;xdg-utils-common.in#L270&quot;&gt;&lt;code&gt;detectDE&lt;&#x2F;code&gt; function&lt;&#x2F;a&gt; using multiple heuristics in order of decreasing accuracy. The result is written to the &lt;code&gt;DE&lt;&#x2F;code&gt; global script variable.&lt;&#x2F;p&gt;
&lt;p&gt;The following tests are executed in order given that all previous tests didn&#x27;t match:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Test if the &lt;code&gt;XDG_CURRENT_DESKTOP&lt;&#x2F;code&gt; environment variable contains a known value.&lt;&#x2F;li&gt;
&lt;li&gt;Test for desktop specific clues
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;KDE_FULL_SESSION&lt;&#x2F;code&gt; is set&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;GNOME_DESKTOP_SESSION_ID&lt;&#x2F;code&gt; is set&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;MATE_DESKTOP_SESSION_ID&lt;&#x2F;code&gt; is set&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;org.gnome.SessionManager&lt;&#x2F;code&gt; name is owned by some application on the d-bus&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;_DT_SAVE_MODE&lt;&#x2F;code&gt; xprop on the root window contains the string &lt;code&gt;xfce4&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;xprop -root&lt;&#x2F;code&gt; has &lt;code&gt;xfce_desktop_window&lt;&#x2F;code&gt; at the start of a line in its output&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;DESKTOP&lt;&#x2F;code&gt; environment variable begins with &lt;code&gt;Enlightenment&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;LXQT_SESSION_CONFIG&lt;&#x2F;code&gt; is set&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Test if the &lt;code&gt;DESKTOP_SESSION&lt;&#x2F;code&gt; environment variable contains a known value.&lt;&#x2F;li&gt;
&lt;li&gt;Test if &lt;code&gt;uname&lt;&#x2F;code&gt; outputs either &lt;code&gt;CYGWIN&lt;&#x2F;code&gt;, &lt;code&gt;Darwin&lt;&#x2F;code&gt; or &lt;code&gt;Linux&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;In case of Linux, if &lt;code&gt;&#x2F;proc&#x2F;version&lt;&#x2F;code&gt; contains the string &lt;code&gt;microsoft&lt;&#x2F;code&gt; and the command &lt;code&gt;powershell.exe&lt;&#x2F;code&gt; is present, assume &lt;code&gt;wsl&lt;&#x2F;code&gt;. (This &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;commit&#x2F;d9e547b64b738ecb53dfb3497842500b8f6368b9&quot;&gt;was added very recently&lt;&#x2F;a&gt; and is not in version &lt;code&gt;1.1.3&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The following tests always run:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;When the desktop is a &lt;code&gt;gnome&lt;&#x2F;code&gt;, test if the &lt;code&gt;gnome-default-applications-properties&lt;&#x2F;code&gt; command is present, if not, assume &lt;code&gt;gnome3&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Test if &lt;code&gt;$XDG_RUNTIME_DIR&#x2F;flatpak-info&lt;&#x2F;code&gt; is present and if yes assume &lt;code&gt;flatpak&lt;&#x2F;code&gt; (Why isn&#x27;t this the first check?)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Because this is implemented in &lt;code&gt;xdg-utils-common.in&lt;&#x2F;code&gt; this also applies to all of the other xdg-utils tools.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;desktop-specific-openers&quot;&gt;Desktop specific openers&lt;&#x2F;h3&gt;
&lt;p&gt;Since &lt;code&gt;xdg-open&lt;&#x2F;code&gt; tries to offload the work when the desktop environment has its own mechanism for opening files lets cover those first.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;desktops-that-just-call-an-opener&quot;&gt;Desktops that &quot;just call an opener&quot;&lt;&#x2F;h4&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Desktop&lt;&#x2F;th&gt;&lt;th&gt;Opener&lt;&#x2F;th&gt;&lt;th&gt;Fall back to &lt;code&gt;open_generic&lt;&#x2F;code&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;deepin&lt;&#x2F;td&gt;&lt;td&gt;dde-open&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;enlightenment&lt;&#x2F;td&gt;&lt;td&gt;enlightenment&lt;wbr&gt;_open&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;lxqt&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;always&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;cygwin&lt;&#x2F;td&gt;&lt;td&gt;cygstart&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;darwin&lt;&#x2F;td&gt;&lt;td&gt;open&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;wsl&lt;&#x2F;td&gt;&lt;td&gt;powershell.exe start&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h4 id=&quot;flatpak&quot;&gt;flatpak&lt;&#x2F;h4&gt;
&lt;p&gt;When in a flatpak &lt;code&gt;xdg-open&lt;&#x2F;code&gt; invokes the &lt;code&gt;Desktop&lt;&#x2F;code&gt; portals &lt;code&gt;OpenURI&lt;&#x2F;code&gt; function using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;gdbus&quot;&gt;&lt;code&gt;gdbus&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;gdbus&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; call&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-session&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;    -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-dest&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; org.freedesktop.portal.Desktop&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;    -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-object-path&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;org&#x2F;freedesktop&#x2F;portal&#x2F;desktop&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;    -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-method&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; org.freedesktop.portal.OpenURI.OpenURI&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; for the &lt;code&gt;1.1.3+&lt;&#x2F;code&gt; version &lt;code&gt;xdg-open&lt;&#x2F;code&gt; uses the &lt;code&gt;org.freedesktop.portal.OpenURI.OpenFile&lt;&#x2F;code&gt; method for files and file URIs. It also added a timeout of 5 seconds.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;gnome-mate-and-xfce&quot;&gt;GNOME, MATE and Xfce&lt;&#x2F;h4&gt;
&lt;p&gt;GNOME 2, GNOME 3, MATE and Xfce are related in that they all share a similar cascade of openers (falling back to the next one, if the previous one isn&#x27;t present) is used.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;exo-open&lt;&#x2F;code&gt; when on xfce&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;gio open&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;gvfs-open&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;gnome-open&lt;&#x2F;code&gt; when on &lt;code&gt;gnome&lt;&#x2F;code&gt; (GNOME 2)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;mate-open&lt;&#x2F;code&gt; when on &lt;code&gt;mate&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;open_generic&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; for GNOME 3 the only options really are &lt;code&gt;gio open&lt;&#x2F;code&gt; and &lt;code&gt;gvfs-open&lt;&#x2F;code&gt; before falling back to the generic opener.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;kde&quot;&gt;KDE&lt;&#x2F;h4&gt;
&lt;p&gt;For KDE, if the &lt;code&gt;KDE_SESSION_VERSION&lt;&#x2F;code&gt; environment variable is set &lt;code&gt;kde-open&lt;&#x2F;code&gt; is used for KDE 4 and &lt;code&gt;kde-open5&lt;&#x2F;code&gt; for for KDE 5. Otherwise &lt;code&gt;kfmclient exec&lt;&#x2F;code&gt; (with some logic to fix the exit code) is used for even older KDE versions.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;commit&#x2F;c10cdaf8a03997cc18e51ee6299f0dcc02c34870&quot;&gt;KDE 6 will use &lt;code&gt;kde-open&lt;&#x2F;code&gt; again&lt;&#x2F;a&gt;, but that isn&#x27;t in version &lt;code&gt;1.1.3&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;lxde&quot;&gt;Lxde&lt;&#x2F;h4&gt;
&lt;p&gt;For Lxde &lt;code&gt;pcmanfm&lt;&#x2F;code&gt; is used if present, but only for filepaths and &lt;code&gt;file:&#x2F;&#x2F;&lt;&#x2F;code&gt; URIs. Otherwise falling back to &lt;code&gt;open_generic&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;generic-opener&quot;&gt;Generic Opener&lt;&#x2F;h2&gt;
&lt;p&gt;The generic opener is invoked by the calling the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;blob&#x2F;v1.1.3&#x2F;scripts&#x2F;xdg-open.in#L402&quot;&gt;&lt;code&gt;open_generic&lt;&#x2F;code&gt; function&lt;&#x2F;a&gt; with the filepath or URI as the only argument.&lt;&#x2F;p&gt;
&lt;p&gt;This is yet another heuristics spaghetti function that tries to delegate work to other tools that may already be configured.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;opening-files&quot;&gt;Opening files&lt;&#x2F;h3&gt;
&lt;p&gt;In case the passed argument looks like a filepath or &lt;code&gt;file:&#x2F;&#x2F;&lt;&#x2F;code&gt; URI, &lt;code&gt;xdg-open&lt;&#x2F;code&gt; tries some additional openers.&lt;&#x2F;p&gt;
&lt;p&gt;If the file doesn&#x27;t exist the script will exit with an appropriate error message.&lt;&#x2F;p&gt;
&lt;p&gt;If an X or Wayland display is present (checked using &lt;code&gt;has_display&lt;&#x2F;code&gt;) the files mime-type is determined using &lt;code&gt;xdg-mime query filetype&lt;&#x2F;code&gt; and the function &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;xdg-open-under-the-hood&#x2F;#open-generic-xdg-mime&quot;&gt;&lt;code&gt;open_generic_xdg_mime&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is attempted for opening.&lt;&#x2F;p&gt;
&lt;p&gt;If no display is present or &lt;code&gt;open_generic_xdg_mime&lt;&#x2F;code&gt; returned, opening with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;run-mailcap&quot;&gt;&lt;code&gt;run-mailcap&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is attempted if present. &lt;code&gt;run-mailcap&lt;&#x2F;code&gt; apparently is the Debian solution to the problem from before freedesktop standardised file opening.&lt;&#x2F;p&gt;
&lt;p&gt;If we have a display and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;mimeopen&quot;&gt;&lt;code&gt;mimeopen&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; from the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;metacpan.org&#x2F;dist&#x2F;File-MimeInfo&quot;&gt;File-MimeInfo perl package&lt;&#x2F;a&gt; is present attempt to open using that.&lt;&#x2F;p&gt;
&lt;p&gt;If all of those miss then the &lt;code&gt;open_generic&lt;&#x2F;code&gt; function continues with treating the first argument given to it as if it was an URI.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;opening-uris&quot;&gt;Opening URIs&lt;&#x2F;h3&gt;
&lt;p&gt;For URIs the following happens until one of the steps finds a way to open the URI.&lt;&#x2F;p&gt;
&lt;p&gt;If there is a display (X or Wayland, checked by &lt;code&gt;has_display&lt;&#x2F;code&gt;) try to derive a URI scheme and open it using &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;xdg-open-under-the-hood&#x2F;#open-generic-xdg-mime&quot;&gt;&lt;code&gt;open_generic_xdg_mime&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; with a mime-type of &lt;code&gt;x-scheme-handler&#x2F;&amp;lt;scheme&amp;gt;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This allows registering custom URI handlers using the already existing mechanism for mime-types.&lt;&#x2F;p&gt;
&lt;p&gt;If the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; environment variable (a colon &lt;code&gt;:&lt;&#x2F;code&gt; separated command list) is set try all browser commands in that variable (if &lt;code&gt;%s&lt;&#x2F;code&gt; is present insert the URI in the desired position using &lt;code&gt;printf&lt;&#x2F;code&gt;) until one of them succeeds. (implemented in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;blob&#x2F;v1.1.3&#x2F;scripts&#x2F;xdg-open.in#L374&quot;&gt;&lt;code&gt;open_envvar&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;If unset try a set of sane defaults depending on whether a display is present or not.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; The &lt;code&gt;BROWSER&lt;&#x2F;code&gt; variable was cleaned up at the start of &lt;code&gt;xdg-open&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;At the time of writing the list of browser commands was …&lt;&#x2F;p&gt;
&lt;p&gt;… when a display is present:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;x-www-browser&lt;&#x2F;li&gt;
&lt;li&gt;firefox&lt;&#x2F;li&gt;
&lt;li&gt;iceweasel&lt;&#x2F;li&gt;
&lt;li&gt;seamonkey&lt;&#x2F;li&gt;
&lt;li&gt;mozilla&lt;&#x2F;li&gt;
&lt;li&gt;epiphany&lt;&#x2F;li&gt;
&lt;li&gt;konqueror&lt;&#x2F;li&gt;
&lt;li&gt;chromium&lt;&#x2F;li&gt;
&lt;li&gt;chromium-browser&lt;&#x2F;li&gt;
&lt;li&gt;google-chrome&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;… for the terminal (always attempted):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;www-browser&lt;&#x2F;li&gt;
&lt;li&gt;links2&lt;&#x2F;li&gt;
&lt;li&gt;elinks&lt;&#x2F;li&gt;
&lt;li&gt;links&lt;&#x2F;li&gt;
&lt;li&gt;lynx&lt;&#x2F;li&gt;
&lt;li&gt;w3m&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;open-generic-xdg-mime&quot;&gt;&lt;code&gt;open_generic_xdg_mime&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;blob&#x2F;v1.1.3&#x2F;scripts&#x2F;xdg-open.in#L335&quot;&gt;This function&lt;&#x2F;a&gt; takes a filepath (or URI) and a mime-type as input.&lt;&#x2F;p&gt;
&lt;p&gt;It uses &lt;code&gt;xdg-mime query default&lt;&#x2F;code&gt; to get the name of the &lt;code&gt;.desktop&lt;&#x2F;code&gt; file, then searches for that in all &lt;code&gt;$XDG_DATA_DIRS&#x2F;applications&#x2F;&lt;&#x2F;code&gt;, when found parses the &lt;code&gt;Exec&lt;&#x2F;code&gt; and other needed fields, runs the resulting command and makes the script exit.&lt;&#x2F;p&gt;
&lt;p&gt;If it didn&#x27;t find a matching file it returns.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;xdg-open&lt;&#x2F;code&gt; tries to open a file or URI.&lt;&#x2F;p&gt;
&lt;p&gt;If it recognises the environment and knows a &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;xdg-open-under-the-hood&#x2F;#desktop-specific-openers&quot;&gt;opener that is specific to that environment&lt;&#x2F;a&gt; it will use that.&lt;&#x2F;p&gt;
&lt;p&gt;If there is no specific opener it will fall back to a cascade of generic openers, preferring the freedesktop way, falling back to legacy openers. The freedesktop mime-type to application mapping uses &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;xdg-mime&quot;&gt;&lt;code&gt;xdg-mime&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For URIs it will attempt to open the URI as if it was a file with the mime-type &lt;code&gt;x-scheme-handler&#x2F;&amp;lt;scheme&amp;gt;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The last configurable fallback is the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; environment variable which is a colon &lt;code&gt;:&lt;&#x2F;code&gt; separated list of commands of which all will be tried until the first one succeeds. If &lt;code&gt;%s&lt;&#x2F;code&gt; is present in the command the URI or filepath will be inserted in its place instead of being appended.&lt;&#x2F;p&gt;
&lt;p&gt;If the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; environment variable is empty, it falls back to a list of hard coded default applications.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;extending&quot;&gt;Extending&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;For fully custom behaviour wrapping &lt;code&gt;xdg-open&lt;&#x2F;code&gt; still seems to be the best choice.&lt;&#x2F;li&gt;
&lt;li&gt;Defaults can be customised by setting a handler in the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; environment variable.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;xdg-open&lt;&#x2F;code&gt; listens to what &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; says on which mime-type a file is and which application to use for opening.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;expectations&quot;&gt;Expectations&lt;&#x2F;h2&gt;
&lt;p&gt;That was quite a bit more desktop-specific stuff in there than expected, while the generic part was supisingly simple at the high level logic view.&lt;&#x2F;p&gt;
&lt;p&gt;One thing that was unexpected was that there is no obvious connection to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;xdg-settings&quot;&gt;&lt;code&gt;xdg-settings&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; i.e. for the default web browser, but maybe that is hidden in &lt;code&gt;xdg-mime&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll try to follow up with articles on both tools. Make sure you subscribe to my &lt;a href=&quot;&#x2F;atom.xml&quot;&gt;atom feed&lt;&#x2F;a&gt; 😉. Bye!&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Improving an fzf example with null bytes</title>
        <published>2023-07-09T00:00:00+00:00</published>
        <updated>2023-07-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/improving-an-fzf-example/"/>
        <id>https://slatecave.net/blog/improving-an-fzf-example/</id>
        
        <summary type="text">or: Why good shell scripts are sometimes bad for explaining</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/improving-an-fzf-example/">&lt;h2 id=&quot;what-is-the-problem&quot;&gt;What is the problem?&lt;&#x2F;h2&gt;
&lt;p&gt;On the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fzf#preview-window=&quot;&gt;fzf manpage&lt;&#x2F;a&gt; the &lt;code&gt;--preview-window&lt;&#x2F;code&gt; option is shown off using a combination of &lt;code&gt;git grep&lt;&#x2F;code&gt; for querying every line of every file in the current git repository, &lt;code&gt;fzf&lt;&#x2F;code&gt; for searching and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;bat&quot;&gt;&lt;code&gt;bat&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; for a preview with the line currently selected in fzf highlighted and scrolled into the middle of the preview window. (pretty awesome example if you asked me.)&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;The example from the manpage&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Preview with bat, matching line in the middle of the window below&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; the fixed header of the top 3 lines&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; […]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; grep&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-line-number&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;fzf&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-delimiter&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; :&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;	-&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-preview&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;bat --style=full --color=always --highlight-line {2} {1}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;	-&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-preview-window&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;~3,+{2}+3&#x2F;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;The example works by relying on the fact that the output of the &lt;code&gt;git grep&lt;&#x2F;code&gt; looks like &lt;code&gt;&amp;lt;filename&amp;gt;:&amp;lt;linenumber: …&lt;&#x2F;code&gt;, split at the colon, &lt;code&gt;{1}&lt;&#x2F;code&gt; is the filename, &lt;code&gt;{2}&lt;&#x2F;code&gt; is the line number.&lt;&#x2F;p&gt;
&lt;p&gt;While the example is very good at explaining how the &lt;code&gt;--preview-window&lt;&#x2F;code&gt; interacts with the &lt;code&gt;--delimiter&lt;&#x2F;code&gt; (its primary purpose), it breaks once there is a colon somewhere in a filename, then &lt;code&gt;{1}&lt;&#x2F;code&gt; and &lt;code&gt;{2}&lt;&#x2F;code&gt; become two halves of an incomplete filename and bat has no idea what it should do with that.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example output produced by &lt;code&gt;git grep --line-number &quot;&quot;&lt;&#x2F;code&gt;. Note that the last line looks a bit funny.&lt;&#x2F;figcaption&gt;
	&lt;pre&gt;&lt;samp&gt;test.txt:1:Lorem ipsum
test.txt:2:dolor sid …
throw:off:1:Does this filename confuse fzf?
&lt;&#x2F;samp&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;let-s-fix-it&quot;&gt;Let&#x27;s fix it!&lt;&#x2F;h2&gt;
&lt;p&gt;The Solution is actually pretty simple: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;git-grep&quot;&gt;&lt;code&gt;git grep&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; supports a &lt;code&gt;-z&lt;&#x2F;code&gt; flag, making it use null bytes instead of colons for separating filename, line number and content, tell fzf to use that as the delimiter using &lt;code&gt;--delimiter &#x27;\0&#x27;&lt;&#x2F;code&gt; and, the preview isn&#x27;t confused anymore.&lt;&#x2F;p&gt;
&lt;p&gt;One remaining problem is that the menu looks like this: &lt;samp&gt;test.txt1Lorem ipsum&lt;&#x2F;samp&gt;, not very readable because those nullbytes are invisible.&lt;&#x2F;p&gt;
&lt;p&gt;But there is nothing stopping us from replacing all null bytes with nullbytes followed by a colon and telling fzf to use that combination as a delimiter. &lt;code&gt;git&lt;&#x2F;code&gt; doesn&#x27;t have something built in for that, but that is what &lt;code&gt;sed&lt;&#x2F;code&gt; was built for.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;The example from the manpage modified to use null separators in addition to human readable ones.&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; grep&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;z&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-line-number&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sed&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;E&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;s|\x00|\x00:|g&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;fzf&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-delimiter&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;\0:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;    -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-preview&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;bat --style=full --color=always --highlight-line {2} {1}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;    -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-preview-window&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;~3,+{2}+3&#x2F;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;In case this looks a bit dull add a &lt;code&gt;--color=auto&lt;&#x2F;code&gt; to the git command and a &lt;code&gt;--ansi&lt;&#x2F;code&gt; to fzf.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;done&quot;&gt;Done&lt;&#x2F;h2&gt;
&lt;p&gt;Given that the more correct version takes noticeably more brain-cycles to decode it is no surprise they used the simple version for the manual.&lt;&#x2F;p&gt;
&lt;p&gt;In case you want to: &lt;a href=&quot;&#x2F;notebook&#x2F;shell-null-termination&quot;&gt;Learn more about null separation in the shell&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Happy experimenting!&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Slatectx: Adding some context to the desktop</title>
        <published>2023-04-23T00:00:00+00:00</published>
        <updated>2023-04-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/slatectx-why-and-how/"/>
        <id>https://slatecave.net/blog/slatectx-why-and-how/</id>
        
        <summary type="text">Most workflows today are app-centric, this is my attempt at trying something different</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/slatectx-why-and-how/">&lt;h2 id=&quot;why-i-created-slatectx&quot;&gt;Why I created slatectx&lt;&#x2F;h2&gt;
&lt;p&gt;To put my usecase and workflow in a few sentences:&lt;&#x2F;p&gt;
&lt;p&gt;What I&#x27;m mostly doing is editing textfiles, writing code, configuration, notes and so on.
I&#x27;m doing a lot of thing in the shell (bash) and my text editor (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kakoune.org&quot;&gt;kakoune&lt;&#x2F;a&gt;), usually working with more than one window open using i3 or sway as a window manager. Leading to …&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I want a new terminal and I want it in the same pwd as the last terminal I interacted with on that workspace!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The workspace constraint is because I sometimes switch between a reference or otherwise related project to the main thing I&#x27;m working on and I don&#x27;t want the pwd on one to influence the pwd on the other and usually I already separate those by putting them onto a different workspace.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;identifying-contexts&quot;&gt;Identifying Contexts&lt;&#x2F;h2&gt;
&lt;p&gt;To be able to do anything that is context aware, one needs a way to identify a certain context and a way of finding out which context currently applies. Since this journey started with a simple POC and then stayed pretty simple slatectx contexts are named by opaque (no assumptions based on the name) strings containing alphanumeric characters, dashes &lt;code&gt;-&lt;&#x2F;code&gt; and underscores &lt;code&gt;_&lt;&#x2F;code&gt; with slashes &lt;code&gt;\&lt;&#x2F;code&gt; being forbidden because these have to work in filenames.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;generating-context-names&quot;&gt;Generating Context Names&lt;&#x2F;h3&gt;
&lt;p&gt;With the requirements being that I don&#x27;t want an additional daemon for doing housekeeping and be able to debug where the names are coming from I settled on the following rough naming scheme:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;session_type&amp;gt;&amp;lt;sesion_id_or_display&amp;gt;-&amp;lt;workspace&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;b&gt;For example,&lt;&#x2F;b&gt; the workspace I&#x27;m writing this post on maps to the context &lt;code&gt;y1-9&lt;&#x2F;code&gt;, derived from me currently using a wayland session that has &lt;code&gt;wayland-1&lt;&#x2F;code&gt; set as its &lt;code&gt;WAYLAND_DISPLAY&lt;&#x2F;code&gt; and being on workspace 1. With an ssh connection this would simply be &lt;code&gt;ssh&lt;&#x2F;code&gt; and tmux uses &lt;code&gt;t-&amp;lt;tmux_pid&amp;gt;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;To generate those identifiers my approach involved &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.local&#x2F;bin&#x2F;slatectx-get-context-name&quot;&gt;finding out which session type is currently used&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.local&#x2F;bin&#x2F;slatectx-get-workspace&quot;&gt;getting the workspace&lt;&#x2F;a&gt; and then stitching that together in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.local&#x2F;bin&#x2F;slatectx-get-context-name&quot;&gt;&lt;code&gt;slatectx-get-context-name&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; which is the central interface for getting the name of the current context (A bit like xdg-open for opening files).&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;slatectx-get-context-name&lt;&#x2F;code&gt; script is supposed to be replaced when one wishes to use a different context naming logic.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;switching-directories&quot;&gt;Switching directories&lt;&#x2F;h2&gt;
&lt;p&gt;With the problem of figuring out the current context out solved one can implement things like a mot recently used directory (the original motivation). The logic behind that has now moved to its own tool &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.local&#x2F;bin&#x2F;slatectx-pwd&quot;&gt;&lt;code&gt;slatectx-pwd&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;When used with the &lt;i&gt;set&lt;&#x2F;i&gt; command it validates that the directory exists (and is a directory), turns a relative into an absolute path and stores the result in &lt;code&gt;$XDG_RUNTIME_DIR&#x2F;slatectx-pwd&#x2F;$(slatectx-get-context-name)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;i&gt;get&lt;&#x2F;i&gt; operation reads this file, runs the checks again (the filesystem could have been modified in between) and prints the path out, depending on the check results with an error return code.&lt;&#x2F;p&gt;
&lt;p&gt;This little helper is used in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.bashrc&quot;&gt;my bashrc&lt;&#x2F;a&gt; to &lt;code&gt;cd&lt;&#x2F;code&gt; into the last used directory on startup and hooked into the &lt;code&gt;PROMPT_COMMAND&lt;&#x2F;code&gt; to record the last used directory.&lt;&#x2F;p&gt;
&lt;p&gt;A similar thing happens with a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.local&#x2F;bin&#x2F;filedialog&quot;&gt;filedialog script&lt;&#x2F;a&gt; I&#x27;m using to quickly navigate the filesystem and open files, which on startup gets passed the result of &lt;code&gt;slatectx-pwd get&lt;&#x2F;code&gt; and has a hook to set the pwd on a change-directory event.&lt;&#x2F;p&gt;
&lt;p&gt;Another thing that is set up in my shell and text-editor is a &lt;code&gt;cdx&lt;&#x2F;code&gt; alias for quickly jumping to the slatectx-pwd in an already open session.&lt;&#x2F;p&gt;
&lt;p&gt;There is also experimental configuration for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.config&#x2F;lf&#x2F;lfrc&quot;&gt;integrating it with lf&lt;&#x2F;a&gt; which I don&#x27;t use as often as I&#x27;d like to, because I haven&#x27;t figured out how to make use of lf in a way that makes me not run into a wall at some point where a command I expect isn&#x27;t there because I haven&#x27;t figured out how to configure that part of lf yet. (one of the reasons I wrote the filedialog script)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shared-text-editor&quot;&gt;Shared text-editor&lt;&#x2F;h2&gt;
&lt;p&gt;One thing I missed after switching to kakoune from gedit was that with gedit I could just have a keybind that opened another text editing window and I could just move the tabs between them (not missing the tab UI though).&lt;&#x2F;p&gt;
&lt;p&gt;With kakoune having a client-server architecture one could replicate having multiple clients share the same open buffers, if only there was a way to find out what the server is call … yes one can tell kakoune what the socket should be called and whether it should start in client or server mode. That&#x27;s what the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.local&#x2F;bin&#x2F;kak-slatectx&quot;&gt;&lt;code&gt;kak-slatectx&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.local&#x2F;bin&#x2F;kak-e&quot;&gt;&lt;code&gt;kak-e&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; are for.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;kak-slatectx&quot;&gt;kak-slatectx&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;kak-slatectx&lt;&#x2F;code&gt; replaces the &lt;code&gt;kak&lt;&#x2F;code&gt; command (using an alias) and by default tells kakoune to start with the given slatectx context as the socket name in client or server mode depending on the presence of the socket file. To make it a full replacement, this behaviour is completely disabled, should one attempt to pass the &lt;code&gt;-c&lt;&#x2F;code&gt; or &lt;code&gt;-s&lt;&#x2F;code&gt; options to kakoune.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;kak-e&quot;&gt;kak-e&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;kak-e&lt;&#x2F;code&gt; is the slatectx enabled fileopener for kakoune, for it to work kakoune has to remember the last window one interacted with, which is a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.config&#x2F;kak&#x2F;autoload&#x2F;current-session.kak&quot;&gt;pretty simple &lt;code&gt;FocusIn&lt;&#x2F;code&gt; hook paired with a custom &lt;code&gt;current_client&lt;&#x2F;code&gt; option&lt;&#x2F;a&gt;. &lt;code&gt;kak-e&lt;&#x2F;code&gt; constructs an edit command for kakoune that then opens the given files in the last focused window, launching a new kakoune instance if needed.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;reusing-an-image-viewer&quot;&gt;Reusing an image viewer&lt;&#x2F;h2&gt;
&lt;p&gt;Another thing I set up a wrapper for is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sr.ht&#x2F;~exec64&#x2F;imv&#x2F;&quot;&gt;imv&lt;&#x2F;a&gt; (which at the time of writing is looking for a new maintainer!), an image viewer which also has a socket for remote controlling it. Since imv always uses its pid for this, an approach similar to slatectx-pwd is used to store the pid of the currently running imv instance and then using that like &lt;code&gt;kak-e&lt;&#x2F;code&gt; to open files in an already open window (Which currently is a bit glitchy).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.local&#x2F;bin&#x2F;imv-slatectx&quot;&gt;&lt;code&gt;imv-slatectx&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; also has an option to disable launching an imv instance, which is useful for preview displays. Open an Image, look at it select the next and the image viewer automatically jumps to the selected image. Close the image viewer and the previews are gone with more screen real estate for managing files. (I&#x27;ve integrated it like this into my lf configuration and it is pretty useful)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Here I described why and how I used some relatively simple shellscript to implement some shared pieces of information that can be used to &lt;b&gt;make applications work together.&lt;&#x2F;b&gt; Something I miss with most GUI applications that maybe have a hared recently used, but completely ignore what the one using them is currently working on.&lt;&#x2F;p&gt;
&lt;p&gt;That said: Feel free to to reuse my code and ideas on this one and nag me on writing documentation and packaging if you are interested.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>What is NAT and why you don&#x27;t want it</title>
        <published>2023-03-27T00:00:00+00:00</published>
        <updated>2023-03-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/nat/"/>
        <id>https://slatecave.net/blog/nat/</id>
        
        <summary type="text">What is Network Address Translation, how does it work and why you don&#x27;t want it.</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/nat/">&lt;p&gt;You may or may not have heard of NAT, Network Address Translation, here is how it works, why you don&#x27;t want it and why you probably have to use it anyway.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-the-internet-was-supposed-to-work&quot;&gt;How the Internet was supposed to work&lt;&#x2F;h2&gt;
&lt;p&gt;The original idea of the internet was that every machine is reachable, can directly connect to other machines using nothing but their public address and also offer serveries. Which worked pretty well until …&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-the-internet-community-started-nating&quot;&gt;Why the Internet community started NATing&lt;&#x2F;h3&gt;
&lt;p&gt;… the Internet started running out of publicly reachable addresses. So everyone started assigning private IP-Addresses to their internal networks, trying to get the most out of the precious public address space.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;legacy-nat&quot;&gt;Legacy NAT&lt;&#x2F;h2&gt;
&lt;p&gt;Legacy NAT is simply swapping one IP-Address for another, with a 1:1 relationship between private and public addresses. Either statically assigned or on demand to a machine that wants to connect, so if you wanted to connect to the Internet you have to wait for a free Line (&lt;i&gt;public Address&lt;&#x2F;i&gt;) from a pool of available addresses (called a &lt;i&gt;NAT Pool&lt;&#x2F;i&gt;). Hello old dialup!&lt;&#x2F;p&gt;
&lt;p&gt;These are respectively called Static and Dynamic NAT.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;modern-nat-port-address-translation-pat&quot;&gt;Modern NAT - Port Address Translation (PAT)&lt;&#x2F;h2&gt;
&lt;p&gt;Nowadays with 10s of devices per household with active connections to outside servers that Legacy approach of give a machine an IP when it needs one and recycle after doesn&#x27;t work anymore.&lt;&#x2F;p&gt;
&lt;p&gt;But one can abuse the fact that most machines don&#x27;t offer services for the public and therefore have a lot of outgoing and no incoming connections. So instead of assigning whole IP-Addresses we made our Routers translate combinations of Layer 4 ports and IP-Address.&lt;&#x2F;p&gt;
&lt;p&gt;If one now wants to connect to the server at &lt;code&gt;1.2.3.4&lt;&#x2F;code&gt; on port 443 from &lt;code&gt;192.168.0.10&lt;&#x2F;code&gt; from port 12345 via a router assigned &lt;code&gt;203.0.113.3&lt;&#x2F;code&gt; (it would with multiple public addresses, too) as its public address  …&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The router tries to find a port on its public address using either the original source port or if already in use because someone else behind the NAT had the same idea of a great source port a port &lt;b&gt;near&lt;&#x2F;b&gt; the original port. Let&#x27;s say we ended up with 12347.&lt;&#x2F;li&gt;
&lt;li&gt;Now the router replaces the internal address with the public one and swaps out the port for the publicly reachable one and remembers the combination until the connection closes, there is a timeout.&lt;&#x2F;li&gt;
&lt;li&gt;If now an answer arrives on &lt;code&gt;203.0.113.3&lt;&#x2F;code&gt; port 12345 the NAT knows that the real destination is &lt;code&gt;192.168.0.10&lt;&#x2F;code&gt; on port 12345 and vice versa.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For ICMP the identifiers can be used similar to port numbers. UDP based protocols need additional support from the NAT as UDP itself has no way of matching requests with replies.&lt;&#x2F;p&gt;
&lt;p&gt;With that setup one can support up to ~&lt;b&gt;60000&lt;&#x2F;b&gt; concurrent TCP connections with a single public IP-Address, great isn&#x27;t it?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;carrier-grade-nat&quot;&gt;Carrier grade NAT&lt;&#x2F;h2&gt;
&lt;p&gt;Well, your provider most probably thinks that that is great because now they can serve a whole lot of customers and still only pay for a handful of public IPs. There is even the &lt;code&gt;100.64.0.0&#x2F;10&lt;&#x2F;code&gt; network reserved in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc6598.html&quot;&gt;RFC 6598&lt;&#x2F;a&gt; for exactly this purpose.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;problems-with-nat&quot;&gt;Problems with NAT&lt;&#x2F;h2&gt;
&lt;p&gt;The problem with this is now that your average client is behind two or even more layers of NAT, one at their provider, one built into their home-router. That comes with at least two problems:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;For real-time applications (Videochat,SIP) you want as direct of a connection as possible which is the opposite of what a NAT does.&lt;&#x2F;li&gt;
&lt;li&gt;Long running connections (i.e. your chat or Mail-client waiting for realtime notifications or an ssh session) may get terminated because the NAT needs free ports because 60000 connections is surprisingly little scaling up.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Also NAT &lt;em&gt;actively hinders innovation&lt;&#x2F;em&gt;. Ever heared of SCTP, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Stream_Control_Transmission_Protocol&quot;&gt;Stream Control Transmission Protocol&lt;&#x2F;a&gt; (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc9260.html&quot;&gt;RFC 9260&lt;&#x2F;a&gt;)? One of the reasons you have probably never seen it in the wild is because old NATs (like Joe Averages router running outdated firmware) have no clue what to do with it. Same problem with  &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;QUIC&quot;&gt;QUIC&lt;&#x2F;a&gt; (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc9000.html&quot;&gt;RFC 9000&lt;&#x2F;a&gt;) which is based on UDP, because punching a hole into the NAT that has the right shape is usually difficult to impossible you probably won&#x27;t find it in the wild.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;nat64&quot;&gt;NAT64&lt;&#x2F;h2&gt;
&lt;p&gt;NAT64 was a way to translate IPv6 adresses in the &lt;code&gt;64:ff9b::&#x2F;96&lt;&#x2F;code&gt; network to IPv4 but it has been deprecated in favour of dualstack configurations. Probably because for that to work either direct application support is needed or a dns Server which has to translate IPv4 addresses to the NATable IPv6 ones.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;myths-around-nat&quot;&gt;Myths around NAT&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;great-i-don-t-need-a-firewall&quot;&gt;Great I don&#x27;t need a Firewall!&lt;&#x2F;h3&gt;
&lt;p&gt;Yes you do! A NAT only denies incoming connections because no machine in the internal network currently punched a hole in it with UPnP. Your NAT is also waaay too complex for trusting it with denying unwanted connections. (Don&#x27;t confuse it with a NAT built into a firewall)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;i-want-ipv6-nat-because-of-privacy&quot;&gt;I want IPv6 NAT because of Privacy!&lt;&#x2F;h3&gt;
&lt;p&gt;If you have this kind of network access you can as well reassign random addresses using your favourite IPv6 address assignment mechanism.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;solving-the-nat-problems&quot;&gt;Solving the NAT problems&lt;&#x2F;h2&gt;
&lt;p&gt;So you don&#x27;t like your NAT anymore? What if the Internet had an address space large enough for every device? Well its your lucky day the solution is called IPv6 and if in 2023 your ISP doesn&#x27;t give you IPv6, ask them nicely why they are stuck in the past.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Building an animated &lt;details&gt; element</title>
        <published>2023-03-17T00:00:00+00:00</published>
        <updated>2023-03-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/animated-details-element/"/>
        <id>https://slatecave.net/blog/animated-details-element/</id>
        
        <summary type="text">Trying to build an accessible accordion element for reasons</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/animated-details-element/">&lt;p&gt;Accordion elements on the web seem to be everywhere and almost everyone seems to implement them in a way that only works for the well sighted user with a modern browser with JavaScript enabled. I want to show that these can be built in a better way.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;so-why-are-accordions-used-at-all&quot;&gt;So why are accordions used at all?&lt;&#x2F;h2&gt;
&lt;p&gt;While using accordions or expanders in some situations is a bad idea, sometimes they help with uncluttering, hiding information that isn&#x27;t needed by most people but available from the already loaded document on request when needed.&lt;&#x2F;p&gt;
&lt;p&gt;An example would be to provide contact details on a page, usually visitors don&#x27;t care, but when they do, that information is easy to find.&lt;&#x2F;p&gt;
&lt;p&gt;Another example would be an event log with the summaries always being visible and the details being available inline on request. sourcehut and YunoHost use this pattern.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Accordions usually are multiple collapsible elements grouped together and only allow one open at a time. I&#x27;m not going into that functionality here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-html-way-of-building-an-accordion&quot;&gt;The HTML way of building an accordion …&lt;&#x2F;h2&gt;
&lt;p&gt;This pattern is so useful that HTML has elements for exactly that usecase, the &lt;code&gt;&amp;lt;details&amp;gt;&lt;&#x2F;code&gt; and &lt;code&gt;&amp;lt;summary&amp;gt;&lt;&#x2F;code&gt; elements, one simply puts a summary inside a details tag along with some content and it works, in all browsers, without JavaScript, with any input method the browser supports.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Apparently &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.scottohara.me&#x2F;blog&#x2F;2022&#x2F;09&#x2F;12&#x2F;details-summary.html&quot;&gt;the details element and accessibility is it&#x27;s own can of worms, too&lt;&#x2F;a&gt;. As the article mentions: You decide.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example of such a details element&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;	&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Your DNS records for example.org are not configured&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;	&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Add an &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; record pointing to &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;93.184.216.34&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;	&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Add an &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;AAAA&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; record pointin to &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;2606:2800:220:1:248:1893:25c8:1946&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;	&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;It is important to set both, otherwise peopley may be unable to access your server.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It then would be displayed similar to this:&lt;&#x2F;p&gt;
&lt;details&gt;
	&lt;summary&gt;Your DNS records for example.org are not configured&lt;&#x2F;summary&gt;
	&lt;p&gt;Add an &lt;code&gt;A&lt;&#x2F;code&gt; record pointing to &lt;code&gt;93.184.216.34&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
	&lt;p&gt;Add an &lt;code&gt;AAAA&lt;&#x2F;code&gt; record pointing to &lt;code&gt;2606:2800:220:1:248:1893:25c8:1946&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
	&lt;p&gt;It is important to set both, otherwise people may be unable to access your server.&lt;&#x2F;p&gt;
&lt;&#x2F;details&gt;
	
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;what-people-are-doing-instead&quot;&gt;What people are doing instead …&lt;&#x2F;h2&gt;
&lt;p&gt;You may have been surprised by the fact that there is an HTML element for exactly this purpose because you have never seen one in the wild.&lt;&#x2F;p&gt;
&lt;p&gt;There are probably three reasons people don&#x27;t use it:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;They simply don&#x27;t know that it exists&lt;&#x2F;li&gt;
&lt;li&gt;They found out, tried to animate it and failed to do so&lt;&#x2F;li&gt;
&lt;li&gt;They use a library which fell victim to one of the previous reasons.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The result being that they give up and write their own accordion from scratch, button &lt;code&gt;onclick&lt;&#x2F;code&gt; handler, element with changing styles, done. Except that the result is usually an accessibility nightmare, and completely falls apart when for some reason the JavaScript needed to operate it isn&#x27;t available.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-is-the-details-element-seemingly-hard-to-animate&quot;&gt;Why is the details element seemingly hard to animate?&lt;&#x2F;h2&gt;
&lt;p&gt;Animating a details tag is simple, one can use the &lt;code&gt;[open=&quot;&quot;]&lt;&#x2F;code&gt; CSS selector to find out whether the details element is currently open, put some keyframes and an animation (more details later) on it and it opens with a smooth animation and closing is … not animated at all.&lt;&#x2F;p&gt;
&lt;p&gt;No your CSS isn&#x27;t at fault here.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;how-the-details-element-works&quot;&gt;How the details element works.&lt;&#x2F;h3&gt;
&lt;p&gt;The details element is a bit of a unicorn in that it manipulates the page tree. When the content gets shown elements are added and when it closes the elements get removed from the page instantly, and while that is great as it saves resources PR is not happy because they want an animation.&lt;&#x2F;p&gt;
&lt;p&gt;My personal guess is that this is where most developers who know about the details element give up.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;animating-it-anyway&quot;&gt;Animating it Anyway&lt;&#x2F;h2&gt;
&lt;p&gt;So we want an animation for sighted people with modern browsers and a good experience for everyone else, so lets try to achieve that by using the web as it was intended, HTML for content, CSS for eye candy and JavaScript for providing some optional functionality.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Animating slightly degrades accessibility (which is probably still better than with your completely custom accordion) of the details element.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;animated-details-element-demo&quot;&gt;You can find the final result over on Codeberg.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s start with the content, I&#x27;ll assume you have a plain details and summary tag filled with your content and we want to leave it as it is.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;	&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Your DNS records for example.org are not configured&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;	&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Add an &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; record pointing to &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;93.184.216.34&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;	&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Add an &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;AAAA&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; record pointin to &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;2606:2800:220:1:248:1893:25c8:1946&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;	&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;It is important to set both, otherwise peopley may be unable to access your server.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In case you want to follow along but only have a desktop browser: open a new tab, navigate to &lt;code&gt;about:blank&lt;&#x2F;code&gt; and open up the developer tools. Depending on the browser you now have a pretty good IDE with live preview.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-grand-opening&quot;&gt;The Grand Opening&lt;&#x2F;h3&gt;
&lt;p&gt;Before we worry about closing the element, lets worry about it opening.&lt;&#x2F;p&gt;
&lt;p&gt;The simplest way here is to simply animate the &lt;code&gt;max-height&lt;&#x2F;code&gt; property from the current height of the element (&lt;code&gt;--details-current-height&lt;&#x2F;code&gt;) to one screen height (&lt;code&gt;100vh&lt;&#x2F;code&gt;), filling only back in time in case the details element is taller than one screen height (i.e. on a smaller device), in that case it will simply snap to full height off screen and is fully viewable.&lt;&#x2F;p&gt;
&lt;p&gt;Because we currently have no idea what the current height of the details element is, falling back to an assumed &lt;code&gt;1em&lt;&#x2F;code&gt; as the height of our summary element works pretty well. (You may have to adjust this based on extra padding and margin you are applying)&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;@&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;keyframes&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt; details-appear&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	from&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;		max-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-misc&quot;&gt; var&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;--details-current-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-other z-unit&quot;&gt;em&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	to&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;		max-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-other z-unit&quot;&gt;vh&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-css&quot;&gt;open&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;	animation&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-other z-unit&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; linear&lt;&#x2F;span&gt;&lt;span&gt; details-appear&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;	animation-fill-mode&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; backwards&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;	&#x2F;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;needed for the max-height animation to work&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;	overflow&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; hidden&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With that the opening part is taken care of.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fading-out-of-existence&quot;&gt;Fading out of Existence&lt;&#x2F;h3&gt;
&lt;p&gt;Because the details element immediately closes and there is no way to animate that we have to put a delay between the actual interaction with the element and the &lt;code&gt;open&lt;&#x2F;code&gt; attribute being removed, so let&#x27;s add some JavaScript.&lt;&#x2F;p&gt;
&lt;p&gt;Because we want to hook on the interaction of a sighted user with the summary element, we use an &lt;code&gt;onclick&lt;&#x2F;code&gt; handler here and assume a &lt;code&gt;details_click_handler()&lt;&#x2F;code&gt; function.&lt;&#x2F;p&gt;
&lt;p&gt;The following function simply adds the &lt;code&gt;onclick&lt;&#x2F;code&gt; handler to every summary element on the page (if you only want to target specific elements … I&#x27;ll leave that as an exercise).&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;function&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; initalize_animated_details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;	var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; details_elements&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; document&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;getElementsByTagName&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;	for&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; details_elements&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable&quot;&gt;length&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		details_elements&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;onclick&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; details_click_handler&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To run this initalizer you can use any method you like, but since in my experience &lt;code&gt;document.onload&lt;&#x2F;code&gt; is a bit wonky when testing inline code on small documents here is a way to defer it until the first interaction.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;document&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;onclick&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;	&#x2F;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; we only need it once &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	document&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;onclick&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; undefined&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;	initalize_animated_details&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;	&#x2F;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; if the event was for a summary tag run the handler &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;	if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;target&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;tagName&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ===&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;SUMMARY&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;		return&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; details_click_handler&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;event&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;	&#x2F;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Otherwise let the browser handle the click &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;	return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With that set up we can now focus on the click handler, which has to do two things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;add a style-class that will trigger the animation&lt;&#x2F;li&gt;
&lt;li&gt;set a timer to actually close the element after the animation.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;figure&gt;
	&lt;figcaption&gt;A bare minimum implementation of the &lt;code&gt;details_click_handler&lt;&#x2F;code&gt;&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;function&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; details_click_handler&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;	let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; details&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;target&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;parentElement&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;	&#x2F;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; When opening, let the browser do its job &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;	if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-logical&quot;&gt;!&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;open&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;		return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;closing&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;	setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;function&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;open&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;remove&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;closing&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 500&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;	return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	&lt;figcaption&gt;Fun fact: if you try that one out right now you&#x27;ll get the experience of someone who is unable to see the closing animation.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;With the bare minimum of JavaScript added let&#x27;s add some CSS to get a proof of concept animated details element. This is basically the reversed opening from the current height, defaulting to &lt;code&gt;100vh&lt;&#x2F;code&gt; to the height of the summary element defaulting to &lt;code&gt;1em&lt;&#x2F;code&gt; again.&lt;&#x2F;p&gt;
&lt;p&gt;Again we&#x27;ll worry about actually setting those variables to something useful later.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;@&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;keyframes&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt; details-disappear&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	from&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;		max-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-misc&quot;&gt; var&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;--details-current-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-other z-unit&quot;&gt;vh&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	to&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;		max-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-misc&quot;&gt; var&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;--details-summary-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-other z-unit&quot;&gt;em&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-class&quot;&gt;closing&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;	animation&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-misc&quot;&gt; var&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;--details-close-animation-length&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; .5&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-other z-unit&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; linear&lt;&#x2F;span&gt;&lt;span&gt; details-disappear&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;	overflow&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; hidden&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;	max-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-misc&quot;&gt; var&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;--details-summary-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-other z-unit&quot;&gt;em&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now you should have a not pretty but animated closing details element.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;improving-the-animation&quot;&gt;Improving the Animation&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we have a proof of concept, lets make it usable.&lt;&#x2F;p&gt;
&lt;p&gt;The issues we currently have are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Handle an interaction while the animation is playing correctly.&lt;&#x2F;li&gt;
&lt;li&gt;Accessibility impact is larger than it could be.&lt;&#x2F;li&gt;
&lt;li&gt;The heights used are still placeholders.&lt;&#x2F;li&gt;
&lt;li&gt;The animation speeds don&#x27;t match up.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;handling-an-interaction-while-the-animation-is-playing&quot;&gt;Handling an interaction while the animation is playing&lt;&#x2F;h3&gt;
&lt;p&gt;Currently when one interacts while the animation is playing the behaviours is that the element keeps closing, which is not great.&lt;&#x2F;p&gt;
&lt;p&gt;To fix it we have to add some additional checks to our handler function to test if the &lt;code&gt;closing&lt;&#x2F;code&gt; class is present on an open element. If that&#x27;s the case we just remove the class gain instead of launching a new timeout and on the timeout part we just discard the timeout if the closing class isn&#x27;t present.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;lowering-the-impact-on-accessibility&quot;&gt;Lowering the impact on accessibility&lt;&#x2F;h3&gt;
&lt;p&gt;There are two problems I&#x27;m trying to solve with this one:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;When only relying on something that uses the accessibility interface of the browser (i.e. screenreader only) there is an inexplicable delay between interaction and the element being announced as collapsed, very confusing and&#x2F;or annoying.&lt;&#x2F;li&gt;
&lt;li&gt;When navigating by keyboard, no matter what the output device is, the content inside the details element can still be focused while the animation is playing, again potentially confusing and&#x2F;or annoying.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Each of the alone would be enough of a reason (at least for me) to not animate the closing of the element on keyboard interaction. (Fixing this is probably possible but at that point we are writing a full blown expander in JavaScript.)&lt;&#x2F;p&gt;
&lt;p&gt;Based on this we make a simple assumption: If you are not using a pointing device, you are probably not in the group that greatly benefits from an animation.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Of course the &quot;uses a pointer&quot; is a heuristic that will be sometimes be wrong.&lt;&#x2F;p&gt;
&lt;p&gt;To achieve this one can test for the &lt;code&gt;offsetX&lt;&#x2F;code&gt; and &lt;code&gt;offsetY&lt;&#x2F;code&gt; values of the event to find out weather the event came from a pointer or not, both are 0 they probably didn&#x27;t come from a pointer and we tell the browser to do what it would do without us interfering.&lt;&#x2F;p&gt;
&lt;p&gt;To stop the opening animation from playing when we won&#x27;t deliver a corresponding closing animation we add an &lt;code&gt;animated&lt;&#x2F;code&gt; class as an additional requirement in our two animation CSS rules.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;The offset check to only play animations for events from pointer devices&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;function&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; details_click_handler&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;	let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; details&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;target&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;parentElement&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;	if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;offsetX&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-logical&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;offsetY&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;remove&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;animated&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;		return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;animated&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;	&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; … Rest of the function goes here …&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;This way we can make PR and our bosses happy while mostly staying out of the way of people who appreciate clean websites more than fancy animations.&lt;&#x2F;p&gt;
&lt;p&gt;Note that after this the animation won&#x27;t play if the JavaScript isn&#x27;t running (The element will just snap open and closed like at the start).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;deglitching-the-animation&quot;&gt;Deglitching the animation&lt;&#x2F;h3&gt;
&lt;p&gt;Now that we have taken care of accessibility issues, we can put the CSS variables introduced earlier to use to make the animation responsive to the current state to avoid some glitches.&lt;&#x2F;p&gt;
&lt;p&gt;This means populating the &lt;code&gt;--details-summary-height&lt;&#x2F;code&gt; and &lt;code&gt;--details-current-height&lt;&#x2F;code&gt; properties directly on the details elements.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;This code is best placed after the offset check so that the properties are already set when opening and closing&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; summary_height&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;target&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;getBoundingClientRect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; current_height&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;getBoundingClientRect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;style&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;setProperty&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;--details-summary-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; summary_height&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;px&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;style&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;setProperty&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;--details-current-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; current_height&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;px&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;getting-the-timings-to-match&quot;&gt;Getting the timings to match&lt;&#x2F;h3&gt;
&lt;p&gt;After that is fixed the last open issue is, that the speed of the animation opening and the speed of the animation closing don&#x27;t match.&lt;&#x2F;p&gt;
&lt;p&gt;With the opening speed currently being one screen height minus summary per second it is pretty easy to get the closing animation to match that as when the accordion is open we can just ask the browser how tall the details and summary elements are, how tall the screen is and calculate the animation length from that. Since we already know the heights of both relevant elements we&#x27;ll use the variables from earlier.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;This code calculating and setting the animation length should be put right before we add the &lt;code&gt;closing&lt;&#x2F;code&gt; CSS class&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; close_animation_duration&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;current_height&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;summary_height&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;window&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;innerHeight&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;style&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;setProperty&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;--details-close-animation-length&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; close_animation_duration&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;To make the actual state match the timeout previously set to &lt;code&gt;500&lt;&#x2F;code&gt; should now be &lt;code&gt;close_animation_duration*1000+10&lt;&#x2F;code&gt; the extra 10ms is to allow the browser a bit of breathing room so that we don&#x27;t cut the animation off before it has finished.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-final-result&quot;&gt;The final result&lt;&#x2F;h3&gt;
&lt;p&gt;The resulting &lt;code&gt;details_click_handler&lt;&#x2F;code&gt; function now should look like the following.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;function&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; details_click_handler&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;	let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; details&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;target&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;parentElement&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;	&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; if called for a non-pointer event don&amp;#39;t play animations&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;	if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;offsetX&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-logical&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;offsetY&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;remove&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;animated&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;		return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;animated&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;	&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; tell the animations where to start and where to end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;	let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; summary_height&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;target&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;getBoundingClientRect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;	let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; current_height&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;getBoundingClientRect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;style&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;setProperty&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;--details-summary-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; summary_height&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;px&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;style&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;setProperty&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;--details-current-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; current_height&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;px&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;	&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; log the event for debugging&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;	&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; console.log(&amp;quot;click&amp;quot;, event);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;	&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; let the browser handle the opening&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;	if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-logical&quot;&gt;!&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;open&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;		return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;	if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;contains&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;closing&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;		&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; abort a closing animatiton when interrupted&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;remove&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;closing&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; else&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;		&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; calculate close animation length&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;		let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; close_animation_duration&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;current_height&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;summary_height&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;window&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;innerHeight&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;style&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;setProperty&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;--details-close-animation-length&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; close_animation_duration&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;		&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; trigger closing animation and correponding timeout&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;closing&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;		setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;function&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;			&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; discard timeout if someone interrupted the animation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;			if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;contains&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;closing&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;				&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; properly close the element and clean up the styleclass&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;				details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;open&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;				details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;remove&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;closing&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;			}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;		}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; close_animation_duration&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1000&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;	return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And our final CSS:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;@&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;keyframes&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt; details-appear&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	from&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;		max-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-misc&quot;&gt; var&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;--details-current-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-other z-unit&quot;&gt;em&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	to&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;		max-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-other z-unit&quot;&gt;vh&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;@&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;keyframes&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt; details-disappear&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	from&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;		max-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-misc&quot;&gt; var&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;--details-current-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-other z-unit&quot;&gt;vh&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	to&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;		max-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-misc&quot;&gt; var&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;--details-summary-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-other z-unit&quot;&gt;em&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-class&quot;&gt;animated&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-css&quot;&gt;open&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;	animation&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-other z-unit&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; linear&lt;&#x2F;span&gt;&lt;span&gt; details-appear&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;	animation-fill-mode&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; backwards&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;	overflow&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; hidden&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-class&quot;&gt;animated&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-class&quot;&gt;closing&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;	animation&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-misc&quot;&gt; var&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;--details-close-animation-length&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; .5&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-other z-unit&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; linear&lt;&#x2F;span&gt;&lt;span&gt; details-disappear&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;	overflow&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; hidden&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;	max-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-misc&quot;&gt; var&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;--details-summary-height&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-other z-unit&quot;&gt;em&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;updates&quot;&gt;Updates&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;2023-03-25&quot;&gt;2023-03-25&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Added note about accordions usually having a grouping functionality.&lt;&#x2F;li&gt;
&lt;li&gt;Added link to a [great article by Scott O&#x27;Hara explaining the problems around how details element is implemented in browsers].&lt;&#x2F;li&gt;
&lt;li&gt;Added explanation why I&#x27;m disabling the animation for non-pointer inputs here.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Having a look at greetd</title>
        <published>2022-08-16T00:00:00+00:00</published>
        <updated>2023-03-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/a-look-at-greetd/"/>
        <id>https://slatecave.net/blog/a-look-at-greetd/</id>
        
        <summary type="text">A writeup of the internals of the greetd login manager</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/a-look-at-greetd/">&lt;h2 id=&quot;what-is-greetd&quot;&gt;What is greetd?&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;greetd is a minimal and flexible login manager daemon that makes no assumptions about what you want to launch.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;In other words &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kl.wtf&#x2F;&quot;&gt;Kenny Levinsen&lt;&#x2F;a&gt; created an awesome piece of software that allows anyone to easily launch &lt;em&gt;anything&lt;&#x2F;em&gt; on login and that without being bound to a fronted.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-code&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;git.sr.ht&amp;#x2F;~kennylevinsen&amp;#x2F;greetd&quot; &gt;
		You can find greetd on sourcehut
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Most information in this article came from reading the greetd code and making notes on good old paper.&lt;&#x2F;p&gt;
&lt;p&gt;Note: Kenny Levinsen also wrote &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kl.wtf&#x2F;posts&#x2F;2022&#x2F;03&#x2F;12&#x2F;login-managers-an-introduction.html&quot;&gt;an interesting blogpost on login managers.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt;
If you want to understand how greetd works please read the code yourself,
this article alone is not enough!&lt;br &#x2F;&gt;
(but hopefully helpful)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;about&#x2F;me&#x2F;#contact&quot;&gt;In case you find a mistake or have feedback please contact me.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;greetd-structure&quot;&gt;How greetd is structured&lt;&#x2F;h2&gt;
&lt;p&gt;Greetd has a pretty modular approach to things and is made of a bunch of components one can look at separately.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The managing greetd server daemon (main focus)&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;greetd --session-worker&lt;&#x2F;code&gt; process (good to know)&lt;&#x2F;li&gt;
&lt;li&gt;The greeter&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I won&#x27;t be looking at the greeter as the interface is pretty well &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~kennylevinsen&#x2F;greetd&#x2F;tree&#x2F;master&#x2F;item&#x2F;man&#x2F;greetd-ipc-7.scd&quot;&gt;described in the manual&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The other two are both compiled into the same binary, the main function decides which component it should become after parsing the command line parameters.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;greetd-common&quot;&gt;The common part&lt;&#x2F;h2&gt;
&lt;p&gt;There is a small common part that always happens when the greetd binary starts up:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;It invokes the commandline and configuration parser to get its complete configuration.&lt;&#x2F;li&gt;
&lt;li&gt;It invokes &lt;code&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;mlockall&quot;&gt;mlockall()&lt;&#x2F;a&gt;&lt;&#x2F;code&gt; to prevent sensitive data like buffers containing passwords from being swapped to disk.&lt;&#x2F;li&gt;
&lt;li&gt;Then it decides what to become.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;greetd-session-worker&quot;&gt;The greetd session worker&lt;&#x2F;h2&gt;
&lt;p&gt;The simpler part of greetd is the actual session worker, so I&#x27;ll start with that …&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-code&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;git.sr.ht&amp;#x2F;~kennylevinsen&amp;#x2F;greetd&amp;#x2F;tree&amp;#x2F;3a32863bc70392449eb10c649cc2917a2ff06ad0&amp;#x2F;item&amp;#x2F;greetd&amp;#x2F;src&amp;#x2F;session&amp;#x2F;worker.rs#L84&quot; &gt;
		The relevant &lt;code&gt;worker()&lt;&#x2F;code&gt; function in &lt;code&gt;greetd&#x2F;src&#x2F;session&#x2F;worker.rs&lt;&#x2F;code&gt;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;The session worker is started by starting greetd with the &lt;code&gt;--session-worker&lt;&#x2F;code&gt; or simply &lt;code&gt;-w&lt;&#x2F;code&gt; flag and passing the number of a file descriptor that is supposed to be a Unix socket for exchanging commands and feedback.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;greetd-session-worker-function&quot;&gt;What it does&lt;&#x2F;h3&gt;
&lt;p&gt;The session worker itself is one strand of pretty straightforward code that feels a bit like a shellscript that asks for input and aborts with an error message on failure.&lt;&#x2F;p&gt;
&lt;p&gt;It does a few things:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Wait for an &lt;code&gt;InitiateLogin&lt;&#x2F;code&gt; or &lt;code&gt;Cancel&lt;&#x2F;code&gt; message.&lt;&#x2F;li&gt;
&lt;li&gt;Setup a conversation with &lt;abbr title=&quot;Pluggable Authentication Modules&quot;&gt;PAM&lt;&#x2F;abbr&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Let a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~kennylevinsen&#x2F;greetd&#x2F;tree&#x2F;3a32863bc70392449eb10c649cc2917a2ff06ad0&#x2F;item&#x2F;greetd&#x2F;src&#x2F;session&#x2F;conv.rs&quot;&gt;PAM adaptor implemented in a separate file&lt;&#x2F;a&gt; do the authentication conversation.&lt;&#x2F;li&gt;
&lt;li&gt;Do &lt;code&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;pam_acct_mgmt&quot;&gt;pam_acct_mgmt()&lt;&#x2F;a&gt;&lt;&#x2F;code&gt; and &lt;code&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;pam_setcred&quot;&gt;pam_setcred()&lt;&#x2F;a&gt;&lt;&#x2F;code&gt; and send a &lt;code&gt;Success&lt;&#x2F;code&gt; message.&lt;&#x2F;li&gt;
&lt;li&gt;Wait for a &lt;code&gt;Args&lt;&#x2F;code&gt; (sets the command for ) or &lt;code&gt;Cancel&lt;&#x2F;code&gt; message and answer &lt;code&gt;Success&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Wait for a &lt;code&gt;Start&lt;&#x2F;code&gt; or &lt;code&gt;Cancel&lt;&#x2F;code&gt; message.&lt;&#x2F;li&gt;
&lt;li&gt;Fetch user information from PAM.&lt;&#x2F;li&gt;
&lt;li&gt;Set a session id using &lt;code&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;setsid.2&quot;&gt;setsid()&lt;&#x2F;a&gt;&lt;&#x2F;code&gt; to make the process the root of a new session.&lt;&#x2F;li&gt;
&lt;li&gt;If we have a specific virtual-terminal configured.
&lt;ol&gt;
&lt;li&gt;Tell PAM that we want to run in that tty.&lt;&#x2F;li&gt;
&lt;li&gt;Set the &lt;code&gt;XDG_VTNR&lt;&#x2F;code&gt; environment variable via PAM.&lt;&#x2F;li&gt;
&lt;li&gt;Open the terminal file, set the tty to text mode, clear it and switch to it if necessary.&lt;&#x2F;li&gt;
&lt;li&gt;Use the greetd terminal abstraction to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~kennylevinsen&#x2F;greetd&#x2F;tree&#x2F;3a32863bc70392449eb10c649cc2917a2ff06ad0&#x2F;item&#x2F;greetd&#x2F;src&#x2F;terminal&#x2F;mod.rs#L204&quot;&gt;do the plumbing for the pipes&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~kennylevinsen&#x2F;greetd&#x2F;tree&#x2F;3a32863bc70392449eb10c649cc2917a2ff06ad0&#x2F;item&#x2F;greetd&#x2F;src&#x2F;terminal&#x2F;mod.rs#L229&quot;&gt;switch the controlling tty&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Set the &lt;abbr title=&quot;Present Working Directory&quot;&gt;pwd&lt;&#x2F;abbr&gt; for the new session, either to the users home or the filesystem root as fallback.&lt;&#x2F;li&gt;
&lt;li&gt;Set a whole bunch of environment variables for the session (including the &lt;code&gt;GREETD_SOCK&lt;&#x2F;code&gt; for the greeter, and the ones received with the &lt;code&gt;Args&lt;&#x2F;code&gt; message) using PAM.&lt;&#x2F;li&gt;
&lt;li&gt;Prepare the command to execute in the new session (shell + profile files + session command)&lt;&#x2F;li&gt;
&lt;li&gt;Fetch the environment variables for the new session from PAM.&lt;&#x2F;li&gt;
&lt;li&gt;Fork&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;After the fork the parent sends the &lt;abbr title=&quot;Process ID&quot;&gt;pid&lt;&#x2F;abbr&gt; to the the greetd server process and closes the socket, sets its &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;prctl.2#PR_SET_PDEATHSIG&quot;&gt;parent death signal&lt;&#x2F;a&gt; to &lt;code&gt;SIGTERM&lt;&#x2F;code&gt; and waits for the child thread to terminate before ending the PAM session.&lt;&#x2F;p&gt;
&lt;p&gt;The child process drops its privileges, also sets its &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;prctl.2#PR_SET_PDEATHSIG&quot;&gt;parent death signal&lt;&#x2F;a&gt; and then uses &lt;code&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;execve.2&quot;&gt;execve()&lt;&#x2F;a&gt;&lt;&#x2F;code&gt; to launch the prepared command with the environment provided by PAM.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note on Environment variables:&lt;&#x2F;b&gt; The variables received using the &lt;code&gt;Args&lt;&#x2F;code&gt; call get set before the ones coming from the greeter to avoid overriding some important variables. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~kennylevinsen&#x2F;greetd&#x2F;commit&#x2F;bc7e369225b628f0fb4863c9cfc4f48a3d632d41&quot;&gt;This behaviour was introduced&lt;&#x2F;a&gt; after &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~kennylevinsen&#x2F;greetd&#x2F;commit&#x2F;2f1810d1a246ee61c7f9334946eb8ed0ee40a80d&quot;&gt;making it possible for the greeter to set the environment variables (again)&lt;&#x2F;a&gt;.
&lt;b&gt;Personal opinion:&lt;&#x2F;b&gt; If you have a good reason for changing those you shouldn&#x27;t be doing that through the greeter anyway.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;greeter-protocol&quot;&gt;Messages between Session Worker and Server&lt;&#x2F;h2&gt;
&lt;p&gt;For later reference here are the messages that are exchanged between session worker and server via given socket using a JSON based protocol.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;parent-to-session-messages&quot;&gt;Parent to Session Messages&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;initiate-login-message&quot;&gt;&lt;code&gt;InitiateLogin&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Message sent to start the login process.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		string service
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The PAM service id so that PAM knows which configuration to apply
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		string class
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		ends up in the &lt;code&gt;XDG_SESSION_CLASS&lt;&#x2F;code&gt;, either &#x27;user&#x27; or &#x27;greeter&#x27;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		string user
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		the username to log in as
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		bool authenticate
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		set to &lt;code&gt;false&lt;&#x2F;code&gt; if we have an autologin session
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		TerminalMode tty
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The description of the terminal to attach to
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		bool source_profile
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Whether or not to read the profile files before executing the session command
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h4 id=&quot;pam-response-message&quot;&gt;&lt;code&gt;PamResponse&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;A response to a question from PAM.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		string? response
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The response, may be null
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h4 id=&quot;args-message&quot;&gt;&lt;code&gt;Args&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Sets the command to be executed for the session when &lt;code&gt;Start&lt;&#x2F;code&gt; is sent.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		string[] cmd
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		An array of arguments representing the command
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		string[] env
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		An array of environment variables to pass to the given command (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~kennylevinsen&#x2F;greetd&#x2F;commit&#x2F;2f1810d1a246ee61c7f9334946eb8ed0ee40a80d&quot;&gt;it was added (back) on 2022-08-13&lt;&#x2F;a&gt;)
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h4 id=&quot;start-message&quot;&gt;&lt;code&gt;Start&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Gives the final go after the session is full set up.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;cancel-message&quot;&gt;&lt;code&gt;Cancel&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Can be used at almost any step to cancel the session setup.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;session-to-parent-messages&quot;&gt;Session to Parent Messages&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;success-message&quot;&gt;&lt;code&gt;Success&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;What it says on the tin. Signals that a command has successfully finished.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;error-message&quot;&gt;&lt;code&gt;Error&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Signals that an error has been caught somewhere.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		Error error
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		A rusty error response, probably gets serialised to an error message
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h4 id=&quot;pam-message-message&quot;&gt;&lt;code&gt;PamMessage&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;A Question or other Output from PAM.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		AuthMsgType style
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		an enum telling what kind of message this is and what the expected response is, one of Visible, Secret, Info or Error
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		string msg
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		a string intended for the human trying to log in
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h4 id=&quot;final-child-pid-message&quot;&gt;&lt;code&gt;FinalChildPid&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Sends back the process id of the session child after the session was launched.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		uint pid
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		the process id
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;greetd-server&quot;&gt;The greetd server&lt;&#x2F;h2&gt;
&lt;p&gt;The server process is the one started by the init-system, it is responsible for launching the session workers and telling them what to do, it also houses the internal state machine that knows which session to launch next, it also does some &quot;managing&quot; for the processes in the session.&lt;&#x2F;p&gt;
&lt;p&gt;The server is made up of multiple components:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A base to orchestrate the startup and running sequence, also handles signals&lt;&#x2F;li&gt;
&lt;li&gt;A &lt;code&gt;Context&lt;&#x2F;code&gt; and and &lt;code&gt;ContextInner&lt;&#x2F;code&gt; to keep track of multiple sessions and scheduling&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Session&lt;&#x2F;code&gt; objects that launch the session workers and handle the communication&lt;&#x2F;li&gt;
&lt;li&gt;A function that does the communicating with the greeter&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;greetd-server-function&quot;&gt;What it does …&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;greetd-server-function-startup&quot;&gt;… on startup&lt;&#x2F;h4&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-code&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;git.sr.ht&amp;#x2F;~kennylevinsen&amp;#x2F;greetd&amp;#x2F;tree&amp;#x2F;3a32863bc70392449eb10c649cc2917a2ff06ad0&amp;#x2F;item&amp;#x2F;greetd&amp;#x2F;src&amp;#x2F;server.rs#L201&quot; &gt;
		The entry point is the &lt;code&gt;main()&lt;&#x2F;code&gt; function in the &lt;code&gt;greetd&#x2F;src&#x2F;server.rs&lt;&#x2F;code&gt; file
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;After the initialisation has decided that it will be a greetd server the main function also does a few things:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Determine the service name it will tell PAM based on which configurations for PAM are available. (warns when using the &lt;code&gt;login&lt;&#x2F;code&gt; fallback)&lt;&#x2F;li&gt;
&lt;li&gt;Get some info about the user configured for the &lt;code&gt;default_session&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Create the Unix Socket (&lt;code&gt;Listener&lt;&#x2F;code&gt;) for the greeter, set permissions and set the &lt;code&gt;GREETD_SOCK&lt;&#x2F;code&gt; environment variable.&lt;&#x2F;li&gt;
&lt;li&gt;Get information about the tty it is supposed to be on.
Returns an error told to wait for the terminal to become active it waits (&lt;code&gt;terminal.switch&lt;&#x2F;code&gt; set to &lt;code&gt;false&lt;&#x2F;code&gt; in config).&lt;&#x2F;li&gt;
&lt;li&gt;Create a &lt;code&gt;Context&lt;&#x2F;code&gt; to do the housekeeping for us.&lt;&#x2F;li&gt;
&lt;li&gt;Start initial session or greeter for the default session depending on which one is configured. (Other state transitions after initial bootstrapping are handled by the &lt;code&gt;Context&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;Create the runfile to know the initial run apart form the others in terms of system restarts, not greetd restarts.&lt;&#x2F;li&gt;
&lt;li&gt;Loop to handle posix signals (&lt;code&gt;SIGALRM&lt;&#x2F;code&gt;, &lt;code&gt;SIGCHLD&lt;&#x2F;code&gt;, &lt;code&gt;SIGTERM&lt;&#x2F;code&gt; and &lt;code&gt;SIGINT&lt;&#x2F;code&gt;) as well as Incoming connections on the greeter socket.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h4 id=&quot;greetd-server-function-greeter&quot;&gt;… on an incoming connection from the greeter&lt;&#x2F;h4&gt;
&lt;p&gt;The connections are accepted using the main-loop (see previous section) the handler gets the bidirectional datagram stream and a copy of the &lt;code&gt;Context&lt;&#x2F;code&gt; structure (the inner context staying the same, this is how the information about the current global state is exchanged between possibly multiple connections).&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-code&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;git.sr.ht&amp;#x2F;~kennylevinsen&amp;#x2F;greetd&amp;#x2F;tree&amp;#x2F;3a32863bc70392449eb10c649cc2917a2ff06ad0&amp;#x2F;item&amp;#x2F;greetd&amp;#x2F;src&amp;#x2F;server.rs#L69&quot; &gt;
		The &lt;code&gt;client_handler()&lt;&#x2F;code&gt; in &lt;code&gt;greetd&#x2F;src&#x2F;server.rs&lt;&#x2F;code&gt; handles the communication
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Inside this function is a loop that reads the request and then decides which function to call on the &lt;code&gt;Context&lt;&#x2F;code&gt;, wraps up the response and sends it back.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		CreateSession
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Calls the &lt;code&gt;create_session()&lt;&#x2F;code&gt; function with the given username, if successful, responds with the first question
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		PostAuthMessageResponse
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Calls the &lt;code&gt;post_response()&lt;&#x2F;code&gt; function to answer the PAM question, fetches and returns the next question or the &lt;code&gt;Ready&lt;&#x2F;code&gt; response if that didn&#x27;t fail
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		StartSession
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Calls the &lt;code&gt;start()&lt;&#x2F;code&gt; function and returns the result
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		CancelSession
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Calls &lt;code&gt;cancel()&lt;&#x2F;code&gt; function and returns the result
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;There are two helper functions:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		client_get_question(context)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This function fetches the next question from the context using the &lt;code&gt;get_question()&lt;&#x2F;code&gt; method, if it returns a question it packs that into an object that can be serialised and sent back, if not it calls &lt;code&gt;wrap_result()&lt;&#x2F;code&gt; on the result and returns that, meaning automatic error handling or an automatic success message in case of an ok.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		wrap_result(result)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This function takes a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;result&#x2F;enum.Result.html&quot;&gt;Result&lt;&#x2F;a&gt; and spits out a spendable success or error message containing appropriate information about the error.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;greetd-context&quot;&gt;Context&lt;&#x2F;h2&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-code&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;git.sr.ht&amp;#x2F;~kennylevinsen&amp;#x2F;greetd&amp;#x2F;tree&amp;#x2F;3a32863bc70392449eb10c649cc2917a2ff06ad0&amp;#x2F;item&amp;#x2F;greetd&amp;#x2F;src&amp;#x2F;context.rs&quot; &gt;
		The &lt;code&gt;Context&lt;&#x2F;code&gt; mechanism is implemented in &lt;code&gt;greetd&#x2F;src&#x2F;context.rs&lt;&#x2F;code&gt;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;As already stated greetd manages its state using &lt;code&gt;Context&lt;&#x2F;code&gt; objects (no idea what the correct rust terminology is, but its a data-structure with a set of functions and quacks like an object). To be more precise it uses two context objects, the outer &lt;code&gt;Context&lt;&#x2F;code&gt; for configuration and connection state and the &lt;code&gt;ContextInner&lt;&#x2F;code&gt; for storing global state of a 3-stage session pipeline that is shared between all interested parts of greetd.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;greed-context-struct&quot;&gt;Datastructures&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;Context&lt;&#x2F;code&gt; object holds the following values:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;sync&#x2F;struct.RwLock.html&quot;&gt;RwLock&lt;&#x2F;a&gt;&amp;lt;ContextInner&amp;gt; inner
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The &lt;code&gt;ContextInner&lt;&#x2F;code&gt; object for global state wrapped in a lock that allows shared reading or exclusive writing
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		string greeter_bin
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The command for the greeter
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		string greeter_user
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The user the greeter runs as
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		string greeter_service
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The PAM service name for starting greeter sessions
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		string pam_service
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The PAM service name for starting normal sessions
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		TerminalMode term_mode
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The vt in which this context spawns sessions
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		bool source_profile
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The &lt;code&gt;source_profile&lt;&#x2F;code&gt; setting
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		string runfile
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The path of the runfile for persisting sate across possible greetd restarts
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;The &lt;code&gt;ContextInner&lt;&#x2F;code&gt; has the following fields:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		SessionChildSet? current
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Information about the currently running session
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		SessionSet? scheduled
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The session that will be started once the currently running session terminates
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		SessionSet? configuring
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Stage before &lt;code&gt;scheduled&lt;&#x2F;code&gt;, a session that isn&#x27;t ready to be launched just yet because the greeter is still answering the questions PAM has
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;The &lt;code&gt;SessionSet&lt;&#x2F;code&gt; here is a &lt;code&gt;Session&lt;&#x2F;code&gt; (representing a session that is being set up) with a timestamp attached. The &lt;code&gt;SessionChildSet&lt;&#x2F;code&gt; stores a &lt;code&gt;SessionChild&lt;&#x2F;code&gt; (representing a running session) and has an &lt;code&gt;is_greeter&lt;&#x2F;code&gt; flag in addition to the timestamp.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; There is a distinction between the &lt;code&gt;Session&lt;&#x2F;code&gt; and &lt;code&gt;SessionChild&lt;&#x2F;code&gt; object because a session  that is already running doesn&#x27;t care about all of the communications foo and is only interested in finding out if the session is still running and ending it if it isn&#x27;t supposed to be running anymore, the only shared value is the pid of the session worker.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;greetd-context-methods&quot;&gt;Methods&lt;&#x2F;h3&gt;
&lt;p&gt;The methods on the context seem to be made to make it easy to write down what should happen from the outside, also they are pretty well commented so you know how they are supposes to be hooked up.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-start-unauthenticated-session&quot;&gt;&lt;code&gt;start_unauthenticated_session()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Directly start an unauthenticated session, bypassing the normal scheduling.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This one translates to spawning a session worker and sending it all commands that should be needed for an autologin session and gives empty answers for any questions PAM asks. This is used for the &lt;code&gt;initial_session&lt;&#x2F;code&gt; and the sessions for the greeters. It returns the created &lt;code&gt;Session&lt;&#x2F;code&gt; as an object. Username, session-class, PAM-service and the command are given as arguments.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-start-greeter&quot;&gt;&lt;code&gt;start_greeter()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Directly start a greeter session, bypassing the normal scheduling.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;A small convenience wrapper that preconfigures the &lt;code&gt;start_unauthenticated_session()&lt;&#x2F;code&gt; with stored settings for a greeter session, uses the &lt;code&gt;greeter_service&lt;&#x2F;code&gt; as the PAM service. It also returns the created &lt;code&gt;Session&lt;&#x2F;code&gt; object.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-greet&quot;&gt;&lt;code&gt;greet()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Directly start a greeter session, bypassing the normal scheduling.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This one starts a greeter session and sets it as the &lt;code&gt;current&lt;&#x2F;code&gt; session in the inner context. It returns an error if that &lt;code&gt;current&lt;&#x2F;code&gt; session is already occupied.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-start-user-session&quot;&gt;&lt;code&gt;start_user_session()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Directly start an initial session, bypassing the normal scheduling.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This will use &lt;code&gt;start_unauthenticated_session()&lt;&#x2F;code&gt; to autologin a given user with the given command. (used to implement the &lt;code&gt;initial_session&lt;&#x2F;code&gt;)&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-create-session&quot;&gt;&lt;code&gt;create_session()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Create a new session for configuration.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Creates a session preconfigured with the Context settings and the given username and swaps it into &lt;code&gt;configuring&lt;&#x2F;code&gt;.
Also mitigates a race-condition by cancelling a swapped out session.&lt;&#x2F;p&gt;
&lt;p&gt;Returns an error if there is no &lt;code&gt;current&lt;&#x2F;code&gt; session or there already is a session &lt;code&gt;configuring&lt;&#x2F;code&gt; or &lt;code&gt;scheduled&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-cancel&quot;&gt;&lt;code&gt;cancel()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Cancel the session being configured.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h4 id=&quot;greetd-context-methods-get-question&quot;&gt;&lt;code&gt;get_question()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Retrieve a question from the session under configuration.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Uses the &lt;code&gt;get_state()&lt;&#x2F;code&gt; method on the &lt;code&gt;Session&lt;&#x2F;code&gt; and returns either an ok when there are no more questions (&lt;code&gt;Ready&lt;&#x2F;code&gt;) or the question that is currently open.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-post-response&quot;&gt;&lt;code&gt;post_response()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Answer a question to the session under configuration.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Calls the &lt;code&gt;post_response()&lt;&#x2F;code&gt; on the currently &lt;code&gt;configuring&lt;&#x2F;code&gt; &lt;code&gt;Session&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-start&quot;&gt;&lt;code&gt;start()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Schedule the session under configuration with the provided arguments.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Returns an error if the &lt;code&gt;configuring&lt;&#x2F;code&gt; session has not signalled that it is ready yet or isn&#x27;t present.&lt;&#x2F;p&gt;
&lt;p&gt;Sets the sessions command to the given value, and swaps it into the &lt;code&gt;scheduled&lt;&#x2F;code&gt; stage, a session that already was in &lt;code&gt;scheduled&lt;&#x2F;code&gt; will be cancelled.&lt;&#x2F;p&gt;
&lt;p&gt;Sets a timer to fire the &lt;code&gt;SIGALRM&lt;&#x2F;code&gt; signal in five seconds which will be received using the &lt;code&gt;alarm()&lt;&#x2F;code&gt; method.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-alarm&quot;&gt;&lt;code&gt;alarm()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Gets called by the main-loop when a &lt;code&gt;SIGALRM&lt;&#x2F;code&gt; (timer) arrives.&lt;&#x2F;p&gt;
&lt;p&gt;Is a noop if no session is scheduled.&lt;&#x2F;p&gt;
&lt;p&gt;If the current session is still running it will be sent a &lt;code&gt;SIGTERM&lt;&#x2F;code&gt; or &lt;code&gt;SIGKILL&lt;&#x2F;code&gt;, depending on how much time has elapsed since the session was scheduled. The &lt;code&gt;SIGALRM&lt;&#x2F;code&gt; will be set up to fire again in one second.&lt;&#x2F;p&gt;
&lt;p&gt;If the current session is cleared the &lt;code&gt;seduled&lt;&#x2F;code&gt; session will be sent a &lt;code&gt;Start&lt;&#x2F;code&gt; command and be turned into the &lt;code&gt;current&lt;&#x2F;code&gt; session.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-check-children&quot;&gt;&lt;code&gt;check_children()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Gets called by the main-loop when a &lt;code&gt;SIGCHLD&lt;&#x2F;code&gt; (a child process has terminated, usually) arrives.&lt;&#x2F;p&gt;
&lt;p&gt;This function calls the &lt;code&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;nix&#x2F;0.25.0&#x2F;nix&#x2F;sys&#x2F;wait&#x2F;fn.waitpid.html&quot;&gt;waitpid()&lt;&#x2F;a&gt;&lt;&#x2F;code&gt; function with the &lt;code&gt;NoHang&lt;&#x2F;code&gt; flag set to tell it that we don&#x27;t want to wait if there is nothing new it can tell us. It can react to a bunch of events:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;In case of a &lt;code&gt;StillAlive&lt;&#x2F;code&gt; or an error indicating that we currently don&#x27;t have any children the function exits.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;States indicating some kind of normal event but not matching as well as Interruptions get thrown away.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Something exited or was killed with a signal and the &lt;code&gt;current&lt;&#x2F;code&gt; session owns that pid:&lt;&#x2F;p&gt;
&lt;p&gt;If a session is &lt;code&gt;scheduled&lt;&#x2F;code&gt; we try to start it and make it the &lt;code&gt;current&lt;&#x2F;code&gt; session, if not and the session was our greeter return an error, in case of a normal session use &lt;code&gt;start_greeter()&lt;&#x2F;code&gt; directly and make it the &lt;code&gt;current&lt;&#x2F;code&gt; session.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;greetd-context-methods-terminate&quot;&gt;&lt;code&gt;terminate()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Notify the Context that we want to terminate. This should be called on SIGTERM.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Shuts down the &lt;code&gt;configuring&lt;&#x2F;code&gt;, &lt;code&gt;scheduled&lt;&#x2F;code&gt; and &lt;code&gt;current&lt;&#x2F;code&gt; session while holding the write lock to avoid race conditions and sessions being rescheduled.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;state-machine&quot;&gt;State Machine&lt;&#x2F;h3&gt;
&lt;p&gt;All these functions form the state machine that cycles between greeter and user sessions and makes sure the pipeline from &lt;code&gt;configuring&lt;&#x2F;code&gt; to &lt;code&gt;current&lt;&#x2F;code&gt; doesn&#x27;t get clogged up by anything misbehaving and that greetd cleans up after itself.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;greetd-session-interface&quot;&gt;The Session Interface&lt;&#x2F;h2&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-code&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;git.sr.ht&amp;#x2F;~kennylevinsen&amp;#x2F;greetd&amp;#x2F;tree&amp;#x2F;3a32863bc70392449eb10c649cc2917a2ff06ad0&amp;#x2F;item&amp;#x2F;greetd&amp;#x2F;src&amp;#x2F;session&amp;#x2F;interface.rs&quot; &gt;
		The session interface is implemented in &lt;code&gt;greetd&#x2F;src&#x2F;session&#x2F;interface.rs&lt;&#x2F;code&gt;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;To make the external session processes and the context talk to each other Greetd has the &lt;code&gt;Session&lt;&#x2F;code&gt; class. It wraps an entire session process and handles the communication. It also remembers the last incoming message as &lt;code&gt;last_msg&lt;&#x2F;code&gt; and the session processes &lt;code&gt;pid&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;However, after setting up the session (as noted earlier) the Communication channel becomes useless as we now only care about the sessions current running status detecting when it exits and ending it if it misbehaves. This is the reason a second object, the &lt;code&gt;SessionChild&lt;&#x2F;code&gt; exists which holds the session managers pid as &lt;code&gt;task&lt;&#x2F;code&gt; and the pid of the process in the session as &lt;code&gt;sub_task&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;greetd-session-methods&quot;&gt;Methods of &lt;code&gt;Session&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;greetd-session-methods-new-external&quot;&gt;&lt;code&gt;new_external()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Creates the &lt;code&gt;Session&lt;&#x2F;code&gt; object and spawns a session worker process with a datagram socket attached for communication.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-session-methods-initiate&quot;&gt;&lt;code&gt;initiate()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Send an &lt;code&gt;InitiateLogin&lt;&#x2F;code&gt; message to the session worker.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-session-methods-get-state&quot;&gt;&lt;code&gt;get_state()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Tries to receive a message from the session worker if there is no &lt;code&gt;last_msg&lt;&#x2F;code&gt; and stores it in &lt;code&gt;last_msg&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Then it returns a &lt;code&gt;SessionState&lt;&#x2F;code&gt; struct contain either a question from PAM or a &lt;code&gt;Ready&lt;&#x2F;code&gt; if PAM has no further questions. This is used in the &lt;code&gt;Context.get_question()&lt;&#x2F;code&gt; method.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-session-methods-cancel&quot;&gt;&lt;code&gt;cancel()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Sends a &lt;code&gt;Cancel&lt;&#x2F;code&gt; message and resets &lt;code&gt;last_msg&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-session-methods-post-response&quot;&gt;&lt;code&gt;post_response()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Send a response to an authentication question, or None to cancel the authentication attempt.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Sends a &lt;code&gt;PostAuthMessageResponse&lt;&#x2F;code&gt; message and resets &lt;code&gt;last_msg&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-session-methods-send-args&quot;&gt;&lt;code&gt;send_args()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Send the arguments that will be used to start the session.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~kennylevinsen&#x2F;greetd&#x2F;commit&#x2F;bc7e369225b628f0fb4863c9cfc4f48a3d632d41&quot;&gt;Since 2022-08-13&lt;&#x2F;a&gt; this also sends a list of environment variables to match the new &lt;code&gt;Args&lt;&#x2F;code&gt; message.&lt;&#x2F;p&gt;
&lt;p&gt;Also reads a message after that and returns whether the command was successful.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-session-methods-start&quot;&gt;&lt;code&gt;start()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Sends a &lt;code&gt;Start&lt;&#x2F;code&gt; request and waits either for an &lt;code&gt;Error&lt;&#x2F;code&gt; or a &lt;code&gt;FinalChildPid&lt;&#x2F;code&gt; answer while dismissing questions from PAM with empty answers.&lt;&#x2F;p&gt;
&lt;p&gt;After that it shouts down the socket and returns a &lt;code&gt;SessionChild&lt;&#x2F;code&gt; object as the &lt;code&gt;Session&lt;&#x2F;code&gt; object mainly made for session setup is no longer useful once the session is running.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;greetd-session-child-methods&quot;&gt;Methods of &lt;code&gt;SessionChild&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;greetd-session-child-methods-owns-pid&quot;&gt;&lt;code&gt;owns_pid()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Tests if the &lt;code&gt;task&lt;&#x2F;code&gt; equals the given process id. (Used for checking if the session terminated on a &lt;code&gt;SIGCHLD&lt;&#x2F;code&gt; signal in the &lt;code&gt;Context&lt;&#x2F;code&gt;)&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-session-child-methods-term&quot;&gt;&lt;code&gt;term()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Used to send the session process (&lt;code&gt;sub_task&lt;&#x2F;code&gt;) a &lt;code&gt;SIGTERM&lt;&#x2F;code&gt; to tell it that we want it shut down.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-session-child-methods-kill&quot;&gt;&lt;code&gt;kill()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Sends a &lt;code&gt;SIGKILL&lt;&#x2F;code&gt; to both, the session manager and the session process. Used by the &lt;code&gt;Context.alert()&lt;&#x2F;code&gt; method when a session takes too long to shut down.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;updates-to-this-page&quot;&gt;Updates to this Page&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;2022-09-11&quot;&gt;2022-09-11&lt;&#x2F;h3&gt;
&lt;p&gt;Added information about the commits adding back the environment variables and another round of spellchecking. May also have broken some links (I hope not too many).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2023-03-03&quot;&gt;2023-03-03&lt;&#x2F;h3&gt;
&lt;p&gt;Added Link to Kenny Levinsens blogpost.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;but-why&quot;&gt;But Why?&lt;&#x2F;h2&gt;
&lt;p&gt;The reason I created this writeup is to first of all have an understanding of what my login manager of choice does and also to have a reference when tinkering with it.
One of my plans with this includes making greetd capable of running multiple sessions simultaneously to allow user switching and to be able to reuse the greeter for the initial login for locking my screen.&lt;&#x2F;p&gt;
&lt;p&gt;In case you have found a mistake, have questions or just feedback in general …&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destime-contact&quot; href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;about&#x2F;me&#x2F;#contact&quot; &gt;
		… please contact me!
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Building a crappy cheap LightDM 1&#x2F;?</title>
        <published>2022-08-05T00:00:00+00:00</published>
        <updated>2022-08-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/cheap-lightdm-part-1/"/>
        <id>https://slatecave.net/blog/cheap-lightdm-part-1/</id>
        
        <summary type="text">First POC of a simple session switcher for building a feature-rich lockscreen</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/cheap-lightdm-part-1/">&lt;p&gt;This is a lightly edited mind dump, don&#x27;t expect a well written post here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-it-is-does-and-why-i-built-it&quot;&gt;What it is, does and why I built it&lt;&#x2F;h2&gt;
&lt;p&gt;The goal of this little project was to build a collection of tools that is able to switch between two sessions on a Linux system for the purpose of having a session for working and one for logging in and unlocking the screen, the way it is implemented is by changing &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Virtual_console&quot;&gt;virtual terminals&lt;&#x2F;a&gt; to the corresponding session when certain state-changes occur, much like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;LightDM&quot;&gt;LightDM&lt;&#x2F;a&gt; and the light-locker.&lt;&#x2F;p&gt;
&lt;p&gt;The reason I&#x27;m doing this is because I want to be able to replace my currently pretty barebones screenlockers with a rich screenlocker that is able to do more than just showing me an unlock form, mostly for my phone but also for my other machines.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-not-just-use-lightdm&quot;&gt;Why not just use LightDM?&lt;&#x2F;h3&gt;
&lt;p&gt;Maybe oversimplified answer: I have the opinion that LightDM isn&#x27;t great on mobile.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;implementation&quot;&gt;Implementation&lt;&#x2F;h2&gt;
&lt;p&gt;The core of this contraption are two instances of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sr.ht&#x2F;~kennylevinsen&#x2F;greetd&#x2F;&quot;&gt;greetd&lt;&#x2F;a&gt;, one for actually logging in and one for giving me a properly set up session to run my compositor in which could be replaced by an instance of tinydm on my phone. These two communicate by always running one of two scripts the locker and the unlocker, both grab the current vt with the &lt;code&gt;chvt&lt;&#x2F;code&gt; command, write to pidfiles and &lt;code&gt;kill&lt;&#x2F;code&gt; each other and run hooks on exit.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Please Note:&lt;&#x2F;strong&gt; For my current implementation the user that runs the logged in session needs passwordless sudo access to the &lt;code&gt;chvt&lt;&#x2F;code&gt; command, in a multiuser scenario this may not be the best idea.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;unlocking&quot;&gt;Unlocking&lt;&#x2F;h3&gt;
&lt;p&gt;Unlocking works by entering your credentials in the greeter, triggering the &lt;code&gt;unlock&lt;&#x2F;code&gt; script which sends a &lt;code&gt;SIGUSR1&lt;&#x2F;code&gt; to the locker process (using the pidfile), and then entering a sleep loop waiting for the next session lock.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;lock&lt;&#x2F;code&gt; script handles this &lt;code&gt;SIGUSR1&lt;&#x2F;code&gt; by killing the lockscreen (in my case swaylock) and using &lt;code&gt;chvt&lt;&#x2F;code&gt; to switch the currently active terminal to the one of the real-work-session.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;locking&quot;&gt;Locking&lt;&#x2F;h3&gt;
&lt;p&gt;Locking works almost exactly the reverse way as unlocking. The &lt;code&gt;lock&lt;&#x2F;code&gt; script starts the screenlocker, kills the unlocker, this time with an ordinary &lt;code&gt;SIGKILL&lt;&#x2F;code&gt; and starts waiting for being unlocked.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;unlocker&lt;&#x2F;code&gt; uses &lt;code&gt;chvt&lt;&#x2F;code&gt; to grab the vt with the greeter on it and exits telling greetd to start the greeter session for the next unlock.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;bootstrapping&quot;&gt;Bootstrapping&lt;&#x2F;h3&gt;
&lt;p&gt;To solve our little problem of how to bootstrap this thing there is also an &lt;code&gt;initial-session-lock&lt;&#x2F;code&gt;, a script that writes its pid to the lockers pidfile and only exits with a success code when it receives a &lt;code&gt;SIGUSR1&lt;&#x2F;code&gt; to act as a trigger for starting the session on the first unlock. For shutting down my current solution is to wrap this in a loop and kill the unlocker after sway exits.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;code&quot;&gt;Code&lt;&#x2F;h2&gt;
&lt;p&gt;Currently this is an experiment I&#x27;ve run on my personal dotfiles so the sources are not really in any central location yet and you probably don&#x27;t want to just copypasta my scripts.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;scripts&quot;&gt;Scripts&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;commit&#x2F;c961b5dc3ecd35f887ecb512dcfa33799cecb947&#x2F;.local&#x2F;bin&#x2F;lock&quot;&gt;My personal screen-&lt;code&gt;lock&lt;&#x2F;code&gt; script&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;commit&#x2F;c961b5dc3ecd35f887ecb512dcfa33799cecb947&#x2F;.local&#x2F;bin&#x2F;unlocker&quot;&gt;My &lt;code&gt;unlocker&lt;&#x2F;code&gt; script&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;commit&#x2F;c961b5dc3ecd35f887ecb512dcfa33799cecb947&#x2F;.local&#x2F;bin&#x2F;initial-session-lock&quot;&gt;My &lt;code&gt;initial-session-lock&lt;&#x2F;code&gt; script&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;greetd-configuration&quot;&gt;Greetd configuration&lt;&#x2F;h3&gt;
&lt;p&gt;As mentioned above I have two instances of greetd running, one that starts the unlocker on login and one that starts the actual session.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;A pretty standard greetd configuration with the &lt;code&gt;unlocker&lt;&#x2F;code&gt; script instead of a real session.&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;terminal&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; The VT to run the greeter on. Can be &amp;quot;next&amp;quot;, &amp;quot;current&amp;quot; or a number&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; designating the VT.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;vt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; 7&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; The default session, also known as the greeter.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;default_session&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Starting tuigreet here which starts the unlocker command directly to unlock the second session&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;command&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;tuigreet --cmd unlocker -r -t&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; The user to run the command as. The privileges this user must have depends&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; on the greeter. A graphical greeter may for example require the user to be&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; in the `video` group.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;user&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;_greeter&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;The one starting the actual session could be replaced by something like &lt;code&gt;tinydm&lt;&#x2F;code&gt; as I only care about the autostarting part and that the environment is properly set up so I don&#x27;t have to do it myself here.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;A greetd configuration that uses the `initial_session` mechanism to autostart a shell oneliner that starts our session.&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;terminal&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; The VT to run the greeter on. Can be &amp;quot;next&amp;quot;, &amp;quot;current&amp;quot; or a number&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; designating the VT.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;vt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; 8&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;general&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Don&amp;#39;t know if actually necessary but I don&amp;#39;t want to restart the session I&amp;#39;m writing this post in&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;runfile&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;run&#x2F;slatian-greetd-initial-session&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;initial_session&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; The command that wits for the initial unlock, if successful starts the session and returns the control to the lockscreen after the session ends before it starts over.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;command&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;sh -c &amp;#39;while true; do initial-session-lock &amp;amp;&amp;amp; dbus-run-session sway; kill \&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$(head -n 1 \&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;$XDG_RUNTIME_DIR&#x2F;unlocker.pid\&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)\&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;; done&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;user&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;slatian&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; default session omitted … nothing interesting there&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;sudo-configuration&quot;&gt;Sudo configuration&lt;&#x2F;h3&gt;
&lt;p&gt;The following snippet placed in a file in &lt;code&gt;&#x2F;etc&#x2F;sudoers.d&#x2F;&lt;&#x2F;code&gt; will give everyone in the &lt;code&gt;wheel&lt;&#x2F;code&gt; group (the group that has sudo access anyways, may also be called &lt;code&gt;sudo&lt;&#x2F;code&gt; on some systems) the permission to run &lt;code&gt;chvt&lt;&#x2F;code&gt; without having to enter a password.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;%wheel	ALL= NOPASSWD: &#x2F;usr&#x2F;bin&#x2F;chvt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;almost-eof&quot;&gt;Almost EOF …&lt;&#x2F;h2&gt;
&lt;p&gt;More or less obviously this is very hacky, has multiple issues and falls apart very easily, somewhere in the next few months I&#x27;ll try to find a cleaner way to implement this without relying on too much on shell scripting and firing signals at processes using pidfiles.&lt;&#x2F;p&gt;
&lt;p&gt;Until that happens I&#x27;ll continue to use &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;integrating-my-screenlocker-with-sxmo-part-2&#x2F;&quot;&gt;my sxmo screenlocker&lt;&#x2F;a&gt; on my phone because it mostly works well enough for what I need.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Tutorial: Smart ssh jumping</title>
        <published>2022-06-05T00:00:00+00:00</published>
        <updated>2022-06-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/smart-ssh-jump/"/>
        <id>https://slatecave.net/blog/smart-ssh-jump/</id>
        
        <summary type="text">SSH jump hosts are great! However, having to think about the current network is a task better done by the CPU</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/smart-ssh-jump/">&lt;p&gt;I&#x27;m assuming you know what an ssh jump host is, why you want it and how you configure it in your ssh &lt;code&gt;ssh_config&lt;&#x2F;code&gt; file.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-problem-i-m-trying-to-solve&quot;&gt;The problem I&#x27;m trying to solve&lt;&#x2F;h2&gt;
&lt;p&gt;SSH jumping (via &lt;code&gt;JumpHost&lt;&#x2F;code&gt;) is usually useful when you have a bastion host you have to log into to reach the network behind. In some environments you never reach this &quot;private&quot; network in any other way, but if the private network belongs to your home- or office and the bastion is for connections from the big scary intertubes you want to use it only when necessary, meaning we need some kind of conditional &lt;code&gt;ProxyJump&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;example-scenario&quot;&gt;Example scenario&lt;&#x2F;h2&gt;
&lt;p&gt;Lets assume we have the following two hosts:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		tesla
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This is our bastion host publicly reachable
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		hawking
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This is our private host we actually want to connect to
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;the-easy-workaround&quot;&gt;The easy workaround&lt;&#x2F;h2&gt;
&lt;p&gt;The easy solution would be to have multiple aliases and using your brain to make the decision by using the hostname when at home and the hostname-remote when elsewhere.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example ssh configuration with different aliases for local and remote connections:&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Host tesla&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	HostName ssh.example.org&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	User nicola&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	IdentityFile ~&#x2F;.ssh&#x2F;id_foo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	Port 22222&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Host hawking&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	User stephen&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	IdentityFile […]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Host hawking-remote&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	ProxyJump tesla&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	User stephen&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	IdentityFile […]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	HostName hawking&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	Port 22&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;Problem is that you have to type the remote alias every time you&#x27;re not at home. Also using git this way is pretty annoying (you have to set up multiple remotes and manually tell git which one to use).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;smart-solution&quot;&gt;Smart solution&lt;&#x2F;h2&gt;
&lt;p&gt;The smart solution would be that ssh somehow could figure out if it is &quot;at home&quot; and if not enable the proxy jump option. For that we have to answer the following two questions:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Are we at home?&lt;&#x2F;li&gt;
&lt;li&gt;How do we tell ssh to (not) use the ProxyJump?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;are-we-at-home&quot;&gt;Are we at home?&lt;&#x2F;h2&gt;
&lt;p&gt;To find out if we are at home there are several options:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Querying DNS and for some address that is different in our home network - Too complicated to set up and is a &lt;abbr title=&quot;Pain In The Backside&quot;&gt;PITA&lt;&#x2F;abbr&gt; with slow resolvers (ssh connections already take 3 seconds to establish on school WiFi 🙄)&lt;&#x2F;li&gt;
&lt;li&gt;Comparing the WiFi name - This is not an option for wired networks.&lt;&#x2F;li&gt;
&lt;li&gt;Manually - forget it I wanted to get rid of that&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;All of the above are not really desirable, so lets think of something else … One thing that is usually frowned upon is &lt;b&gt;fingerprinting&lt;&#x2F;b&gt;, however if we turn the concept around that your local machine fingerprints its environment all of the sudden it becomes a pretty good tool.&lt;&#x2F;p&gt;
&lt;p&gt;If the year is somewhere after 2020 you probably have IPv6 configured and if you are someone like me, you probably have your own local prefix which is a pretty unique identifier for a network which the router is nice enough to tell your device when it connects!&lt;&#x2F;p&gt;
&lt;p&gt;So the magic for finding out if you are at home becomes an &lt;code&gt;ip a&lt;&#x2F;code&gt; piped into a &lt;code&gt;grep -F &quot;$ip_address&quot;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For my purposes I wrote a little script called &lt;code&gt;has-ip-grep&lt;&#x2F;code&gt; that is a simple shell pipe returning success if a given &quot;fingerprint&quot; matches and fails if it does not.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;The &lt;code&gt;has-ip-grep&lt;&#x2F;code&gt; script which tests if the first argument given to it matches the IP-address output part of an &lt;code&gt;ip a&lt;&#x2F;code&gt; command.&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#!&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;&#x2F;bin&#x2F;sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function z-builtin&quot;&gt;set&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;e&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ip&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; awk&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;inet&#x2F; { print $2 }&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; grep&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;F&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;Explanation: &lt;code&gt;ip a&lt;&#x2F;code&gt; prefixes the addresses assigned to interfaces with &lt;code&gt;inet&lt;&#x2F;code&gt; or &lt;code&gt;inet6&lt;&#x2F;code&gt; the awk command therefore looks for lines containing the string &lt;code&gt;inet&lt;&#x2F;code&gt; and if it finds one outputs the second column which is the IP-address. The output gets piped into a silent grep command that is told to look for instances of the first script argument if that&#x27;s not the case. The &lt;code&gt;set -e&lt;&#x2F;code&gt; makes sure the script returns an error code if that match fails.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;examples&quot;&gt;Examples&lt;&#x2F;h3&gt;
&lt;p&gt;In the first example we try to match against our local IPv4 loopback interface which should always return a success, which works great for a quick test!&lt;&#x2F;p&gt;
&lt;figure&gt;
	
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;has-ip-grep&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 127.0.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	&lt;figcaption&gt;&lt;b&gt;Example 1:&lt;&#x2F;b&gt; Succeeds because we have the IPv4-loopback-address assigned to our &lt;code&gt;lo&lt;&#x2F;code&gt; interface.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The second example show matching against the prefix usually used by a popular series of home routers made by a German company, this is also great for testing but it will very likely not be enough to identify your home network because your neighbours and your friend&#x27;s network probably have the same fingerprint.&lt;&#x2F;p&gt;
&lt;figure&gt;
	
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;has-ip-grep&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 192.168.178&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	&lt;figcaption&gt;&lt;b&gt;Example 2:&lt;&#x2F;b&gt; Usually succeeds if you are connected to a popular series of German home routers.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Other problems are that because we match substrings here this will only be convenient to match against ranges that align nicely with the decimal notation and that we get false positives, for example if someone assigns us a &lt;code&gt;10.192.168.178&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We can reduce the probability of false positives by matching against our IPv6-address, replace the &lt;code&gt;fd45:6789:abcd:&lt;&#x2F;code&gt; in the third Example with your own local IPv6-prefix. I don&#x27;t recommend you rely your global unicast prefix as it is usually assigned by your &lt;abbr title=&quot;Internet Service Provider&quot;&gt;ISP&lt;&#x2F;abbr&gt; and may change behind your back.&lt;&#x2F;p&gt;
&lt;figure&gt;
	
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;has-ip-grep&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; fd45:6789:abcd:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	&lt;figcaption&gt;&lt;b&gt;Example 3:&lt;&#x2F;b&gt; Will succeed in your home network and maybe some other random network half around the globe you will never know exists.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;So now we have a pretty good (works most of the time) way to know if we are at home lets put it to use!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conditional-proxyjump&quot;&gt;Conditional ProxyJump&lt;&#x2F;h2&gt;
&lt;p&gt;Luckily ssh has exactly what we need to combine it with our command we just created.&lt;&#x2F;p&gt;
&lt;p&gt;You are probably familiar with the &lt;code&gt;Host&lt;&#x2F;code&gt; keyword in your ssh configuration, turns out if you have a decently modern version of ssh there is also a &lt;code&gt;Match&lt;&#x2F;code&gt; keyword which supports some arguments.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-text&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;man.voidlinux.org&amp;#x2F;ssh_config#Match&quot; &gt;
		See the manual: &lt;code&gt;man 5 ssh_config&lt;&#x2F;code&gt;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;With that knowledge you can match against the target host (if you don&#x27;t you&#x27;ll run into funny recursion …), the network fingerprint of your choice and arrive at a configuration that can decide on the proxy without having to query your brain.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example ssh configuration which automagically knows how to connect.&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Host tesla&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	HostName ssh.example.org&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	User nicola&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	IdentityFile ~&#x2F;.ssh&#x2F;id_foo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	Port 22222&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Host hawking&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	User stephen&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	IdentityFile […]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Match Host hawking !Exec &amp;quot;has-ip-grep &amp;#39;192.168.42&amp;#39; &amp;amp;&amp;amp; has-ip-grep &amp;#39;fd42:f00b:aba2:&amp;#39;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	ProxyJump tesla&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	HostName hawking&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	Port 22&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;explanation&quot;&gt;Explanation&lt;&#x2F;h3&gt;
&lt;p&gt;The configuration for tesla stays the same (assuming we only want to connect to it from the public side). The configuration for the local connection to hawking can stay too, the &lt;code&gt;Host hawking-remote&lt;&#x2F;code&gt; directive was replaced with a &lt;code&gt;Match&lt;&#x2F;code&gt; that tests if we want to connect to hawking and then executes our shell command.&lt;&#x2F;p&gt;
&lt;p&gt;In this case we check for two IP-addresses that we expect to get assigned in our private network, As I&#x27;m running dual stack it won&#x27;t hurt to check against the IPv4-address too.&lt;&#x2F;p&gt;
&lt;p&gt;These two commands are combined with some shell syntax and quoted using double-quotes because that&#x27;s how &lt;code&gt;ssh_config&lt;&#x2F;code&gt; works. The Exec is prefixed with an exclamation mark because we want to match if we are NOT at home. The part after that is the same as with the remote alias except for the User and IdentityFile because we now the &lt;code&gt;Host hawking&lt;&#x2F;code&gt; always matches and we don&#x27;t have to duplicate them anymore.&lt;&#x2F;p&gt;
&lt;p&gt;I can imagine it being a lot of fun configuring several Networks this way 🙃.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;security&quot;&gt;Security&lt;&#x2F;h2&gt;
&lt;p&gt;This is by no means any kind of security measure, if the network you are connected to matches the fingerprint of your private network this will make the wrong decision to connect directly! However if you are running such a setup you should have the certificate fingerprints of your real servers already in your known_hosts file and then just be smart enough to not accept any new certificates over untrusted connections.&lt;&#x2F;p&gt;
&lt;p&gt;If that finger print matches when you are not in your network (assuming you randomly chose an IPv6-prefix) you should get the hell out of there!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;debugging&quot;&gt;Debugging&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;ssh -v&lt;&#x2F;code&gt; is your friend!&lt;&#x2F;p&gt;
&lt;p&gt;If ssh seems to do nothing except burning CPU cycles you might have ran into some recursion problem, Make sure that your &lt;code&gt;Match&lt;&#x2F;code&gt; keywords always match on some host and that your proxy connections aren&#x27;t jumping in circles!&lt;&#x2F;p&gt;
&lt;p&gt;Also some pits I fell into (all of them are documented in the &lt;code&gt;ssh_config&lt;&#x2F;code&gt; man-page):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;If your forget the &lt;code&gt;Host&lt;&#x2F;code&gt; rule on a &lt;code&gt;Match&lt;&#x2F;code&gt; you&#x27;ll get some recursion going.&lt;&#x2F;li&gt;
&lt;li&gt;A &lt;code&gt;Host&lt;&#x2F;code&gt; rule ends when a &lt;code&gt;Match&lt;&#x2F;code&gt; rule starts.&lt;&#x2F;li&gt;
&lt;li&gt;When you assign a variable multiple times only the value from the first assignment is taken (especially important when you want to rewrite the host for short aliases and later to localhost when redirecting into a tunnel which won&#x27;t work).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;that-s-it&quot;&gt;That&#x27;s it!&lt;&#x2F;h2&gt;
&lt;p&gt;Have fun connecting to your servers without having to think about changing names! If you spotted a mistake somewhere or have other feedback feel free to contact me!&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Integrating swaylock-mobile with Sxmo 2&#x2F;?</title>
        <published>2022-05-07T00:00:00+00:00</published>
        <updated>2022-05-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/integrating-my-screenlocker-with-sxmo-part-2/"/>
        <id>https://slatecave.net/blog/integrating-my-screenlocker-with-sxmo-part-2/</id>
        
        <summary type="text">First attempt to itegrate my screenlcker with sxmo (quirky and barebones, but as success)</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/integrating-my-screenlocker-with-sxmo-part-2/">&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lists.sr.ht&#x2F;~mil&#x2F;sxmo-announce&#x2F;%3C3FV34P1BUD8B6.34I97X4SKLKHH%40yellow-orcess.my.domain%3E&quot;&gt;Sxmo 1.9.0 was released last weekend&lt;&#x2F;a&gt; for PostmarketOS edge, which means … I have continued my work on the screenlocker as promised. In this part I&#x27;ll mostly explain what needed to be done (and why) for the screenlocker to work.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;related-resources&quot;&gt;Related Resources&lt;&#x2F;h2&gt;

	

&lt;ul class=&quot;link-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;integrating-my-screenlocker-with-sxmo-part-1&#x2F;&quot;&gt;Part 1 documenting how the screenlocking part of sxmo works under the hood&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;experiment.sxmo-screelocker&quot;&gt;Screenlocker Integration Dotfiles&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;swaylock-mobile&quot;&gt;Swaylock mobile, my screenlocker&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;what-my-current-solution-can-and-can-t-do&quot;&gt;What my current solution can and can&#x27;t do&lt;&#x2F;h2&gt;
&lt;p&gt;Current sate of swaylock-mobile is, that it&#x27;s a pretty standard swaylock that fits on a touchscreen (even when rotated!) with a keypad for entering numeric pins without a real keyboard, that is enough to integrate it into Sxmo and get some additional security for my phone in daily use.&lt;&#x2F;p&gt;
&lt;p&gt;So the integration currently handles:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Locking the screen, before turning it off&lt;&#x2F;li&gt;
&lt;li&gt;Proximity-lock for both locked and unlocked states&lt;&#x2F;li&gt;
&lt;li&gt;State-management for the idle locker and lisgd&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;More important, what does it NOT handle:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;X integration, I have not tested it with the dwm version&lt;&#x2F;li&gt;
&lt;li&gt;Graceful degradation, fallback in case there is no screenlocker doesn&#x27;t work&lt;&#x2F;li&gt;
&lt;li&gt;Maybe I have broken some suspend inhibitors … I did&lt;&#x2F;li&gt;
&lt;li&gt;Remaining slightly broken states&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;changes&quot;&gt;Changes&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;new-wrapper-script-run-screenlocker&quot;&gt;New Wrapper script run-screenlocker&lt;&#x2F;h3&gt;
&lt;p&gt;This is a script that runs swaylock-mobile until it exits with a success status-code (the warning that you should make sure it is restarted in case of a crash still applies) and after that calls the unlock hook. This script gets called from the lock hook and should be the only thing that can call the unlock hook. (one exception would be the fallback …)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hooks-that-were-modified&quot;&gt;Hooks that were modified&lt;&#x2F;h3&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		sxmo_hook_inputhandler.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The Powerbutton now only toggles screenon and screenoff and calls the lock hook before off, also the corner-swipe locks the screen and turns it off.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		sxmo_hook_lock.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Just does, what it says on the tin: locking, also makes sure it only locks when called from an unlocked sate, also invokes the new idlelocker hook.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		sxmo_hook_proximitylock.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The proximitylock now calls the screenon hook instead of the unlock hook, also disabled the can_suspend locks.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		sxmo_hook_screenoff.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Now uses $SXMO_STATE.screen for storing screen state.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		sxmo_hook_unlock.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Now uses the idlelocker hook for setting the screenlocker.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h3 id=&quot;hooks-that-were-added&quot;&gt;Hooks that were added&lt;&#x2F;h3&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		sxmo_hook_idlelocker.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Sets the idlelocker for screenon scenarios, depending on the SXMO_STATE.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		sxmo_hook_screenon.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Gets called for turning the screen on (and some screen related services).
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h3 id=&quot;new-state-file&quot;&gt;New state-file&lt;&#x2F;h3&gt;
&lt;p&gt;There is a new file to keep track of things, previously $SXMO_STATE was used for storing three states sxmo could be in: screenoff, locked and unlocked.&lt;&#x2F;p&gt;
&lt;p&gt;These states would still be sufficient, but the model with a state for the lock (locked and unlocked) and a state for the screen (on and off), which allows me to simplify the previously error prone and complex state machine into two state-machines which just turn things on and off without having to make assumptions about the screen-state or the lock status.&lt;&#x2F;p&gt;
&lt;p&gt;For that I needed a second state-file though which I called $SXMO_STATE.screen for now (clean solution would be to introduce a $SXMO_SCREEN_STATE and $SXMO_LOCK_STATE).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;new-environment-variable-sxmo-reason&quot;&gt;New environment variable: SXMO_REASON&lt;&#x2F;h3&gt;
&lt;p&gt;Before rewriting the state-machines, I tried to work with the old states and figuring out what to do depending on where the call came from, which turned out to be a horrible buggy mess, but its the reason SXMO_REASON was introduced.&lt;&#x2F;p&gt;
&lt;p&gt;Why is it still there you ask: Because if the screen is locked and the proximity-lock triggers in most of the cases you don&#x27;t want the screen to turn on again by just uncovering the proximity sensor… the screenon hook script currently uses it to cancel if this variable is set to proximity-lock and the lockscreen is active. (Someone could make that a preference, however it won&#x27;t work when the system goes to sleep)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;additional-sway-configuration&quot;&gt;Additional sway configuration&lt;&#x2F;h3&gt;
&lt;p&gt;One thing that isn&#x27;t immediately obvious is that you want to turn your screen back on again and that it doesn&#x27;t work with the default configuration, the reason being that sway detects when a proper screenlocker is active it only allows keyboard bindings (the power-button behaves as if it was the XF86PowerOff key on a keyboard) when they are bound with a --locked switch, so I put a bindsym into my sway config file, the cleaner way would have been to update sxmo_swayinitconf.sh (I did it that way because I hope this way it will break less often than modifying the swayinitconf script).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fixing-the-remaining-todos&quot;&gt;Fixing the remaining TODOs&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;it-doesn-t-work-with-x&quot;&gt;It doesn&#x27;t work with X&lt;&#x2F;h3&gt;
&lt;p&gt;I (or someone who knows sxmo) would have to make sure all modifications are also applied to the dwm version (not sure about how to solve the power-button problem though)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;graceful-degradation-in-case-of-missing-disabled-screenlocker&quot;&gt;Graceful degradation in case of missing&#x2F;disabled screenlocker&lt;&#x2F;h3&gt;
&lt;p&gt;My approach would be a dummy screenlocker, that creates a file somewhere and then watches it for close_writes with inotifywait (and deletes it after), the handler for the power-button in the file exists + locked + screen on would touch that file, which would trigger an unlock.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;broken-suspend-inhibitors&quot;&gt;Broken suspend inhibitors&lt;&#x2F;h3&gt;
&lt;p&gt;The proximity-lock hook by default locks the can_suspend mutex, I leave it on for the lock-screen which blocks the suspend script from doing its job, that&#x27;s not battery friendly … Lets just bypass that by disabling the lock and hope it won&#x27;t break.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;slightly-broken-states&quot;&gt;Slightly broken states&lt;&#x2F;h3&gt;
&lt;p&gt;There are two scenarios I know of where the state handling is slightly broken, both involve the proximity-lock:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;When unlocked + proximity-lock active (sensor covered) + power button the screen turns on for a second and then goes to lock + screenoff. I currently don&#x27;t know why exactly this happens as the power-button should check against the screen-state and toggle it once, turning it on …&lt;&#x2F;li&gt;
&lt;li&gt;When locked + proximity-lock active + power button the screen turns on (like I want it when unlocked, ironically) and stays on for the 8 seconds timeout when not touched, this can be explained by how the proximity-lock currently triggers on changes (which is very good, because it saves power!)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Integrating swaylock-mobile with Sxmo 1&#x2F;?</title>
        <published>2022-03-18T00:00:00+00:00</published>
        <updated>2022-03-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/integrating-my-screenlocker-with-sxmo-part-1/"/>
        <id>https://slatecave.net/blog/integrating-my-screenlocker-with-sxmo-part-1/</id>
        
        <summary type="text">Finding out how sxmo curerently handles screenlocking and suspending (pre sxmo 1.9.0)</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/integrating-my-screenlocker-with-sxmo-part-1/">&lt;p&gt;I have been trying to introduce my touchscreen based screenlocker to Sxmo for over a week now. Turns out it is not as easy as I thought.
In this part of the sersies I&#x27;ll try to describe what sxmo does for screenlocking and what changes with the 1.9.0 release. The actual implementation will be discussed in part 2.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-i-have-tried-so-far&quot;&gt;What I have tried so far&lt;&#x2F;h2&gt;
&lt;p&gt;My first approach was to try out the desktop screensaver, the problem was, that it tried to interact with the newer version of Sxmo, so I reverted to messing with the default screenlock hook.&lt;&#x2F;p&gt;
&lt;p&gt;This at first seemed simple, just insert the screenlocker after the service starting and stopping foo, lock my phone, the screenlocker comes up as expected, I try to enter my pin and … nothing, I forgot that Sxmo turns off the touch input for the locked mode, so I&#x27;m not able to enter my pin on the touchscreen. Ok easy fix turn the touch back on after starting the screenlocker … and yes I&#x27;m successfully able to unlock my phone, but for some reason the screenlocker comes up very soon and after coming Up my phone won&#x27;t go to sleep …&lt;&#x2F;p&gt;
&lt;p&gt;The reason turns out to be that the sxmo_screenlock.sh script figures out the current state by looking at what it turned off. That means, I broke that by messing with the touchscreen state, which brings us to the present.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;where-i-m-now&quot;&gt;Where I&#x27;m now&lt;&#x2F;h2&gt;
&lt;p&gt;This is the &quot;legacy&quot; script that still runs on my phone which will be removed when Sxmo updates to 1.9.0. See the next section for the new solution.&lt;&#x2F;p&gt;
&lt;p&gt;This script seems to be the pint where everything related to locking and sleeping goes through.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;setting-state&quot;&gt;Setting state&lt;&#x2F;h3&gt;
&lt;p&gt;It defines the following functions for setting state:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		lock
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Turns touch input off
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		unlock
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Turns both touch input and the screen back on
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		off
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Turns the screen off
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		crust
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Sends the phone to deep sleep and determines why it woke up
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;I probably want to to add some semantics here to find out why the locking&#x2F;screenoff happened and launch my screenlocker if the reason is an idle session or a power button event.&lt;&#x2F;p&gt;
&lt;p&gt;These functions get called from a few places:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;&quot;&gt;

	
		&lt;dt&gt;
		core&#x2F;sxmo_inputhandler.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Has a lock_screen_action() function for when the screen is locked to tun it off, into locked and back into unlocked.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Also calls the lock function when you swipe away from the bottom left corner.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		core&#x2F;sxmo_screenlock_deeper.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Similar to the the lock_screen_action() above but is intended for being called after a few seconds of idle time and it won&#x27;t move to an unlocked state.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		core&#x2F;sxmo_proximitylock.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This script periodically checks if the distance sensor is covered and then makes the decision to call the unlock or the off function, also based on the state of the screenlocker.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		core&#x2F;sxmo_rtcwake.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This script seems to be called by nothing … it calls the crust function on sxmo_screenlock.sh
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		default_hooks&#x2F;contextmenu
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Calls lock, off and crust with the intention of explicitly locking the screen.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		notifications&#x2F;sxmo_notificationwrite.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Calls the lock function if the screen is off, probably with the intention to make the screen light up when there is a notification.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;Idea from wiring that: start the screenlocker as a sxmo_service from a blocking-hook and make the unlock function refuse to work as long as it is running, execute the unlock when it returns, if no lockscreen hook is specified, fallback to turning off the touchscreen.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;querying-state&quot;&gt;Querying state&lt;&#x2F;h3&gt;
&lt;p&gt;These states from above can also be queried with the getCurState function, it returns:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		unlock
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		When unlocked, this is returned if the touchscreen is on
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		lock
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		When locked with the screen on, this is returned if touch is off but the screen is on
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		off
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		When locked with screen off, this is returned when both touch and screen are off
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;This will need some modifications as I&#x27;m breaking the assumption that touch state equals locked state.&lt;&#x2F;p&gt;
&lt;p&gt;Quite a few scripts rely on this one:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		notifications&#x2F;sxmo_notificationwrite.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Tests for the off state to turn he screen on to get attention.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		core&#x2F;sxmo_inputhandler.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Uses this to cycle states.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		core&#x2F;sxmo_screenlock_deeper.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Uses this to determine the next deeper lock&#x2F;sleep state.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		core&#x2F;sxmo_proximitylock.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		(Ab)uses this to avoid unnecessary calls to unlock and off. But is not really context aware. It probably gets stopped elsewhere.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		core&#x2F;sxmo_rtcwake.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Cronjob, Only goes to crust if not in an unlocked state. (It has other factor too, but not being unlocked is a requirement)
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;Looking at this I&#x27;m not really willing to break this function, maybe some kind of soft&#x2F;hard lock mechanism for just turning off touch and having a proper lockscreen up would be useful.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-new-screenlock-suspend-mechanism&quot;&gt;The new screenlock &#x2F; suspend mechanism&lt;&#x2F;h2&gt;
&lt;p&gt;To understand the new mechanism you first have to understand sxmo_mutex.sh. (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~mil&#x2F;sxmo-utils&#x2F;commit&#x2F;ec6d08e91b94fc725733f65c7dbf313feb2a35cd#scripts&#x2F;core&#x2F;sxmo_mutex.sh&quot;&gt;It is documented in it&#x27;s first commit.&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;Diff from previous, old mechanism:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The is_idle and can_suspend hooks were replaced by mutex locks called from various sources.&lt;&#x2F;li&gt;
&lt;li&gt;The crust part of sxmo_screenlock.sh was replaced by sxmo_suspend.sh and sxmo_hook_suspend.sh.&lt;&#x2F;li&gt;
&lt;li&gt;The hacky detect locked state approach was moved to a much cleaner have a file which holds the state approach.&lt;&#x2F;li&gt;
&lt;li&gt;The rest of sxmo_screenlock.sh was replaces by hook scripts which makes my journey much easier.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;suspending&quot;&gt;Suspending&lt;&#x2F;h3&gt;
&lt;p&gt;This suspend part consists of a few files:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		default_hooks&#x2F;sxmo_hook_suspend.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Does the actual suspending.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		default_hooks&#x2F;sxmo_hook_mnc.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Figures out how long it is until the next Chronjob with sxmo_rtcwake in its name is scheduled.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		default_hooks&#x2F;sxmo_hook_presuspend.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Called before suspending, does cleanup like pausing media and turning off screen.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		default_hooks&#x2F;sxmo_hook_postwake.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Called after waking up with the reason for the wakeup as the first argument.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		core&#x2F;sxmo_suspend.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		When called fires off the whole suspend hook firework, goes too sleep, wakes up again and does a bit of housekeeping.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		ore&#x2F;sxmo_rtcwake.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Cronjob wrapper (I assume) that does some housekeeping with locks to make sure the cronjobs don&#x27;t get interrupted.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;The whole thing is triggered from the screenoff hook which starts a sxmo_periodically which runs sxmo_hook_check_state_mutexes.sh and if there is no good reason to stay awake goes to sleep using sxmo_suspend.sh with a sxmo_mutex.sh and a holdexec. (At least for touchscreens with three buttons which is the category my pinephone falls into)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;locking&quot;&gt;Locking&lt;&#x2F;h3&gt;
&lt;p&gt;Sxmo still has the three states, but they were moved to their own hooks.&lt;&#x2F;p&gt;
&lt;p&gt;This means that …&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The unlock script is now responsible for calling sxmo_hook_lock.sh&lt;&#x2F;li&gt;
&lt;li&gt;The lock script is responsible for turning the screen off&lt;&#x2F;li&gt;
&lt;li&gt;The screenoff script is responsible for going to deep sleep&lt;&#x2F;li&gt;
&lt;li&gt;Unlocking still happens through the lockstate cycling in the inputhandler&lt;&#x2F;li&gt;
&lt;li&gt;The unlock script is called by the start hook&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The proximitylock script calls the screenoff hook to lock and the unlock hook, so a screenlocker can actually be placed in the lock hook without having to worry about the proximitylock interfering, the postwake hook would have to unlock the touchscreen from the screenoff hook, which it already does and the lock hook shouldn&#x27;t turn off the touchscreen (that&#x27;s a beautiful hook fallback), the only &quot;problem&quot; with this approach would be that the state would say unlocked while the lockscreen is up. (interactive would be a better identifier)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;to-be-continued&quot;&gt;To be continued&lt;&#x2F;h2&gt;
&lt;p&gt;Note 2022-05-07: there was a previous version of this article that said: this little diff will do it, just use the lock hook … Nope, that won&#x27;t work …&lt;&#x2F;p&gt;
&lt;p&gt;Most of the above is still true for the released version (good enough as a little guide for myself at least).&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-action-next&quot; href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;integrating-my-screenlocker-with-sxmo-part-2&#x2F;&quot; &gt;
		For the actual implementation see part 2
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
</content>
    </entry>
</feed>

