<?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</title>
    <subtitle>Slatians hideout</subtitle>
    <link rel="self" type="application/atom+xml" href="https://slatecave.net/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://slatecave.net"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-05-28T00:00:00+00:00</updated>
    <id>https://slatecave.net/atom.xml</id>
    <entry xml:lang="en">
        <title>Caddy JSON log format</title>
        <published>2026-03-22T00:00:00+00:00</published>
        <updated>2026-03-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/caddy-log-format/"/>
        <id>https://slatecave.net/notebook/caddy-log-format/</id>
        
        <summary type="text">How the default JSON access log format of the Caddy webserver is structured</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/caddy-log-format/">&lt;h2 id=&quot;the-access-log-format&quot;&gt;The Access log Format&lt;&#x2F;h2&gt;
&lt;p&gt;The access logs are written as text files with one independent JSON object per line for each request.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;level&lt;&#x2F;code&gt;: log level, set to &lt;code&gt;info&lt;&#x2F;code&gt; for most requests, sometimes &lt;code&gt;error&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;ts&lt;&#x2F;code&gt;: Timestamp of thee request, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;caddyserver.com&#x2F;docs&#x2F;json&#x2F;logging&#x2F;logs&#x2F;encoder&#x2F;json&#x2F;time_format&quot;&gt;format is configurable&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;logger&lt;&#x2F;code&gt;: An identifier for the logger inside caddy that produced the line&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;msg&lt;&#x2F;code&gt;: Always set to &lt;code&gt;handled request&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;request&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;remote_ip&lt;&#x2F;code&gt;: IP that the request came from&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;remote_port&lt;&#x2F;code&gt;: Port number that the request came from (as string)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;client_ip&lt;&#x2F;code&gt;: IP that caddy believes is the actual client&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;proto&lt;&#x2F;code&gt;: HTTP protocol i.e. &lt;code&gt;HTTP&#x2F;1.1&lt;&#x2F;code&gt;, &lt;code&gt;HTTP&#x2F;2.0&lt;&#x2F;code&gt;, &lt;code&gt;HTTP&#x2F;3.0&lt;&#x2F;code&gt;, …&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;method&lt;&#x2F;code&gt;: The http method used for the request i.e. &lt;code&gt;GET&lt;&#x2F;code&gt;, &lt;code&gt;OPTIONS&lt;&#x2F;code&gt;, &lt;code&gt;POST&lt;&#x2F;code&gt;, &lt;code&gt;PUT&lt;&#x2F;code&gt;, &lt;code&gt;DELETE&lt;&#x2F;code&gt;, …&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;host&lt;&#x2F;code&gt;: The host the request was targeted at (i.e. &lt;code&gt;example.org&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;uri&lt;&#x2F;code&gt;: Absolue path and query of the URI that was requested i.e. &lt;code&gt;&#x2F;&lt;&#x2F;code&gt;, &lt;code&gt;&#x2F;icon.png&lt;&#x2F;code&gt;, &lt;code&gt;&#x2F;search&#x2F;?q=foo&lt;&#x2F;code&gt;, …&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;headers&lt;&#x2F;code&gt;: An object describing the request headers&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;tls&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;resumed&lt;&#x2F;code&gt;: nullable boolean, weather a previous tls connection was resumed&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;version&lt;&#x2F;code&gt;: nullable integer, the legacy version field of the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Transport_Layer_Security#TLS_record&quot;&gt;TLS-record&lt;&#x2F;a&gt; (substract &lt;code&gt;768&lt;&#x2F;code&gt; to get the minor version)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;cipher_suite&lt;&#x2F;code&gt;: nullable integer, &lt;em&gt;somehow&lt;&#x2F;em&gt; encodes the used TLS cipher suite&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;proto&lt;&#x2F;code&gt;: nullable string, empty, null or &lt;code&gt;http&#x2F;1.1&lt;&#x2F;code&gt; for &lt;code&gt;HTTP&#x2F;1.*&lt;&#x2F;code&gt;, &lt;code&gt;h2&lt;&#x2F;code&gt; for &lt;code&gt;HTTP&#x2F;2.0&lt;&#x2F;code&gt; and &lt;code&gt;h3&lt;&#x2F;code&gt; for &lt;code&gt;HTTP&#x2F;3.0&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;server_name&lt;&#x2F;code&gt; nullable string, the name given via SNI&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;bytes_read&lt;&#x2F;code&gt;: integer, How many request body bytes were read&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;user_id&lt;&#x2F;code&gt;: string, usually empty&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;duration&lt;&#x2F;code&gt;: float, How long the request took in seconds&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;size&lt;&#x2F;code&gt;: integer: Reply size in bytes&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;status&lt;&#x2F;code&gt;: integer: HTTP status code, status code &lt;code&gt;0&lt;&#x2F;code&gt; means that the connection was closed before a reply could be sent&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;resp_headers&lt;&#x2F;code&gt;: Object describing the response headers&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;headers&quot;&gt;Headers&lt;&#x2F;h3&gt;
&lt;p&gt;Header names are always writte in Kebab-Title-Case i.e. &lt;code&gt;User-Agent&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Header names always map to an array of values as it is possible that each header could be duplicated (sometimes intentional, sometimes not).&lt;&#x2F;p&gt;
&lt;p&gt;Some headers like &lt;code&gt;Authorization&lt;&#x2F;code&gt; always have the placeholder value &lt;code&gt;REDACTED&lt;&#x2F;code&gt;, caddy does this automatically by default.&lt;&#x2F;p&gt;
&lt;p&gt;Example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;json&quot;&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 z-quoted z-double z-json&quot;&gt;headers&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;	&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-property-name&quot;&gt;Content-Length&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-punctuation&quot;&gt;		&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-json&quot;&gt;227&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-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-support z-type z-property-name&quot;&gt;User-Agent&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-punctuation&quot;&gt;		&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-json&quot;&gt;Synapse&#x2F;1.149.1&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-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-support z-type z-property-name&quot;&gt;Content-Type&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-punctuation&quot;&gt;		&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-json&quot;&gt;application&#x2F;json&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-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-support z-type z-property-name&quot;&gt;Authorization&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-punctuation&quot;&gt;		&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-json&quot;&gt;REDACTED&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-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;</content>
    </entry>
    <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>The SQLite strict Guide</title>
        <published>2026-01-23T00: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/notebook/sqlite-strict-guide/"/>
        <id>https://slatecave.net/notebook/sqlite-strict-guide/</id>
        
        <summary type="text">How to make data validation in SQLite possible, an overview</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/sqlite-strict-guide/">&lt;h2 id=&quot;sqlite-is-permissive-by-default&quot;&gt;SQLite is permissive by default&lt;&#x2F;h2&gt;
&lt;p&gt;SQLite is famous for working with the absolute minimum of data specification. While this may help with prototyping it is really bad at catching mistakes early because a lot of unintended things are permitted.&lt;&#x2F;p&gt;
&lt;p&gt;A table definition can be as simple as:&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; notes&lt;&#x2F;span&gt;&lt;span&gt;(id, note);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This creates a table &lt;code&gt;notes&lt;&#x2F;code&gt; with the columns &lt;code&gt;id&lt;&#x2F;code&gt; and &lt;code&gt;note&lt;&#x2F;code&gt;. What is permitted inside these columns? Everything!&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Setting &lt;code&gt;note&lt;&#x2F;code&gt; and &lt;code&gt;id&lt;&#x2F;code&gt; both to &lt;code&gt;NULL&lt;&#x2F;code&gt;? Yes&lt;&#x2F;li&gt;
&lt;li&gt;Having ten notes with the same &lt;code&gt;id&lt;&#x2F;code&gt;? Yes&lt;&#x2F;li&gt;
&lt;li&gt;Mixing numbers, text and BLOB-encoded UUIDs in &lt;code&gt;id&lt;&#x2F;code&gt;? Completely fine&lt;&#x2F;li&gt;
&lt;li&gt;Setting &lt;code&gt;note&lt;&#x2F;code&gt; to 3.1415 (the number)? Why not&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As you can see this simple example allows a huge number of unintended variations of the data, most of these will probably make your program crash if actually encountered and it only gets worse with more complex schemata.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;who-is-this-guide-for&quot;&gt;Who is this Guide for?&lt;&#x2F;h2&gt;
&lt;p&gt;This guide is for everyone who wants their database to loudly complain about incorrect data the moment something tries to write it to catch errors early and save headaches later.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to follow along you can do so by running &lt;code&gt;sqlite3&lt;&#x2F;code&gt; and copying the examples into the resulting SQLite shell.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;primary-keys&quot;&gt;&lt;code&gt;PRIMARY KEY&lt;&#x2F;code&gt;s&lt;&#x2F;h2&gt;
&lt;p&gt;Declare your primary keys as &lt;code&gt;INTEGER PRIMARY KEY NOT NULL&lt;&#x2F;code&gt;, this will make them a super fast alias for the &lt;code&gt;rowid&lt;&#x2F;code&gt; and autofill when you&#x27;re inserting new columns.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Using &lt;code&gt;INT PRIMARY KEY&lt;&#x2F;code&gt; will not work!&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;www.sqlite.org&amp;#x2F;lang_createtable.html#rowid&quot; &gt;
		SQLite &lt;code&gt;CREATE TABLE&lt;&#x2F;code&gt; documentation
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;explicit-null-and-not-null&quot;&gt;Explicit &lt;code&gt;NULL&lt;&#x2F;code&gt; and &lt;code&gt;NOT NULL&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;By default SQLite assumes that any column is nullable and usually you don&#x27;t want that because few things can ruin your day like a &lt;code&gt;NULL&lt;&#x2F;code&gt; in the wrong place.&lt;&#x2F;p&gt;
&lt;p&gt;Always declare &lt;code&gt;NULL&lt;&#x2F;code&gt; or &lt;code&gt;NOT NULL&lt;&#x2F;code&gt; with every column.&lt;&#x2F;p&gt;
&lt;p&gt;This way it is always explicit what the column expects.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;strict-tables&quot;&gt;&lt;code&gt;STRICT&lt;&#x2F;code&gt; Tables&lt;&#x2F;h2&gt;
&lt;p&gt;SQLite by default does not do any actual type checking on the columns (except for some special cases). Putting &lt;code&gt;STRICT&lt;&#x2F;code&gt; as the last word of a &lt;code&gt;CREATE TABLE&lt;&#x2F;code&gt; statement changes that and makes SQLite check.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example of a strict table:&lt;&#x2F;figcaption&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; strict_example&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;    id &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; PRIMARY KEY&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;    foo &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;    bar &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;INTEGER&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; NULL&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;) STRICT;&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; Will work&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; strict_example(foo, bar) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;VALUES&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;Human rights&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;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;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; strict_example(foo, bar) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;VALUES&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;Matter&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;span class=&quot;z-keyword&quot;&gt;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;&#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; Automatic type conversion still happens&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; strict_example(foo, bar) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;VALUES&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;123&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;123&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;&#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; Will not work&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; strict_example(foo, bar) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;VALUES&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;Matter&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;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Antimatter&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-punctuation z-definition z-comment&quot;&gt;--&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Output:&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; Runtime error: cannot store TEXT value in INTEGER column strict_example.bar (19)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;In strict tables you can use the following datatypes:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Type&lt;&#x2F;th&gt;&lt;th&gt;What to use for&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;INT&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Prefer using &lt;code&gt;INTEGER&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;INTEGER&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;A 64 bit signed integer (that is encoded suing a variable length scheme), it can also be used as &lt;code&gt;PRIMARY KEY&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;REAL&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;A number type that can store non integer numbers (64-bit float)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;TEXT&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Use for storing well formed Unicode&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;BLOB&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Can store arbitrary binary data&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;ANY&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Can store an arbitrary datatype, similar to using a table without &lt;code&gt;STRICT&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Constraints will be checked when modifying table records and can be explicitly checked using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.sqlite.org&#x2F;pragma.html#pragma_integrity_check&quot;&gt;&lt;code&gt;PRAGMA integrity_check&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.sqlite.org&#x2F;pragma.html#pragma_quick_check&quot;&gt;&lt;code&gt;PRAGMA quick_check&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&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;sqlite.org&amp;#x2F;stricttables.html&quot; &gt;
		SQLite strict table documentation
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;check-constraints&quot;&gt;&lt;code&gt;CHECK()&lt;&#x2F;code&gt; Constraints&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;CHECK(…)&lt;&#x2F;code&gt; constraints can be used for additional validation of data in the same place as &lt;code&gt;UNIQUE&lt;&#x2F;code&gt;, between the parenthesis there must be an SQL expression that must evaluate to a nonzero value for the check to pass. The check is ran and must pass when rows are added or updated.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;An example would be &lt;code&gt;CHECK(start_time &amp;lt; end)&lt;&#x2F;code&gt;:&lt;&#x2F;figcaption&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; check_example&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;    id &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; PRIMARY KEY&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;    start_time &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;INTEGER&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;    end_time &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;INTEGER&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; 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 class=&quot;z-storage z-modifier&quot;&gt;    CHECK&lt;&#x2F;span&gt;&lt;span&gt;(start_time &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;lt;=&lt;&#x2F;span&gt;&lt;span&gt; end_time)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;) STRICT;&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; Will work&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; check_example(start_time, end_time) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;VALUES&lt;&#x2F;span&gt;&lt;span&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;span class=&quot;z-constant&quot;&gt;5&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;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; check_example(start_time, end_time) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;VALUES&lt;&#x2F;span&gt;&lt;span&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;span class=&quot;z-keyword&quot;&gt;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;&#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; Will fail because of the check&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; check_example(start_time, end_time) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;VALUES&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&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-punctuation z-definition z-comment&quot;&gt;--&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Output:&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; Runtime error: CHECK constraint failed: start_time &amp;lt;= end_time (19)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;Trying to construct a record where the start time is after the end time is not possible on this table. However leaving the &lt;code&gt;end_time&lt;&#x2F;code&gt; &lt;code&gt;NULL&lt;&#x2F;code&gt; will be possible because then &lt;code&gt;start_time &amp;lt;= end_time&lt;&#x2F;code&gt; evaluates to &lt;code&gt;NULL&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Using subqueries inside the &lt;code&gt;CHECK&lt;&#x2F;code&gt; is not possible.&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;sqlite.org&amp;#x2F;lang_createtable.html#ckconst&quot; &gt;
		SQLite &lt;code&gt;CHECK&lt;&#x2F;code&gt; constraint documentation
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;unique-index&quot;&gt;&lt;code&gt;UNIQUE INDEX&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;SQLite supports the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sqlite.org&#x2F;lang_createtable.html#uniqueconst&quot;&gt;&lt;code&gt;UNIQUE&lt;&#x2F;code&gt; keyword&lt;&#x2F;a&gt; to make sure the values or combinations of values only occur once, this is a well known SQL feature, another way to enforce the same constraints is using &lt;code&gt;CREATE UNIQUE INDEX&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;UNIQUE INDEX&lt;&#x2F;code&gt; has the additional benefit of not only allowing one to use the columns of a table, but also SQL expressions (SQLite calls this an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sqlite.org&#x2F;syntax&#x2F;indexed-column.html&quot;&gt;indexed-column&lt;&#x2F;a&gt;) which can be be used to get creative with SQL constraints.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example of a table that enforces a case insensitive uniqueness constraint on the &lt;code&gt;txt&lt;&#x2F;code&gt; column:&lt;&#x2F;figcaption&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; unique_example&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;    id &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; PRIMARY KEY&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;    txt &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;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;) STRICT;&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; UNIQUE INDEX&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; case_insensititve&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; unique_example(&lt;&#x2F;span&gt;&lt;span&gt;lower&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;txt));&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; Will work&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; unique_example(txt) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;VALUES&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;Abc&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;&#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; Will fail becuase a diffent abc is already present&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; unique_example(txt) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;VALUES&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;ABC&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-punctuation z-definition z-comment&quot;&gt;--&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Output:&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; Runtime error: UNIQUE constraint failed: index &amp;#39;case_insensititve&amp;#39; (19)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	&lt;figcaption&gt;It works by checking the lower cased text is unique.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;&lt;b&gt;Note on the Example:&lt;&#x2F;b&gt; This specific constraint could also be applied with a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sqlite.org&#x2F;lang_createtable.html#collateclause&quot;&gt;&lt;code&gt;COLLATE NOCASE&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; in combination with a regular &lt;code&gt;UNIQUE&lt;&#x2F;code&gt; on the column.&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;sqlite.org&amp;#x2F;lang_createindex.html#uniqueidx&quot; &gt;
		SQLite unique index documentation
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;combining-unique-and-null&quot;&gt;Combining &lt;code&gt;UNIQUE&lt;&#x2F;code&gt; and &lt;code&gt;NULL&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;SQLite handles combining &lt;code&gt;NULL&lt;&#x2F;code&gt; and &lt;code&gt;UNIQUE&lt;&#x2F;code&gt; by allowing &lt;code&gt;NULL&lt;&#x2F;code&gt; to appear multiple times within a unique column.&lt;&#x2F;p&gt;
&lt;p&gt;This behaviour may be different from a database you are used to as some other databases only allow one &lt;code&gt;NULL&lt;&#x2F;code&gt; per unique column.&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;sqlite.org&amp;#x2F;nulls.html&quot; &gt;
		SQLite &lt;code&gt;NULL&lt;&#x2F;code&gt; handling documentation and comparison
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;foreign-keys-and-references&quot;&gt;Foreign Keys and &lt;code&gt;REFERENCES&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Enable foreign key support with:&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;PRAGMA foreign_keys &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; ON&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;Add a &lt;code&gt;REFERENCES {parent_table}({parent_column})&lt;&#x2F;code&gt; where &lt;code&gt;{parent_table}&lt;&#x2F;code&gt; and &lt;code&gt;{parent_column}&lt;&#x2F;code&gt; identify the database table and column the ids in the annotated column point to. The &lt;code&gt;REFERENCES&lt;&#x2F;code&gt; must point at columns that are a &lt;code&gt;PRIMARY KEY&lt;&#x2F;code&gt; or have a &lt;code&gt;UNIQUE&lt;&#x2F;code&gt; constraint.&lt;&#x2F;p&gt;
&lt;p&gt;In SQLite the terminology is always that a &lt;i&gt;child table&lt;&#x2F;i&gt; references a &lt;i&gt;parent table&lt;&#x2F;i&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It also protects the references from breaking by prohibiting any changes to the fields in the parent table that are referenced by any child tables. See the next section on how to configure this.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example where an item table references entries in a category table:&lt;&#x2F;figcaption&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;PRAGMA foreign_keys &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; ON&lt;&#x2F;span&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; TABLE&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; example_category&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;    c_id &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;INTEGER&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; NOT NULL&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-modifier&quot;&gt; PRIMARY KEY&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;    name&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;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;) STRICT;&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;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; example_category(c_id, &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;VALUES&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-constant&quot;&gt;1&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;foo&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&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;2&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;bar&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&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;3&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;baz&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;&#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; TABLE&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; example_item&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;    i_id &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;INTEGER&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; NOT NULL&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-modifier&quot;&gt; PRIMARY KEY&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;    category &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;INTEGER&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; NULL&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-modifier&quot;&gt; REFERENCES&lt;&#x2F;span&gt;&lt;span&gt; example_category(c_id)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;) STRICT;&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; Will work&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; example_item(category) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;VALUES&lt;&#x2F;span&gt;&lt;span&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;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; example_item(category) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;VALUES&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;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;&#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; Will fail because there is no category with id 30&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; example_item(category) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;VALUES&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;30&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 z-definition z-comment&quot;&gt;--&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Output:&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; Runtime error: FOREIGN KEY constraint failed (19)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-text&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;sqlite.org&amp;#x2F;foreignkeys.html&quot; &gt;
		SQLite foreign key documentation
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;on-update-and-on-delete&quot;&gt;&lt;code&gt;ON UPDATE&lt;&#x2F;code&gt; and &lt;code&gt;ON DELETE&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This is an extension of the &lt;code&gt;REFERENCES&lt;&#x2F;code&gt; clause in the previous section. The &lt;code&gt;REFERENCES&lt;&#x2F;code&gt; itself can make sure that no new columns violate the schema while &lt;code&gt;ON UPDATE&lt;&#x2F;code&gt; and &lt;code&gt;ON DELETE&lt;&#x2F;code&gt; make sure, they stay valid when &lt;code&gt;UPDATE&lt;&#x2F;code&gt; and &lt;code&gt;DELETE&lt;&#x2F;code&gt; are used on the referenced (&quot;parent&quot;) rows by deciding how those operations should be handled.&lt;&#x2F;p&gt;




	


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

	
		&lt;dt&gt;
		&lt;code&gt;NO ACTION&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		No action is taken when the parent key is modified, this is the same behaviour as without any &lt;code&gt;REFERENCES&lt;&#x2F;code&gt; and allows breaking the reference without any intervention or warning from the database.
		&lt;&#x2F;dd&gt;
	

	

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

	
		&lt;dd&gt;
		This will cause any operation that would result in changed or broken references to throw the error &quot;FOREIGN KEY constraint failed (19)&quot;. This is the default when a &lt;code&gt;REFERENCES&lt;&#x2F;code&gt; doesn&#x27;t specify any &lt;code&gt;ON UPDATE&lt;&#x2F;code&gt; or &lt;code&gt;ON DELETE&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;SET NULL&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This will set the reference field to &lt;code&gt;NULL&lt;&#x2F;code&gt; on the operation(s) it is configured for.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;SET DEFAULT&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This will set the reference field to its &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.sqlite.org&#x2F;lang_createtable.html#the_default_clause&quot;&gt;&lt;code&gt;DEFAULT&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; value on the operation(s) it is configured for.
		&lt;&#x2F;dd&gt;
	

	

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

	
		&lt;dd&gt;
		This will make the references follow what the parent is doing, on an update all referencing fields will be updated too, on a deletion all referencing fields will be deleted too.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;figure&gt;
	&lt;figcaption&gt;An example that demonstrates cascading updates and deletes in a setting where log entries reference some kind of program run:&lt;&#x2F;figcaption&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;PRAGMA foreign_keys &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; ON&lt;&#x2F;span&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; TABLE&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; example_run&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;    r_id &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;INTEGER&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; NOT NULL&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-modifier&quot;&gt; PRIMARY KEY&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;    name&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;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;) STRICT;&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;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; example_run(r_id, &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;VALUES&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-constant&quot;&gt;1&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;blubber&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&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;2&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;dragon&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;&#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; TABLE&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; example_log&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;    l_id &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;INTEGER&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; NOT NULL&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-modifier&quot;&gt; PRIMARY KEY&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;    run &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;INTEGER&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; NULL&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-modifier&quot;&gt; REFERENCES&lt;&#x2F;span&gt;&lt;span&gt; example_run(r_id) &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-modifier&quot;&gt;ON UPDATE CASCADE&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-modifier&quot;&gt; ON DELETE CASCADE&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;    message&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;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;) STRICT;&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;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; example_log(run, &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;VALUES&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-constant&quot;&gt;1&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;blah&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&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;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;blubb&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&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;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;done&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&gt;	(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;2&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;dragon goes &amp;quot;miep&amp;quot;&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&gt;	(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;2&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;dragon receives hug&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;&#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; Move ids of the run around for demonstration purposes&lt;&#x2F;span&gt;&lt;&#x2F;span&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; example_run &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;SET&lt;&#x2F;span&gt;&lt;span&gt; r_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; 42&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; WHERE&lt;&#x2F;span&gt;&lt;span&gt; r_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; 2&lt;&#x2F;span&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-punctuation z-definition z-comment&quot;&gt;--&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; All entries previously associated with run 2 have now been rewritten to run 42&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&lt;&#x2F;span&gt;&lt;span&gt; run, &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;message&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; FROM&lt;&#x2F;span&gt;&lt;span&gt; example_log;&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; Output:&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; 1|blah&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; 1|blubb&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; 1|done&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; 42|dragon goes &amp;quot;miep&amp;quot;&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; 42|dragon receives hug&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; Will delete the run&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;DELETE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; FROM&lt;&#x2F;span&gt;&lt;span&gt; example_run &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span&gt; r_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; 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;&#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 also the log entries associated with the run:&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; This will return no entries where the run is set to 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;SELECT&lt;&#x2F;span&gt;&lt;span&gt; run, &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;message&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; FROM&lt;&#x2F;span&gt;&lt;span&gt; example_log;&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; Output:&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; 42|dragon goes &amp;quot;miep&amp;quot;&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; 42|dragon receives hug&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-text&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;sqlite.org&amp;#x2F;foreignkeys.html#fk_actions&quot; &gt;
		SQLite &lt;code&gt;ON DELETE&lt;&#x2F;code&gt; and &lt;code&gt;ON UPDATE&lt;&#x2F;code&gt; documentation
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;see-also-triggers&quot;&gt;See also: Triggers&lt;&#x2F;h2&gt;
&lt;p&gt;Triggers can be a way to enforce other constraints on the data model, even spanning multiple tables and producing custom error messages using the &lt;code&gt;RAISE()&lt;&#x2F;code&gt; function.&lt;&#x2F;p&gt;
&lt;p&gt;However, getting triggers right would a it&#x27;s own guide and usually one wants &lt;code&gt;INSERT&lt;&#x2F;code&gt; &lt;em&gt;and&lt;&#x2F;em&gt; &lt;code&gt;UPDATE&lt;&#x2F;code&gt; triggers leading to a lot of duplicate code that is difficult to debug.&lt;&#x2F;p&gt;
&lt;p&gt;In a nutshell: Triggers are powerful but difficult, use them only when there is absolutely no other option.&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;www.sqlite.org&amp;#x2F;lang_createtrigger.html&quot; &gt;
		SQLite &lt;code&gt;CREATE TRIGGER&lt;&#x2F;code&gt; documentation
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&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>Interesting Resources for building Search Engines</title>
        <published>2025-02-01T00: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/notebook/interesting-search/"/>
        <id>https://slatecave.net/notebook/interesting-search/</id>
        
        <summary type="text">A collection of links to articles and documentation.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/interesting-search/">&lt;h2 id=&quot;how-search-works&quot;&gt;How Search works&lt;&#x2F;h2&gt;
&lt;p&gt;Articles that dive into how a Search Engine works under the hood.&lt;&#x2F;p&gt;

	

&lt;ul class=&quot;link-list&quot;&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;izihawa.github.io&#x2F;summa&#x2F;blog&#x2F;how-search-engines-work&#x2F;&quot;&gt;How Search Engines Work — Summa Blog&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;blog&#x2F;how-full-text-search-engines-work&quot;&gt;How to […]: inside a full text search engine — meilisearch Blog&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;infolab.stanford.edu&#x2F;pub&#x2F;papers&#x2F;google.pdf&quot;&gt;The Anatomy of a Large-Scale Hypertextual Web Search Engine — The original Google Paper (PDF)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p&gt;&lt;b&gt;Shameless self promotion:&lt;&#x2F;b&gt; Check out the &lt;a href=&quot;&#x2F;topics&#x2F;#search-engines&quot;&gt;Search Engines topic here on slatecave.net&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;datastructures&quot;&gt;Datastructures&lt;&#x2F;h2&gt;
&lt;p&gt;Datastructures useful for building search engines.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fst-finite-state-transducers&quot;&gt;FST — Finite State Transducers&lt;&#x2F;h3&gt;
&lt;p&gt;In a nutshell: FSTs use properties of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Finite-state_machine&quot;&gt;State Machines&lt;&#x2F;a&gt; to store sorted sets of strings, map strings to numbers and run fast prefix queries, but also other queries using automata like regex or even &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Levenshtein_distance&quot;&gt;levenstein distance&lt;&#x2F;a&gt; queries.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;burntsushi.net&amp;#x2F;transducers&amp;#x2F;&quot; &gt;
		Index 1,600,000,000 Keys with Automata and Rust
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;An implementation would be the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;fst&quot;&gt;rust fst crate&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;roaring-bitmaps&quot;&gt;Roaring Bitmaps&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;roaringbitmap.org&#x2F;&quot;&gt;Roaring Bitmaps&lt;&#x2F;a&gt; are datastructures, that from the outside can be operated like giant bitmaps, storing very big sets of numbers, but optimise themselves on the inside to use memory as efficient as possible and staying fast.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;arxiv.org&amp;#x2F;pdf&amp;#x2F;1402.6407v4&quot; &gt;
		Better bitmap performance with Roaring bitmaps — Research Paper (PDF)
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;crawling-strategies&quot;&gt;Crawling strategies&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;stract&quot;&gt;Stract&lt;&#x2F;h3&gt;
&lt;p&gt;Stract is mostly Interesting because of its politeness algorithm that in a nutshell is a politeness factor multiplied by a either the response time or a minimum delay, whichever is larger. Capped at a maximum delay of 180s that should be good for the website and the crawler.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; While the algorithm is Interesting the robots.txt &lt;code&gt;Crawl-delay&lt;&#x2F;code&gt; should take priority if present (though that one should also be capped at a friendly value).&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;stract.com&amp;#x2F;webmasters&quot; &gt;
		Stract Crawler — Webmaster Documentation
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;server-to-crawler-protocols&quot;&gt;Server to Crawler Protocols&lt;&#x2F;h2&gt;
&lt;p&gt;If are crawling the web and care about admins not making your life difficult one should respect the web servers and their administrators. Respecting these signals also helps gathering higher quality data.&lt;&#x2F;p&gt;
&lt;p&gt;The baseline is implementing and respecting robot.txt, other protocols may provide additional useful metadata depending on your use case.&lt;&#x2F;p&gt;

	

&lt;ul class=&quot;link-list&quot;&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.robotstxt.org&#x2F;&quot;&gt;robots.txt website&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.w3.org&#x2F;community&#x2F;reports&#x2F;tdmrep&#x2F;CG-FINAL-tdmrep-20240202&#x2F;#sec-introduction&quot;&gt;Text Data Mining — TDM Reservation Protocol&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p&gt;An interesting overview is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;seirdy.one&#x2F;meta&#x2F;scrapers-i-block&#x2F;#how-i-block-bots&quot;&gt;&quot;How I block bots&quot; by Seirdy&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;relevancy-and-ranking&quot;&gt;Relevancy and Ranking&lt;&#x2F;h2&gt;
&lt;p&gt;List of algorithms that allow one to sort search results.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;bag-of-words-ranking&quot;&gt;Bag of words ranking&lt;&#x2F;h3&gt;
&lt;p&gt;The most straight forward way of measuring how well a document matches a query is word counting. Though simple counters aren&#x27;t very good at this, weighting them relative to statistics of your document corpus makes very good at keyword based search though.&lt;&#x2F;p&gt;
&lt;p&gt;Usually the go to is BM25 as it automatically lightens the impact of common words and rewards completeness over repeating the same words over and over again. It also has a theoretical maximum output, so one can normalise it.&lt;&#x2F;p&gt;

	

&lt;ul class=&quot;link-list&quot;&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Okapi_BM25&quot;&gt;Okapi BM25 — simple normalised and tuneable term frequency based algorithm&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Tf%E2%80%93idf&quot;&gt;TF-IDF — term frequency-inverse document frequency&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h3 id=&quot;pagerank&quot;&gt;PageRank&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;PageRank&quot;&gt;PageRank&lt;&#x2F;a&gt; at its core is for measuring a documents overall relevancy in the context of other documents that can link to each other, not towards a search query.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s famous for being at the core of Google for many years.&lt;&#x2F;p&gt;
&lt;p&gt;The PageRank algorithm is described in the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;infolab.stanford.edu&#x2F;pub&#x2F;papers&#x2F;google.pdf&quot;&gt;original Google Paper from 1998&lt;&#x2F;a&gt; but also has its own dedicated Paper.&lt;&#x2F;p&gt;
&lt;p&gt;In a nutshell: Every page has a score, it distributes that score via its links to other pages (and keeps some for itself for dampening reasons) iterate a few times and the scores now represent how relevant in terms of incoming links a page is.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;http:&amp;#x2F;&amp;#x2F;ilpubs.stanford.edu:8090&amp;#x2F;422&amp;#x2F;1&amp;#x2F;1999-66.pdf&quot; &gt;
		The PageRank Citation Ranking: Bringing Order to the Web
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h3 id=&quot;textrank&quot;&gt;TextRank&lt;&#x2F;h3&gt;
&lt;p&gt;TextRank is a variation of PageRank applied to texts.&lt;&#x2F;p&gt;
&lt;p&gt;It can be used for keyword extraction, simple, automatic summaries, but also for augmenting bag of words algorithms with some smarter weights.&lt;&#x2F;p&gt;
&lt;p&gt;TextRank can also be used to pull in knowledge from a knowledge graph when connecting words and sentences.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;aclanthology.org&amp;#x2F;W04-3252&amp;#x2F;&quot; &gt;
		TextRank: Bringing Order into Text
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;text-processing&quot;&gt;Text Processing&lt;&#x2F;h2&gt;
&lt;p&gt;Text by itself is pretty messy, when building a search engine one probably wants to apply some normalization.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;snowball-stemming&quot;&gt;Snowball Stemming&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Snowball is a small string processing language for creating stemming algorithms for use in Information Retrieval, plus a collection of stemming algorithms implemented using it.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The best thing about Snowball is that it can be compiled to other languages you are more likely to write your search engine in.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;snowballstem.org&amp;#x2F;&quot; &gt;
		Snowball
	&lt;&#x2F;a&gt;
&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>Acronyms on the Social Internet</title>
        <published>2024-12-25T00:00:00+00:00</published>
        <updated>2024-12-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/social-acronyms/"/>
        <id>https://slatecave.net/notebook/social-acronyms/</id>
        
        <summary type="text">A little dictionary for random letter collections one might encounter on the Internet.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/social-acronyms/">&lt;p&gt;&lt;q&gt;TIL: This thing is very useful IMHO&lt;&#x2F;q&gt; … what ???&lt;&#x2F;p&gt;
&lt;h2 id=&quot;acronyms&quot;&gt;Acronyms&lt;&#x2F;h2&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	


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

	
		&lt;dt&gt;
		ALT (Text)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Alternative Text
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		ALT-Text is a kind of image description that describes the &lt;em&gt;meaning&lt;&#x2F;em&gt; of or the &lt;em&gt;intention&lt;&#x2F;em&gt; behind an image (but not necessarily every visual detail) so it helps people who can&#x27;t see (slow connection, broken link, blind, …) or understand image for some reason.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		It can also be used as an art-form where the ALT-Text serves its original function &lt;strong&gt;and&lt;&#x2F;strong&gt; is itself worth reading because it is written in a creative way.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		alt (Account)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Alternative&#x2F;Secondary Account
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Some people have multiple Accounts they publish on or openly keep as backups, these may be referred to as alt accounts or &lt;q&gt;alts&lt;&#x2F;q&gt;.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		CW
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Trauma_trigger#Trigger_warnings&quot;&gt;Content Warning&lt;&#x2F;a&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		DM
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Direct Message
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Message sent with restricted visibility to only a few intended recipients.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		IMO
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		In My Opinion
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		IMHO
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		In My Honest Opinion
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		OH
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Over Heard
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Usually used as a prefix for posts quoting something funny someone overheard.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		The variation &lt;q&gt;Self OH&lt;&#x2F;q&gt; means someone is quoting themselves.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		RFC
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Request For Comments
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Either refers to the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;&quot;&gt;IETF&#x2F;IRTF&#x2F;IAB RFC specifications&lt;&#x2F;a&gt;, a project specific RFC system or a standalone document someone wants feedback on.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		TIL
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Today I Learned
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Used as a prefix for posts where someone who just learned about something is sharing that information. Usually because that information is considered useful.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;This list isn&#x27;t complete, it&#x27;ll be expanded as people ask me or I learn about things myself.&lt;&#x2F;p&gt;
&lt;p&gt;Also see the list of Acronyms for content warnings below.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tone-indicators&quot;&gt;Tone Indicators&lt;&#x2F;h2&gt;
&lt;p&gt;Tone indicators like &lt;code&gt;&#x2F;s&lt;&#x2F;code&gt; are sometimes appended to the end of a message to signal the intention behind a message (i.e. sarcasm), because on the internet things aren&#x27;t always obvious (evidence: you are reading this article). They always start with a forward slash &lt;code&gt;&#x2F;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The most important&#x2F;common ones are:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


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

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

	
		&lt;dd&gt;
		sarcasm
		&lt;&#x2F;dd&gt;
	

	

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

	
		&lt;dd&gt;
		joking
		&lt;&#x2F;dd&gt;
	

	

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

	
		&lt;dd&gt;
		half joking
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-action-next&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;en.wikipedia.org&amp;#x2F;wiki&amp;#x2F;Tone_indicator&quot; &gt;
		Wikipedia has a more complete list and background information.
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;acronyms-for-content-warnings&quot;&gt;Acronyms for Content Warnings&lt;&#x2F;h2&gt;
&lt;p&gt;Content warnings are an important mechanism to let people opt out of content they might find uncomfortable to very stressing. To keep the needed effort low a bunch of acronyms to tag common&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	


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

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

	
		&lt;dd&gt;
		Positive Content
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Used as a suffix the poster thinks the content behind the warning is on the more positive side of things.
		&lt;&#x2F;dd&gt;
	

	

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

	
		&lt;dd&gt;
		Negative Content
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Used as a suffix the poster thinks the content behind the warning is on the more negative side of things.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		ph
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Physical Health
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Usually content mentioning injuries or other physical health issues
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		mh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Mental Health
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Content mentioning mental health issues
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		pol, depol, uspol, …
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Politics
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Usually used as part of &lt;i&gt;Content Warning&lt;&#x2F;i&gt; messages to flag messages mentioning politics, it can be prefixed by a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;List_of_ISO_3166_country_codes&quot;&gt;two or three letter country reference&lt;&#x2F;a&gt; to quickly communicate which country is talked about.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&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>Templating Zoo</title>
        <published>2024-07-02T00:00:00+00:00</published>
        <updated>2024-07-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/templating/"/>
        <id>https://slatecave.net/notebook/templating/</id>
        
        <summary type="text">Stuck with some undocumented templating?</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/templating/">&lt;h2 id=&quot;what-is-templating&quot;&gt;What is Templating?&lt;&#x2F;h2&gt;
&lt;p&gt;Templating in the sense of this articlee is when there is a text with placeholders that get filled in according to some set of rules (the &lt;i&gt;templating language&lt;&#x2F;i&gt;). Using templating is often easier than hardcoding some string assembly logic, it also has the benefit that the template could be read in from a configuration file.&lt;&#x2F;p&gt;
&lt;p&gt;In case you&#x27;ve ended up here because you have some sample templating but no documentation: Keep reading, you&#x27;ll probably find what you&#x27;re searching for. (No gurantees though, there are many templating mechanisms out there)&lt;&#x2F;p&gt;
&lt;p&gt;Since most Templating languages borrow from each other it makes sense to group them into Families.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-printf-likes&quot;&gt;The printf likes&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Printf&quot;&gt;printf()&lt;&#x2F;a&gt; function makes it easy to generate semi-templated text in C and other languages without having to do manual string manipulaion. These templates are sometimes even applicable in reverse like with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Scanf&quot;&gt;scanf&lt;&#x2F;a&gt; or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;man3&#x2F;strptime.3&quot;&gt;strptime&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
			
			
		
	


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

	
		&lt;dt&gt;
		How to recognize
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The placeholders usually start with a percent sign (&lt;code&gt;%&lt;&#x2F;code&gt;) and end in a single letter that depends on the datatype. Extra information on how exactly to format is between the two.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Special characters may be escapable using &lt;a href=&quot;&#x2F;notebook&#x2F;ascii&#x2F;#escaping-in-scripting-and-programming&quot;&gt;backslash (&lt;code&gt;&#x2F;&lt;&#x2F;code&gt;) escape sequences&lt;&#x2F;a&gt;.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Examples
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;Template that contains a string: %s&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;Formatted number: %.2f&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;Literal percent sign: %%&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Languages
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		printf likes are implemented in many places
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		The fully featured printf is likely to be found around C programs or in scripting languages like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.lua.org&#x2F;manual&#x2F;5.4&#x2F;manual.html#pdf-string.format&quot;&gt;&lt;code&gt;string.format()&lt;&#x2F;code&gt; in lua&lt;&#x2F;a&gt; or the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;printf&quot;&gt;printf shell command&lt;&#x2F;a&gt;.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Documentation
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Printf#Format_specifier&quot;&gt;printf&lt;&#x2F;a&gt; for generic number and string formatting
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;strftime.3&quot;&gt;strftime&lt;&#x2F;a&gt; for formatting timestamps (quite popular across different libraries)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		custom formats like for the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;find#printf&quot;&gt;find command&lt;&#x2F;a&gt; also exist, those are usually documented in a manual somewhere
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;&lt;b&gt;Compatibility Warning:&lt;&#x2F;b&gt; printf implementations are not consistent and not all implementations support all features. For example encoding characters as octal (i.e. &lt;code&gt;&#x2F;033&lt;&#x2F;code&gt;) is more widely supported than its hexadecimal cousin (i.e. &lt;code&gt;\x1B&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;python-printf-style&quot;&gt;Python printf-style&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3.12&#x2F;library&#x2F;stdtypes.html#old-string-formatting&quot;&gt;Python has &quot;printf-style String Formatting&quot;&lt;&#x2F;a&gt; using the &lt;code&gt;%&lt;&#x2F;code&gt;-operator. The printf syntax has been extended to allow indexing dictionaries and is usually encoutered as &lt;code&gt;%(key)s&lt;&#x2F;code&gt; where &lt;code&gt;key&lt;&#x2F;code&gt; is the key used to index the dictionary and &lt;code&gt;s&lt;&#x2F;code&gt; is an example of the most common printf type, but all other types work too, if the data allows.&lt;&#x2F;p&gt;
&lt;p&gt;While &lt;strong&gt;this approach is dicouraged&lt;&#x2F;strong&gt; (in favor of pythons new format function) it will be used for quite some time in configuration files.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;go-templates&quot;&gt;Go-Templates&lt;&#x2F;h2&gt;
&lt;p&gt;Go-templates are named after ther implementation in the go programming language, the are part of the de-facto standard library. They are best known from the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;&quot;&gt;Hugo static site generator&lt;&#x2F;a&gt;, though the hugo extension usually aren&#x27;t reimplemented elsewhere. The template packages by themselves are pretty barebones.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	


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

	
		&lt;dt&gt;
		How to recognize
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;strong&gt;All&lt;&#x2F;strong&gt; template syntax happens eclosed between &lt;code&gt;{{&lt;&#x2F;code&gt; and &lt;code&gt;}}&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Placeholders usually start with a dot (&lt;code&gt;.&lt;&#x2F;code&gt;)
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Examples
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;{{.Count}} items are made of {{.Material}}&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;{{23 -}} &amp;lt; {{- 45}}&lt;&#x2F;code&gt; renders to &lt;code&gt;23&amp;lt;45&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;Hello, {{.}}!&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;{{ if and (eq .View &quot;search&quot;) (ne .Data.Site &quot;&quot;) }}…{{ end }}&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Langugage
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Go
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Documentation
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;text&#x2F;template&quot;&gt;text&#x2F;template&lt;&#x2F;a&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;html&#x2F;template&quot;&gt;html&#x2F;template&lt;&#x2F;a&gt;
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;jinja2-and-friends&quot;&gt;Jinja2 and friends&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jinja.palletsprojects.com&#x2F;en&#x2F;&quot;&gt;Jinja2&lt;&#x2F;a&gt; is a flexible and popular templating engine, that is used almost everywhere, where moere advanced templating is used. It also heavily inspired the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;keats.github.io&#x2F;tera&#x2F;&quot;&gt;tera crate for rust&lt;&#x2F;a&gt;, which has almost identical syntax.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
			
			
		
	


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

	
		&lt;dt&gt;
		How to recognize
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Variables are enclosed in &lt;code&gt;{{&lt;&#x2F;code&gt; and &lt;code&gt;}}&lt;&#x2F;code&gt;, template logic uses &lt;code&gt;{%&lt;&#x2F;code&gt; and &lt;code&gt;%}&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Templates based on other templates have &lt;code&gt;{% extends &quot;…&quot; %}&lt;&#x2F;code&gt; as their first line.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Variables don&#x27;t start with a dot (&lt;code&gt;.&lt;&#x2F;code&gt;) (that&#x27;&#x27;s a go template thing)
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Examples
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;This morning I need {{ size | default(value=&quot;a huge cup of&quot; }} coffe.&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;{% if thing.is_working %}🥳{% else %}🔧{% endif %}&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Languages
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Python
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Rust
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Documentation
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jinja.palletsprojects.com&#x2F;en&#x2F;&quot;&gt;Jinja2&lt;&#x2F;a&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;keats.github.io&#x2F;tera&#x2F;docs&#x2F;&quot;&gt;tera&lt;&#x2F;a&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Where to find
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		In web-related contexts, generating HTML or XML
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Configuration templates, for example in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.ansible.com&#x2F;ansible&#x2F;latest&#x2F;collections&#x2F;ansible&#x2F;builtin&#x2F;template_module.html&quot;&gt;Ansible&lt;&#x2F;a&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		In other contexts too …
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;xslt&quot;&gt;XSLT&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;XSLT&quot;&gt;XSLT (Extensible Stylesheet Language Transformations)&lt;&#x2F;a&gt; is usually used to convert XML to different XML, but it can also be (ab)used to convert XML to text.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
				
			
		
	


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

	
		&lt;dt&gt;
		How to recognize
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The template is XML and mentions the &lt;code&gt;xsl:&lt;&#x2F;code&gt; namespace (&lt;code&gt;http:&#x2F;&#x2F;www.w3.org&#x2F;1999&#x2F;XSL&#x2F;Transform&lt;&#x2F;code&gt;) a lot.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Example
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		See the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;XSLT#Examples&quot;&gt;Wikipedia Article on XSLT&lt;&#x2F;a&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Languages
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Java, but not exclusiely
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Where to find
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		In enterprisey apllications from the 90s
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;a href=&quot;&#x2F;blog&#x2F;atom-xslt&#x2F;&quot;&gt;In your web-browser&lt;&#x2F;a&gt;
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Standards Zoo</title>
        <published>2024-06-01T00:00:00+00:00</published>
        <updated>2024-06-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/standards/"/>
        <id>https://slatecave.net/notebook/standards/</id>
        
        <summary type="text">Bookmarks for public content of widely used ISO-Standards.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/standards/">&lt;h2 id=&quot;why-this-page-exists&quot;&gt;Why this page exists&lt;&#x2F;h2&gt;
&lt;p&gt;Many things in our world interoperate because they are standardised. Most notably the internet which is built on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rfc-editor.org&quot;&gt;freely available Standards published by the Internet Engineering Task Force (IETF) on rfc-editor.org&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;However most standardisation organisations charge a premium for the standard documents. Through various legal frameworks and publishing by the organisations in their own interest the important content of those standards is public knowledge.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Useful Previews:&lt;&#x2F;b&gt; ISO has free previews of specifications that only include the informative sections. (Those may already contain the information you need to understand something.&lt;&#x2F;p&gt;
&lt;p&gt;This page aims to collect these in one place, one Section per topic.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;timekeeping&quot;&gt;Timekeeping&lt;&#x2F;h2&gt;
&lt;p&gt;For Date and Timestamps there are &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.iso.org&#x2F;standard&#x2F;70907.html&quot;&gt;ISO 8601-1&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.iso.org&#x2F;standard&#x2F;70908.html&quot;&gt;ISO 8601-2&lt;&#x2F;a&gt;. The most important part of those (exact timestamps) is also known as &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc3339&quot;&gt;RFC 3339&lt;&#x2F;a&gt; which got extended by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc9557&quot;&gt;RFC 9557&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ISO_8601&quot;&gt;Wikipedia article on ISO 8601&lt;&#x2F;a&gt; also explains pretty well what the standard specifies.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;languages&quot;&gt;Languages&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;language-identification&quot;&gt;Language Identification&lt;&#x2F;h3&gt;
&lt;p&gt;When in need of a list of codes for unambiguous language identification the standard is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.iso.org&#x2F;standard&#x2F;74575.html&quot;&gt;ISO 639&lt;&#x2F;a&gt; (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.iso.org&#x2F;iso-639-language-code&quot;&gt;ISO 639 Popular standards article&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;One of the official language registries is maintained by the US Library of Congress. A &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.loc.gov&#x2F;standards&#x2F;iso639-2&#x2F;&quot;&gt;list for languages titled ISO 639-2&lt;&#x2F;a&gt; and one for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.loc.gov&#x2F;standards&#x2F;iso639-5&#x2F;&quot;&gt;language families titled ISO 639-5&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;iso639-3.sil.org&#x2F;&quot;&gt;complete ISO 639-3 language list&lt;&#x2F;a&gt; is officially maintained by SIL International (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.sil.org&#x2F;language-technology&#x2F;standards&quot;&gt;SIL also promotes other language related standards&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;language-tagging&quot;&gt;Language Tagging&lt;&#x2F;h3&gt;
&lt;p&gt;For language tagging (i.e. describing a documents language, like the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;html.spec.whatwg.org&#x2F;multipage&#x2F;dom.html#the-lang-and-xml:lang-attributes&quot;&gt;HTML &lt;code&gt;lang&lt;&#x2F;code&gt; attribute&lt;&#x2F;a&gt;) there is the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;info&#x2F;bcp47&quot;&gt;BCP 47 collection of standards&lt;&#x2F;a&gt; the corresponding &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.iana.org&#x2F;assignments&#x2F;language-subtag-registry&#x2F;language-subtag-registry&quot;&gt;language subtag registry maintained by IANA&lt;&#x2F;a&gt; contains codes and descriptions for languages, language variants, regions, scripts. Most of them are compatible with their ISO counterparts.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; The form of language tag with the underscore (i.e. &lt;code&gt;de_DE&lt;&#x2F;code&gt;) &lt;strong&gt;is not&lt;&#x2F;strong&gt; and has never been BCP 47 compliant, write it with a hyphen (i.e. &lt;code&gt;de-DE&lt;&#x2F;code&gt;) instead. The underscore version is used in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pubs.opengroup.org&#x2F;onlinepubs&#x2F;9799919799&#x2F;basedefs&#x2F;V1_chap08.html#tag_08_02&quot;&gt;POSIX systems for Localization&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;script-identification&quot;&gt;Script Identification&lt;&#x2F;h3&gt;
&lt;p&gt;For unambiguous script identification &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.iso.org&#x2F;standard&#x2F;81905.html&quot;&gt;ISO 15924&lt;&#x2F;a&gt; assigns unambiguous four-letter codes to every script in wide enough use that they are relevant. The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.unicode.org&#x2F;iso15924&#x2F;&quot;&gt;ISO 15924 code list&lt;&#x2F;a&gt; is maintained by the Unicode consortium.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;regions-and-countries&quot;&gt;Regions and Countries&lt;&#x2F;h2&gt;
&lt;p&gt;For identifying countries and their subdivision there is ISO 3166 (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.iso.org&#x2F;iso-3166-country-codes.html&quot;&gt;ISO 3166 Popular  standards article&lt;&#x2F;a&gt;). The Machine readable lists are a paid service, but there is a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.iso.org&#x2F;obp&#x2F;ui&#x2F;#iso:pub:PUB500001:en&quot;&gt;human readable list of the ISO 3166-1 codes&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There is a useful &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;List_of_ISO_3166_country_codes&quot;&gt;List of ISO 3166 country codes on Wikipedia&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.iana.org&#x2F;assignments&#x2F;language-subtag-registry&#x2F;language-subtag-registry&quot;&gt;BCP 47 language subtag registry&lt;&#x2F;a&gt; also maps some of the Alpha-2 codes to English country names.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;places-with-transportation-infrastructure&quot;&gt;Places with Transportation Infrastructure&lt;&#x2F;h3&gt;
&lt;p&gt;For places with Transportation Infrastructure there is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;unece.org&#x2F;trade&#x2F;uncefact&#x2F;unlocode&quot;&gt;UN&#x2F;LOCODE&lt;&#x2F;a&gt;. It also has a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;service.unece.org&#x2F;trade&#x2F;locode&#x2F;2023-1%20SubdivisionCodes.htm&quot;&gt;list of Countries and their subdivisions&lt;&#x2F;a&gt; and a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;unece.org&#x2F;trade&#x2F;cefact&#x2F;unlocode-code-list-country-and-territory&quot;&gt;list of Countries and places with transportation infrastructure&lt;&#x2F;a&gt; relevant for larger scale trading.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;business&quot;&gt;Business&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;identifying-currencies&quot;&gt;Identifying Currencies&lt;&#x2F;h3&gt;
&lt;p&gt;For unambiguous currency identification &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.iso.org&#x2F;standard&#x2F;64758.html&quot;&gt;ISO 4217&lt;&#x2F;a&gt; (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.iso.org&#x2F;iso-4217-currency-codes.html&quot;&gt;ISO 4217 Popular standards article&lt;&#x2F;a&gt;) specifies lists of current and historic currencies. The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.six-group.com&#x2F;en&#x2F;products-services&#x2F;financial-information&#x2F;data-standards.html&quot;&gt;currency registry&lt;&#x2F;a&gt; is maintained by Six Group Ltd.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Portable Shell-Scripting</title>
        <published>2024-05-17T00:00:00+00:00</published>
        <updated>2024-05-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/portable-shell/"/>
        <id>https://slatecave.net/notebook/portable-shell/</id>
        
        <summary type="text">Knowledge for writing portable shell scripts that work across operating system distributions.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/portable-shell/">&lt;h2 id=&quot;what-makes-your-shellscripts-break&quot;&gt;What makes your shellscripts break?&lt;&#x2F;h2&gt;
&lt;p&gt;Before outlining how shellscripts can be made portable … why do they break?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;This is a &quot;you should consider&quot;, not a &quot;you have to&quot;:&lt;&#x2F;b&gt; Depending on the purpose and intended audience of your script not all of the following may apply.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;not-my-shell&quot;&gt;Not my Shell&lt;&#x2F;h3&gt;
&lt;p&gt;Not everyone has the same shell installed. Different systems come with different shells installed, mainly either &lt;code&gt;bash&lt;&#x2F;code&gt; or &lt;code&gt;dash&lt;&#x2F;code&gt; (though there are other shells for running scripts you might run across).&lt;&#x2F;p&gt;
&lt;p&gt;While the language those shells use looks the same every shell has some extensions to make live a bit easier. Problem is that using those extensions aren&#x27;t standardised and only work in one specific shell, that might not be installed on your target system.&lt;&#x2F;p&gt;
&lt;p&gt;This problem usually happens with bash extensions, they are also known as &lt;i&gt;bashisms&lt;&#x2F;i&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;not-my-coreutils&quot;&gt;Not my Coreutils&lt;&#x2F;h3&gt;
&lt;p&gt;The coreutils are those programs everyone uses all the time when writing more advanced shell scripts. Examples are &lt;code&gt;grep&lt;&#x2F;code&gt;, &lt;code&gt;sed&lt;&#x2F;code&gt;, &lt;code&gt;ls&lt;&#x2F;code&gt; and so on.&lt;&#x2F;p&gt;
&lt;p&gt;Well known implementations are the GNU coreutils, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.busybox.net&#x2F;&quot;&gt;busybox&lt;&#x2F;a&gt; or Apples implementation for MacOS.&lt;&#x2F;p&gt;
&lt;p&gt;Like the shells different implementations of those commands have different extensions. Using those extensions will break your script in unexpected ways when running on a different system that has a different set or version of Coreutils installed.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note on &lt;code&gt;awk&lt;&#x2F;code&gt;:&lt;&#x2F;b&gt; &lt;code&gt;awk&lt;&#x2F;code&gt; usually isn&#x27;t part of the coreutils package, but the problems applies. See &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;invisible-island.net&#x2F;mawk&#x2F;&quot;&gt;mawk&lt;&#x2F;a&gt; for a less well known, but still popular implementation.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;not-my-environment&quot;&gt;Not my Environment&lt;&#x2F;h3&gt;
&lt;p&gt;When writing scripts it is easy to assume that &lt;code&gt;&#x2F;tmp&lt;&#x2F;code&gt; is where temporary files go, &lt;code&gt;&#x2F;run&#x2F;user&#x2F;$UID&lt;&#x2F;code&gt; houses session files and &lt;code&gt;~&#x2F;.cache&#x2F;&lt;&#x2F;code&gt; is a sensible cache directory … &lt;strong&gt;all of those are wrong&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;That is how your systems work, maybe your friends system works like this too, but some people out there use different file system layout for very good reasons (performance, security, etc.).&lt;&#x2F;p&gt;
&lt;p&gt;Those people are very very glad when everything uses standardised environment variables like &lt;code&gt;TMPDIR&lt;&#x2F;code&gt;, &lt;code&gt;XDG_RUNTIME_DIR&lt;&#x2F;code&gt; and &lt;code&gt;XDG_CACHE_HOME&lt;&#x2F;code&gt;. (And you might become such a someone faster than you expect!)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;not-my-program&quot;&gt;Not my Program&lt;&#x2F;h3&gt;
&lt;p&gt;In rare cases programs have different names on different distributions.&lt;&#x2F;p&gt;
&lt;p&gt;This can&#x27;t be easily avoided by some standardised mechanism and is something one should generally be aware of.&lt;&#x2F;p&gt;
&lt;p&gt;One example is &lt;code&gt;readlink -f&lt;&#x2F;code&gt; to canonicalize filenames, except for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.freebsd.org&#x2F;cgi&#x2F;man.cgi?query=readlink&quot;&gt;FreeBSD readlink&lt;&#x2F;a&gt;, where that option sets a formatting template.&lt;&#x2F;p&gt;
&lt;p&gt;Another Example of this is the Tesseract OCR engine CLI-tool which is called &lt;code&gt;tesseract&lt;&#x2F;code&gt;, except on Void Linux where it is called &lt;code&gt;tesseract-ocr&lt;&#x2F;code&gt; because they already had a different program packaged that is called &lt;code&gt;tesseract&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For other programs that aren&#x27;t part of the directories in &lt;code&gt;PATH&lt;&#x2F;code&gt; one might run into this problem sooner.&lt;&#x2F;p&gt;
&lt;p&gt;When implementing something like this: Use sensible defaults, allow for working around any issues and document how the mechanism works.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;not-my-display-server&quot;&gt;Not my Display Server&lt;&#x2F;h3&gt;
&lt;p&gt;Do you run Xorg, Wayland, only a TTY (over SSH) or something else?How many of your tools rely on that specific display protocol being available?&lt;&#x2F;p&gt;
&lt;p&gt;Make sure you know the limits of your tools and implement appropriate mechanisms to switch tools when necessary.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;posix-shell&quot;&gt;POSIX Shell&lt;&#x2F;h2&gt;
&lt;p&gt;POSIX is a great thing, it standardises (amongst other things) shell syntax and command line utilities.&lt;&#x2F;p&gt;
&lt;p&gt;If you only use what POSIX defines in a way it is defined by POSIX your shellscripts will work across a wider range of systems without you having to do any extra work.&lt;&#x2F;p&gt;
&lt;p&gt;The official standard document is the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;publications.opengroup.org&#x2F;standards&#x2F;unix&#x2F;t101&quot;&gt;Single UNIX® Specification, Version 4, 2018 Edition&lt;&#x2F;a&gt; published by the Open Group, but that is behind a login.&lt;&#x2F;p&gt;
&lt;p&gt;Luckily Wikipedia has a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;List_of_POSIX_commands&quot;&gt;List of POSIX commands&lt;&#x2F;a&gt;, where for almost every listed command the first external link on the command specific page is the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pubs.opengroup.org&#x2F;onlinepubs&#x2F;9699919799&#x2F;&quot;&gt;official POSIX specification in HTML&lt;&#x2F;a&gt; (no login needed).&lt;&#x2F;p&gt;
&lt;p&gt;Starting a shellscript with &lt;code&gt;#!&#x2F;bin&#x2F;sh&lt;&#x2F;code&gt; usually indicates that it is using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pubs.opengroup.org&#x2F;onlinepubs&#x2F;9699919799&#x2F;utilities&#x2F;V3_chap02.html&quot;&gt;POSIX compatible shell syntax&lt;&#x2F;a&gt;. In practice this means you just write normal shell code but watch out to avoid some shell-specific features. (See Linters)&lt;&#x2F;p&gt;
&lt;p&gt;POSIX also specifies some &lt;a href=&quot;&#x2F;notebook&#x2F;env&#x2F;#linux-and-posix&quot;&gt;Environment Variables&lt;&#x2F;a&gt; that can be used to find out the right thing to do, like where to put temporary files.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xdg-freedesktop-org&quot;&gt;XDG - freedesktop.org&lt;&#x2F;h2&gt;
&lt;p&gt;If you are interacting with user folders or the desktop try to use the &lt;a href=&quot;&#x2F;notebook&#x2F;env&#x2F;#xdg-freedesktop-org&quot;&gt;Freedesktop specified Environment Variables&lt;&#x2F;a&gt;. Also 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;, while not perfect provide ready to use implementations of commonly needed functionality, most prominent opening files.&lt;&#x2F;p&gt;
&lt;p&gt;Also see my blog-posts on the xdg-utils: &lt;a href=&quot;&#x2F;blog&#x2F;xdg-open-under-the-hood&#x2F;&quot;&gt;&quot;It just opens files&quot; - xdg-open under the hood&lt;&#x2F;a&gt; and &lt;a href=&quot;&#x2F;blog&#x2F;xdg-mime&#x2F;&quot;&gt;xdg-mime: Mapping Files to Applications taken apart&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;linters&quot;&gt;Linters&lt;&#x2F;h2&gt;
&lt;p&gt;Linters can help you avoid some not very obvious mistakes and compatibility problems.&lt;&#x2F;p&gt;
&lt;p&gt;For shell scripts the go to linter is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;shellcheck.net&quot;&gt;shellcheck&lt;&#x2F;a&gt; (your favourite Linux distribution probably has it packaged).&lt;&#x2F;p&gt;
&lt;p&gt;If you need to lint an &lt;code&gt;awk&lt;&#x2F;code&gt; script&#x2F;command: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;gawk&quot;&gt;&lt;code&gt;gawk&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; has the &lt;code&gt;--lint&lt;&#x2F;code&gt; and &lt;code&gt;--lint-old&lt;&#x2F;code&gt; options (both will run the command) that make gawk tell you when it stumbles across something you might want to avoid happening. The &lt;code&gt;--lint-old&lt;&#x2F;code&gt; option is also great for catching constructs that only work in &lt;code&gt;gawk&lt;&#x2F;code&gt; but not other implementations.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;declare-your-dependencies&quot;&gt;Declare your Dependencies&lt;&#x2F;h2&gt;
&lt;p&gt;When using non-POSIX tools, try to document what you are using and why.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Links to Elsewhere - The Slatecave Railstation</title>
        <published>2024-04-25T00:00:00+00:00</published>
        <updated>2024-04-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/railstation/"/>
        <id>https://slatecave.net/railstation/</id>
        
        <content type="html" xml:base="https://slatecave.net/railstation/">&lt;p&gt;Just like a real Railstation this page gets you to a lot places, the only differences are that you don&#x27;t need a ticket and don&#x27;t have to wait.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;arrivals&quot;&gt;Arrivals&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;i&gt;If you&#x27;ve arrived here from a different site:&lt;&#x2F;i&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Welcome to the Slatecave!&lt;&#x2F;p&gt;
&lt;p&gt;You can explore this site, when you&#x27;re done there is a link at the bottom of each page leading back here to the Railstation.&lt;&#x2F;p&gt;
&lt;p&gt;You&#x27;ll find things about Linux, programming, standards and more here.&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;&quot; &gt;
		Explore
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;friendly-sites&quot;&gt;Friendly Sites&lt;&#x2F;h2&gt;
&lt;p&gt;Run by awesome people:&lt;&#x2F;p&gt;

	

&lt;ul class=&quot;link-list&quot;&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;seirdy.one&quot;&gt;seirdy.one by Seirdy&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;volpeon.ink&quot;&gt;volpeon.ink by Volpeon&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;adrian.geek.nz&quot;&gt;adrian.geek.nz by Adrian Cochrane&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolderg.xyz&quot;&gt;smolderg.xyz by Therra&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;steffo.blog&quot;&gt;steffo.blog by Steffo&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;finnley.dev&quot;&gt;finnley.dev by Finnley&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;envs.net&quot;&gt;envs.net, a Pubnix by creme and others&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p&gt;&lt;small&gt;This list is not complete for the simple reason of having to fit this list into some finite space.&lt;&#x2F;small&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note of Thank you!&lt;&#x2F;b&gt; Both &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;seirdy.one&quot;&gt;seirdy.one&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;volpeon.ink&quot;&gt;volpeon.ink&lt;&#x2F;a&gt; inspired this website when it was created and continue to do so. The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;adrian.geek.nz&#x2F;docs.html&quot;&gt;&quot;How does it work?&quot; series&lt;&#x2F;a&gt; is one of the main reasons for the taking software apart type posts on my blog.&lt;&#x2F;p&gt;
&lt;p&gt;Run by on-profit Organisations, providing useful services:&lt;&#x2F;p&gt;

	

&lt;ul class=&quot;link-list&quot;&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&quot;&gt;Codeberg.org - Codeforge hosting&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;disroot.org&quot;&gt;Disroot.org - Web services&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;wall-of-badges&quot;&gt;Wall of Badges&lt;&#x2F;h2&gt;
&lt;p&gt;Not a wall right now, but one has to start somewhere.&lt;&#x2F;p&gt;
&lt;p class=&quot;badge-wall&quot;&gt;

&lt;a class=&quot;webbadge &quot; href=&quot;https:&amp;#x2F;&amp;#x2F;seirdy.one&quot;&gt;&lt;img class=&quot;webbadge-img&quot; src=&quot;&#x2F;resources&#x2F;badges&#x2F;88x31_seirdy-one.png&quot; alt=&quot;Seirdy, simple white on black text next to a :; icon.&quot;&#x2F;&gt;&lt;&#x2F;a&gt;



&lt;a class=&quot;webbadge &quot; href=&quot;https:&amp;#x2F;&amp;#x2F;volpeon.ink&quot;&gt;&lt;img class=&quot;webbadge-img&quot; src=&quot;&#x2F;resources&#x2F;badges&#x2F;volpeon-ink.svg&quot; alt=&quot;A mysterious wyvern in a forest, the text says volpeon&quot;&#x2F;&gt;&lt;&#x2F;a&gt;



&lt;a class=&quot;webbadge &quot; href=&quot;https:&amp;#x2F;&amp;#x2F;codeberg.org&quot;&gt;&lt;img class=&quot;webbadge-img&quot; src=&quot;&#x2F;resources&#x2F;badges&#x2F;88x31_codeberg-org.png&quot; alt=&quot;Codeberg, text next to a codeberg icon.&quot;&#x2F;&gt;&lt;&#x2F;a&gt;



&lt;a class=&quot;webbadge &quot; href=&quot;https:&amp;#x2F;&amp;#x2F;smolderg.xyz&quot;&gt;&lt;img class=&quot;webbadge-img&quot; src=&quot;&#x2F;resources&#x2F;badges&#x2F;88x31_smolderg-xyz-by-liah.png&quot; alt=&quot;Smol derg, big heart.&quot;&#x2F;&gt;&lt;&#x2F;a&gt;



&lt;a class=&quot;webbadge &quot; href=&quot;https:&amp;#x2F;&amp;#x2F;finnley.dev&quot;&gt;&lt;img class=&quot;webbadge-img&quot; src=&quot;&#x2F;resources&#x2F;badges&#x2F;88x31_finnley-dev.png&quot; alt=&quot;A fox emoji, but it&amp;#x27;s a colorful arctic fox. The text says Finnley.dev&quot;&#x2F;&gt;&lt;&#x2F;a&gt;



&lt;a class=&quot;webbadge &quot; href=&quot;https:&amp;#x2F;&amp;#x2F;theresnotime.co.uk&quot;&gt;&lt;img class=&quot;webbadge-img&quot; src=&quot;&#x2F;resources&#x2F;badges&#x2F;88x31_theresnotime-co-uk.png&quot; alt=&quot;A fox emoji, with the text TheresNoTime Below, the background is a non-binary flag.&quot;&#x2F;&gt;&lt;&#x2F;a&gt;



&lt;a class=&quot;webbadge &quot; href=&quot;https:&amp;#x2F;&amp;#x2F;sdomi.pl&quot;&gt;&lt;img class=&quot;webbadge-img&quot; src=&quot;&#x2F;resources&#x2F;badges&#x2F;88x31_sdomi-pl.png&quot; alt=&quot;A text saying sdomi next to a witch hat, the background was procedurally generated.&quot;&#x2F;&gt;&lt;&#x2F;a&gt;



&lt;a class=&quot;webbadge &quot; href=&quot;https:&amp;#x2F;&amp;#x2F;steffo.blog&quot;&gt;&lt;img class=&quot;webbadge-img&quot; src=&quot;&#x2F;resources&#x2F;badges&#x2F;88x31_steffo-blog.png&quot; alt=&quot;It says Steffo, the blue text has draconic horns and a fluffy tail is also visible.&quot;&#x2F;&gt;&lt;&#x2F;a&gt;



&lt;a class=&quot;webbadge &quot; href=&quot;https:&amp;#x2F;&amp;#x2F;hexaitos.com&quot;&gt;&lt;img class=&quot;webbadge-img&quot; src=&quot;&#x2F;resources&#x2F;badges&#x2F;88x31_hexaitos-com.png&quot; alt=&quot;A fluffy velocirator looking twowards some text that says hexaitos.com&quot;&#x2F;&gt;&lt;&#x2F;a&gt;



&lt;a class=&quot;webbadge vertical-webbadge&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;lf-net.org&quot;&gt;&lt;img class=&quot;webbadge-img&quot; src=&quot;&#x2F;resources&#x2F;badges&#x2F;31x88_lf-net-org.png&quot; alt=&quot;A stylized LF integrated into a QR-code scaffold, followed by the text &amp;#x27;Enter the next line&amp;#x27;, followed by a laptop showing a command prompt.&quot;&#x2F;&gt;&lt;&#x2F;a&gt;



&lt;a class=&quot;webbadge &quot; href=&quot;https:&amp;#x2F;&amp;#x2F;jak2k.eu&quot;&gt;&lt;img class=&quot;webbadge-img&quot; src=&quot;&#x2F;resources&#x2F;badges&#x2F;88x31_jak2k-eu.png&quot; alt=&quot;It says JAK2K next to a green orb on a very green badge.&quot;&#x2F;&gt;&lt;&#x2F;a&gt;



&lt;a class=&quot;webbadge &quot; href=&quot;https:&amp;#x2F;&amp;#x2F;servfail.network&quot;&gt;&lt;img class=&quot;webbadge-img&quot; src=&quot;&#x2F;resources&#x2F;badges&#x2F;88x31_servfail-network.png&quot; alt=&quot;7-Segment style text that says SERVFAIL DNS and a purple pentagram with candles on its corers that is the servfail logo.&quot;&#x2F;&gt;&lt;&#x2F;a&gt;



&lt;a class=&quot;webbadge &quot; href=&quot;https:&amp;#x2F;&amp;#x2F;entropia.de&amp;#x2F;GPN23&quot;&gt;&lt;img class=&quot;webbadge-img&quot; src=&quot;&#x2F;resources&#x2F;badges&#x2F;88x31_gpn23_hidden_pattern.png&quot; alt=&quot;GPN23, Hidden Pattern(s)&quot;&#x2F;&gt;&lt;&#x2F;a&gt;



&lt;a class=&quot;webbadge &quot; href=&quot;https:&amp;#x2F;&amp;#x2F;events.ccc.de&amp;#x2F;congress&amp;#x2F;2025&amp;#x2F;hub&amp;#x2F;&quot;&gt;&lt;img class=&quot;webbadge-img&quot; src=&quot;&#x2F;resources&#x2F;badges&#x2F;88x31_39c3_violet.png&quot; alt=&quot;39C3&quot;&#x2F;&gt;&lt;&#x2F;a&gt;



&lt;a class=&quot;webbadge &quot; href=&quot;https:&amp;#x2F;&amp;#x2F;entropia.de&amp;#x2F;GPN24&quot;&gt;&lt;img class=&quot;webbadge-img&quot; src=&quot;&#x2F;resources&#x2F;badges&#x2F;88x31_gpn24.png&quot; alt=&quot;GPN24, Gulasch at the scale of chaos.&quot;&#x2F;&gt;&lt;&#x2F;a&gt;



&lt;a class=&quot;webbadge &quot; href=&quot;&amp;#x2F;about&amp;#x2F;#legal-foo&quot;&gt;&lt;img class=&quot;webbadge-img&quot; src=&quot;&#x2F;resources&#x2F;badges&#x2F;cc-by-nc-sa.eu.svg&quot; alt=&quot;CC BY-NC-SA&quot;&#x2F;&gt;&lt;&#x2F;a&gt;


&lt;&#x2F;p&gt;
&lt;h2 id=&quot;webrings&quot;&gt;Webrings&lt;&#x2F;h2&gt;
&lt;section class=&quot;webring-widget&quot;&gt;
	
	&lt;h3 id=&quot;envs-webring&quot;&gt;Envs Webring&lt;&#x2F;h3&gt;
	&lt;p&gt;A webring for every creature on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;envs.net&quot;&gt;envs.net&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

	&lt;ul class=&quot;link-list row fill&quot;&gt;
		&lt;li&gt;&lt;a class=&quot;decoration-action-previous&quot; href=&quot;https:&#x2F;&#x2F;envs.net&#x2F;ring&#x2F;?action=prev&amp;amp;me=slatian&quot;&gt;Previous&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
		
		&lt;li&gt;&lt;a class=&quot;decoration-action-shuffle&quot; href=&quot;https:&#x2F;&#x2F;envs.net&#x2F;ring&#x2F;?action=random&amp;amp;me=slatian&quot;&gt;Random&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
		
		
		&lt;li&gt;&lt;a class=&quot;decoration-action-next&quot; href=&quot;https:&#x2F;&#x2F;envs.net&#x2F;ring&#x2F;?action=next&amp;amp;me=slatian&quot;&gt;Next&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
	&lt;&#x2F;ul&gt;
	&lt;p class=&quot;button-paragraph&quot;&gt;&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;envs.net&amp;#x2F;ring&amp;#x2F;&quot; class=&quot;decoration-destination-info&quot;&gt;Envs webring homepage&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;section&gt;
&lt;h2 id=&quot;linking-to-the-slatecave&quot;&gt;Linking to the Slatecave&lt;&#x2F;h2&gt;
&lt;p&gt;If you have a similar page and want to link back to the slatecave: Thank you!&lt;&#x2F;p&gt;
&lt;p&gt;You can use the following badges if you want to:&lt;&#x2F;p&gt;
&lt;p&gt;

&lt;a class=&quot;webbadge &quot; href=&quot;https:&amp;#x2F;&amp;#x2F;slatecave.net&amp;#x2F;railstation&quot;&gt;&lt;img class=&quot;webbadge-img&quot; src=&quot;&#x2F;resources&#x2F;badges&#x2F;88x31_slatecave-net.png&quot; alt=&quot;slatecave.net, Behind the text there is a drawing of the head of a floofy dragon.&quot;&#x2F;&gt;&lt;&#x2F;a&gt;



&lt;a class=&quot;webbadge &quot; href=&quot;https:&amp;#x2F;&amp;#x2F;slatecave.net&amp;#x2F;railstation&quot;&gt;&lt;img class=&quot;webbadge-img&quot; src=&quot;&#x2F;resources&#x2F;badges&#x2F;88x31_slatecave-net_sleeping-slatian.png&quot; alt=&quot;A sleeping drgon drawn in warm, flat color lines on a dark background, the outline has a slight redish to orangely pastel tint&quot;&#x2F;&gt;&lt;&#x2F;a&gt;


&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;about&#x2F;me&#x2F;#contact&quot;&gt;Let me know&lt;&#x2F;a&gt; about your link, I&#x27;ll probably give you a backlink in the wall of badges above.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note on Hotlinking:&lt;&#x2F;b&gt; If you want to hotlink (load the image from slatecave.net) feel free to do so. But please consider serving it from your own webserver.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Image credits:&lt;&#x2F;b&gt; Image credits for the dragon in the background of the first badge go to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;Mollin_Art&quot;&gt;Mollin&lt;&#x2F;a&gt; (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mollincomms.carrd.co&#x2F;&quot;&gt;website&lt;&#x2F;a&gt;), it is derived from the sketch of a dragon bean I&#x27;ve commissioned. (You don&#x27;t have to give credits, when using the button)&lt;&#x2F;p&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>ATmega328P and the Arduino UNO R3</title>
        <published>2024-03-31T00:00:00+00:00</published>
        <updated>2024-03-31T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/atmega328p/"/>
        <id>https://slatecave.net/notebook/atmega328p/</id>
        
        <summary type="text">Pinout of the ATmega328P and how it maps to the Arduino UNO R3.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/atmega328p/">&lt;p&gt;Pinout of the ATmega328P and how it maps to the Arduino UNO R3.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; According to the Datasheet most of this Information also applies to the ATmega328, ATmega168(P)A, ATmega88(P)A and ATmega48(P)A.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pinout-table&quot;&gt;Pinout Table&lt;&#x2F;h2&gt;
&lt;p&gt;The following Table applies to the 28 Pin DIP Packaged ATmega328. The Arduino Pin mapping is based on the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.arduino.cc&#x2F;hardware&#x2F;uno-rev3&#x2F;&quot;&gt;Arduino UNO R3&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;table&gt;
	&lt;tr&gt;
		&lt;th&gt;Pin&lt;&#x2F;th&gt;
		&lt;th&gt;Name&lt;&#x2F;th&gt;
		&lt;th&gt;Ardu&amp;shy;ino Pin&lt;&#x2F;th&gt;
		&lt;th&gt;PWM&lt;&#x2F;th&gt;
		&lt;th&gt;PC&amp;shy;INT&lt;&#x2F;th&gt;
		&lt;th&gt;Bus&lt;&#x2F;th&gt;
		&lt;th&gt;Extra&lt;&#x2F;th&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;1&lt;&#x2F;td&gt;
		&lt;td&gt;PC6&lt;&#x2F;td&gt;
		&lt;td&gt;Reset&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;14&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;Reset&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;2&lt;&#x2F;td&gt;
		&lt;td&gt;PD0&lt;&#x2F;td&gt;
		&lt;td&gt;0&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;16&lt;&#x2F;td&gt;
		&lt;td&gt;Serial RX&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;3&lt;&#x2F;td&gt;
		&lt;td&gt;PD1&lt;&#x2F;td&gt;
		&lt;td&gt;1&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;17&lt;&#x2F;td&gt;
		&lt;td&gt;Serial TX&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;4&lt;&#x2F;td&gt;
		&lt;td&gt;PD2&lt;&#x2F;td&gt;
		&lt;td&gt;2&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;18&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;Ext. Interrupt 0&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;5&lt;&#x2F;td&gt;
		&lt;td&gt;PD3&lt;&#x2F;td&gt;
		&lt;td&gt;3&lt;&#x2F;td&gt;
		&lt;td&gt;OC2B&lt;&#x2F;td&gt;
		&lt;td&gt;19&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;Ext. Interrupt 1&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;6&lt;&#x2F;td&gt;
		&lt;td&gt;PD4&lt;&#x2F;td&gt;
		&lt;td&gt;4&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;20&lt;&#x2F;td&gt;
		&lt;td&gt;Serial XCK&lt;&#x2F;td&gt;
		&lt;td&gt;Timer 0&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;7&lt;&#x2F;td&gt;
		&lt;td&gt;VCC&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;8&lt;&#x2F;td&gt;
		&lt;td&gt;GND&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;9&lt;&#x2F;td&gt;
		&lt;td&gt;PB6&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;6&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;XTAL1, TOSC1&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;10&lt;&#x2F;td&gt;
		&lt;td&gt;PB7&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;7&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;XTAL2, TOSC2&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;11&lt;&#x2F;td&gt;
		&lt;td&gt;PD5&lt;&#x2F;td&gt;
		&lt;td&gt;5&lt;&#x2F;td&gt;
		&lt;td&gt;OC0B&lt;&#x2F;td&gt;
		&lt;td&gt;21&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;Timer 1&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;12&lt;&#x2F;td&gt;
		&lt;td&gt;PD6&lt;&#x2F;td&gt;
		&lt;td&gt;6&lt;&#x2F;td&gt;
		&lt;td&gt;OC0A&lt;&#x2F;td&gt;
		&lt;td&gt;22&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;AIN0&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;13&lt;&#x2F;td&gt;
		&lt;td&gt;PD7&lt;&#x2F;td&gt;
		&lt;td&gt;7&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;23&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;AIN1&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;14&lt;&#x2F;td&gt;
		&lt;td&gt;PB0&lt;&#x2F;td&gt;
		&lt;td&gt;8&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;0&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;CLKO, ICP1&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;15&lt;&#x2F;td&gt;
		&lt;td&gt;PB1&lt;&#x2F;td&gt;
		&lt;td&gt;9&lt;&#x2F;td&gt;
		&lt;td&gt;OC1A&lt;&#x2F;td&gt;
		&lt;td&gt;1&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;16&lt;&#x2F;td&gt;
		&lt;td&gt;PB2&lt;&#x2F;td&gt;
		&lt;td&gt;10&lt;&#x2F;td&gt;
		&lt;td&gt;OC1B&lt;&#x2F;td&gt;
		&lt;td&gt;2&lt;&#x2F;td&gt;
		&lt;td&gt;SPI SS&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;17&lt;&#x2F;td&gt;
		&lt;td&gt;PB3&lt;&#x2F;td&gt;
		&lt;td&gt;11&lt;&#x2F;td&gt;
		&lt;td&gt;OC2A&lt;&#x2F;td&gt;
		&lt;td&gt;3&lt;&#x2F;td&gt;
		&lt;td&gt;SPI MOSI&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;18&lt;&#x2F;td&gt;
		&lt;td&gt;PB4&lt;&#x2F;td&gt;
		&lt;td&gt;12&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;4&lt;&#x2F;td&gt;
		&lt;td&gt;SPI MISO&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;19&lt;&#x2F;td&gt;
		&lt;td&gt;PB5&lt;&#x2F;td&gt;
		&lt;td&gt;13&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;5&lt;&#x2F;td&gt;
		&lt;td&gt;SPI SCK&lt;&#x2F;td&gt;
		&lt;td&gt;Builtin LED (Arduino)&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;20&lt;&#x2F;td&gt;
		&lt;td&gt;AVCC&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;Analog VCC&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;21&lt;&#x2F;td&gt;
		&lt;td&gt;AREF&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;Analog Reference&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;22&lt;&#x2F;td&gt;
		&lt;td&gt;GND&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;23&lt;&#x2F;td&gt;
		&lt;td&gt;PC0&lt;&#x2F;td&gt;
		&lt;td&gt;14 (A0)&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;8&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;ADC0&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;24&lt;&#x2F;td&gt;
		&lt;td&gt;PC1&lt;&#x2F;td&gt;
		&lt;td&gt;15 (A1)&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;9&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;ADC1&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;25&lt;&#x2F;td&gt;
		&lt;td&gt;PC2&lt;&#x2F;td&gt;
		&lt;td&gt;16 (A2)&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;10&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;ADC2&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;26&lt;&#x2F;td&gt;
		&lt;td&gt;PC3&lt;&#x2F;td&gt;
		&lt;td&gt;17 (A3)&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;11&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;ADC3&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;27&lt;&#x2F;td&gt;
		&lt;td&gt;PC4&lt;&#x2F;td&gt;
		&lt;td&gt;18 (A4)&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;12&lt;&#x2F;td&gt;
		&lt;td&gt;I²C SDA&lt;&#x2F;td&gt;
		&lt;td&gt;ADC4&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

	&lt;tr&gt;
		&lt;td&gt;28&lt;&#x2F;td&gt;
		&lt;td&gt;PC5&lt;&#x2F;td&gt;
		&lt;td&gt;19 (A5)&lt;&#x2F;td&gt;
		&lt;td&gt;&lt;&#x2F;td&gt;
		&lt;td&gt;13&lt;&#x2F;td&gt;
		&lt;td&gt;I²C SCL&lt;&#x2F;td&gt;
		&lt;td&gt;ADC5&lt;&#x2F;td&gt;
	&lt;&#x2F;tr&gt;

&lt;&#x2F;table&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


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

	
		&lt;dt&gt;
		Pin
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Physical Pin on the ATmega328P DIP Package.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Name
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The Pin name in the ATmega328 Datasheet.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Arduino Pin
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The Pin number&#x2F;name assigned by Arduino.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		PWM
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Which oscillator the Pin is connected to. If it is connected to one it supports PWM.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		PCINT
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The Pin Change Interrupt number assigned to the pin.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Bus
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Bus protocol Hardware the pin is connected to.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Extra
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Any extra features the Pin might have or a short explanation.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;&lt;b&gt;Note on SPI Nomenclature:&lt;&#x2F;b&gt; Instead of the old &quot;Master&quot; and &quot;Slave&quot; terminology on SPI you can use &lt;i&gt;Main&lt;&#x2F;i&gt; and &lt;i&gt;Secondary&lt;&#x2F;i&gt; while keeping all the old Acronyms. 😉&lt;&#x2F;p&gt;
&lt;h2 id=&quot;barebones-arduino-uno-r3-compatible&quot;&gt;Barebones Arduino UNO R3 compatible&lt;&#x2F;h2&gt;
&lt;p&gt;Maybe you&#x27;ve built something awesome and now you want to get it off that bulky Arduino board and for some reason the individual parts are cheaper than a tiny Arduino compatible board (they used to be, guess who has a stash of ATmega328P chips).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;needed-parts&quot;&gt;Needed Parts&lt;&#x2F;h3&gt;
&lt;p&gt;To build a barebones Arduino UNO R3 compatible you need:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;One ATmega328P&lt;&#x2F;li&gt;
&lt;li&gt;Something to flash the Arduino bootloader&lt;&#x2F;li&gt;
&lt;li&gt;Power circuit:
&lt;ul&gt;
&lt;li&gt;A stable 5V Power supply (I.e. from USB)&lt;&#x2F;li&gt;
&lt;li&gt;Two optional 100nF Capacitors&lt;&#x2F;li&gt;
&lt;li&gt;One optional 10μH Inductor&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Clock Circuit:
&lt;ul&gt;
&lt;li&gt;One 16MHz Crystal&lt;&#x2F;li&gt;
&lt;li&gt;Two 22pf Capacitors&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Reset Circuit
&lt;ul&gt;
&lt;li&gt;One button (open by default)&lt;&#x2F;li&gt;
&lt;li&gt;One 10KΩ (roughly) Resistor&lt;&#x2F;li&gt;
&lt;li&gt;One optional 100nF Capacitor&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;building-the-circuit&quot;&gt;Building the circuit&lt;&#x2F;h3&gt;

	



	


&lt;figure&gt;


&lt;svg original-width=&quot;125mm&quot; original-height=&quot;100mm&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 125 100&quot; xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot; xmlns:cc=&quot;http:&#x2F;&#x2F;creativecommons.org&#x2F;ns#&quot; xmlns:dc=&quot;http:&#x2F;&#x2F;purl.org&#x2F;dc&#x2F;elements&#x2F;1.1&#x2F;&quot; xmlns:rdf=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;1999&#x2F;02&#x2F;22-rdf-syntax-ns#&quot;&gt;
	&lt;title&gt;Schematic: Barebones ATmega328P Circuit&lt;&#x2F;title&gt;
	&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot;&gt;
		&lt;path d=&quot;m60 20v75h40v-75h-15a5 5 0 0 1-5 5 5 5 0 0 1-5-5z&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
		&lt;path d=&quot;m35 10-5-5-5 5z&quot; stroke-width=&quot;.5&quot;&#x2F;&gt;
		&lt;path d=&quot;m120 10-5-5-5 5z&quot; stroke-width=&quot;.5&quot;&#x2F;&gt;
	&lt;&#x2F;g&gt;
	&lt;g transform=&quot;translate(1.22e-6 -30)&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;.5&quot;&gt;
		&lt;path d=&quot;m20 120h10&quot;&#x2F;&gt;
		&lt;path d=&quot;m22 122h6&quot;&#x2F;&gt;
		&lt;path d=&quot;m24 124h2&quot;&#x2F;&gt;
		&lt;path d=&quot;m25 120v-5&quot;&#x2F;&gt;
	&lt;&#x2F;g&gt;
	&lt;g transform=&quot;translate(85 -30)&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;.5&quot;&gt;
		&lt;path d=&quot;m20 120h10&quot;&#x2F;&gt;
		&lt;path d=&quot;m22 122h6&quot;&#x2F;&gt;
		&lt;path d=&quot;m24 124h2&quot;&#x2F;&gt;
		&lt;path d=&quot;m25 120v-5&quot;&#x2F;&gt;
	&lt;&#x2F;g&gt;
	&lt;g fill=&quot;currentColor&quot; stroke-width=&quot;.265&quot;&gt;
		&lt;path d=&quot;m61.3 56.5h0.369l1.3-2.58v-0.33h-1.86v0.312h1.5v0.0227zm3.49-2.91h-0.369l1.07 2.91h0.364l1.07-2.91h-0.369l-0.864 2.45h-0.0341zm4.79 0.909c-0.108-0.597-0.585-0.949-1.17-0.949-0.744 0-1.28 0.574-1.28 1.49s0.54 1.49 1.28 1.49c0.585 0 1.06-0.352 1.17-0.949h-0.352c-0.0852 0.403-0.432 0.619-0.818 0.619-0.528 0-0.943-0.409-0.943-1.16s0.415-1.16 0.943-1.16c0.386 0 0.733 0.216 0.818 0.619zm2.91 0c-0.108-0.597-0.585-0.949-1.17-0.949-0.744 0-1.28 0.574-1.28 1.49s0.54 1.49 1.28 1.49c0.585 0 1.06-0.352 1.17-0.949h-0.352c-0.0852 0.403-0.432 0.619-0.818 0.619-0.528 0-0.943-0.409-0.943-1.16s0.415-1.16 0.943-1.16c0.386 0 0.733 0.216 0.818 0.619z&quot; removed-label=&quot;7 VCC&quot;&#x2F;&gt;
		&lt;path d=&quot;m62.1 61.6c0.581 0 0.986-0.339 0.989-0.812-0.0028-0.368-0.249-0.68-0.562-0.739v-0.017c0.273-0.071 0.452-0.338 0.455-0.648-0.0028-0.443-0.375-0.773-0.881-0.773-0.511 0-0.884 0.33-0.881 0.773-0.0028 0.31 0.176 0.577 0.455 0.648v0.017c-0.32 0.0582-0.565 0.371-0.562 0.739-0.0028 0.473 0.402 0.812 0.989 0.812zm0-0.312c-0.399 0-0.645-0.205-0.642-0.517-0.0028-0.328 0.268-0.562 0.642-0.562 0.368 0 0.639 0.234 0.642 0.562-0.0028 0.312-0.249 0.517-0.642 0.517zm0-1.38c-0.318 0-0.543-0.199-0.54-0.5-0.0028-0.295 0.213-0.489 0.54-0.489 0.321 0 0.537 0.193 0.54 0.489-0.0028 0.301-0.227 0.5-0.54 0.5zm4.7-0.347h0.364c-0.108-0.562-0.58-0.949-1.19-0.949-0.746 0-1.27 0.574-1.27 1.49 0 0.92 0.523 1.49 1.3 1.49 0.693 0 1.19-0.462 1.19-1.18v-0.312h-1.11v0.312h0.767c-0.0099 0.523-0.354 0.852-0.847 0.852-0.54 0-0.955-0.409-0.955-1.16s0.415-1.16 0.932-1.16c0.42 0 0.706 0.237 0.83 0.619zm3.29-0.909h-0.347v2.29h-0.0284l-1.59-2.29h-0.341v2.91h0.352v-2.28h0.0284l1.59 2.28h0.341zm1.6 2.91c0.886 0 1.39-0.551 1.39-1.46 0-0.903-0.5-1.45-1.35-1.45h-0.938v2.91zm-0.545-0.312v-2.28h0.562c0.682 0 1.03 0.432 1.03 1.14 0 0.71-0.347 1.15-1.07 1.15z&quot; removed-label=&quot;8 GND&quot;&#x2F;&gt;
		&lt;path d=&quot;m62.2 23.6h-0.352l-0.727 0.483v0.358l0.71-0.472h0.017v2.54h0.352zm2.01 2.91h0.352v-1.14h0.636c0.0256 0 0.0497 0 0.0739-0.0014l0.614 1.14h0.409l-0.658-1.2c0.371-0.126 0.544-0.429 0.544-0.815 0-0.514-0.307-0.892-0.989-0.892h-0.983zm0.352-1.45v-1.14h0.619c0.472 0 0.653 0.23 0.653 0.58 0 0.349-0.182 0.562-0.648 0.562zm3.05 1.5c0.443 0 0.767-0.222 0.869-0.551l-0.324-0.0909c-0.0852 0.227-0.283 0.341-0.545 0.341-0.393 0-0.665-0.254-0.68-0.722h1.58v-0.142c0-0.812-0.483-1.09-0.938-1.09-0.591 0-0.983 0.466-0.983 1.14 0 0.67 0.386 1.12 1.02 1.12zm-0.68-1.31c0.0227-0.339 0.263-0.642 0.646-0.642 0.364 0 0.597 0.273 0.597 0.642zm3.64-0.426c-0.105-0.31-0.341-0.517-0.773-0.517-0.46 0-0.801 0.261-0.801 0.631 0 0.301 0.179 0.503 0.58 0.597l0.364 0.0852c0.22 0.0511 0.324 0.156 0.324 0.307 0 0.188-0.199 0.341-0.511 0.341-0.274 0-0.446-0.118-0.506-0.352l-0.318 0.0796c0.0781 0.371 0.384 0.568 0.83 0.568 0.507 0 0.852-0.277 0.852-0.653 0-0.304-0.19-0.496-0.58-0.591l-0.324-0.0795c-0.259-0.0639-0.375-0.151-0.375-0.318 0-0.188 0.199-0.324 0.466-0.324 0.293 0 0.413 0.162 0.472 0.312zm1.46 1.74c0.443 0 0.767-0.222 0.869-0.551l-0.324-0.0909c-0.0852 0.227-0.283 0.341-0.545 0.341-0.393 0-0.665-0.254-0.68-0.722h1.58v-0.142c0-0.812-0.483-1.09-0.938-1.09-0.591 0-0.983 0.466-0.983 1.14 0 0.67 0.386 1.12 1.02 1.12zm-0.68-1.31c0.0227-0.339 0.263-0.642 0.646-0.642 0.364 0 0.597 0.273 0.597 0.642zm3.04-0.915h-0.466v-0.523h-0.335v0.523h-0.33v0.284h0.33v1.36c0 0.381 0.307 0.562 0.591 0.562 0.125 0 0.205-0.0227 0.25-0.0398l-0.0682-0.301c-0.0284 0.0057-0.0739 0.017-0.148 0.017-0.148 0-0.29-0.0454-0.29-0.33v-1.27h0.466z&quot; removed-label=&quot;1 Reset&quot;&#x2F;&gt;
		&lt;path d=&quot;m62 63.6c-0.591-0.0071-0.972 0.438-0.972 0.972 0 0.562 0.415 0.966 0.915 0.966 0.301 0 0.557-0.148 0.716-0.386h0.0227c0 0.716-0.278 1.12-0.727 1.12-0.307 0-0.494-0.193-0.557-0.46h-0.347c0.0682 0.46 0.415 0.773 0.903 0.773 0.653 0 1.06-0.58 1.06-1.61 0-1.07-0.534-1.37-1.01-1.38zm0 0.312c0.364 0 0.631 0.312 0.631 0.653 0 0.347-0.284 0.659-0.642 0.659-0.358 0-0.619-0.29-0.619-0.653 0-0.364 0.273-0.659 0.631-0.659zm2.91-0.273h-0.415l0.938 1.45-0.938 1.45h0.415l0.75-1.19h0.0227l0.75 1.19h0.415l-0.915-1.45 0.915-1.45h-0.415l-0.75 1.21h-0.0227zm2.24 0.312h0.915v2.6h0.352v-2.6h0.915v-0.312h-2.18zm2.51 2.6 0.293-0.824h1.18l0.293 0.824h0.369l-1.07-2.91h-0.364l-1.07 2.91zm0.403-1.14 0.466-1.31h0.0227l0.466 1.31zm2.18 1.14h1.7v-0.312h-1.35v-2.6h-0.352zm3.04-2.91h-0.352l-0.727 0.483v0.358l0.71-0.472h0.017v2.54h0.352z&quot; removed-label=&quot;9 XTAL1&quot;&#x2F;&gt;
		&lt;path d=&quot;m62.2 68.6h-0.352l-0.727 0.483v0.358l0.71-0.472h0.017v2.54h0.352zm1.78 2.95c0.642 0 1.01-0.544 1.01-1.49 0-0.943-0.375-1.49-1.01-1.49-0.636 0-1.01 0.551-1.01 1.49 0 0.95 0.369 1.49 1.01 1.49zm0-0.312c-0.423 0-0.67-0.425-0.67-1.18 0-0.756 0.25-1.19 0.67-1.19 0.42 0 0.67 0.432 0.67 1.19 0 0.757-0.247 1.18-0.67 1.18zm2.9-2.64h-0.415l0.938 1.45-0.938 1.45h0.415l0.75-1.19h0.0227l0.75 1.19h0.415l-0.915-1.45 0.915-1.45h-0.415l-0.75 1.21h-0.0227zm2.24 0.312h0.915v2.6h0.352v-2.6h0.915v-0.312h-2.18zm2.51 2.6 0.293-0.824h1.18l0.293 0.824h0.369l-1.07-2.91h-0.364l-1.07 2.91zm0.403-1.14 0.466-1.31h0.0227l0.466 1.31zm2.18 1.14h1.7v-0.312h-1.35v-2.6h-0.352zm2.2 0h1.84v-0.312h-1.35v-0.0227l0.653-0.699c0.5-0.536 0.648-0.786 0.648-1.11 0-0.449-0.364-0.807-0.875-0.807-0.51 0-0.898 0.347-0.898 0.858h0.335c0-0.331 0.214-0.551 0.551-0.551 0.315 0 0.557 0.193 0.557 0.5 0 0.268-0.158 0.467-0.494 0.835l-0.96 1.05z&quot; removed-label=&quot;10 XTAL2&quot;&#x2F;&gt;
		&lt;path d=&quot;m82.8 66.5 0.293-0.824h1.18l0.293 0.824h0.369l-1.07-2.91h-0.364l-1.07 2.91zm0.403-1.14 0.466-1.31h0.0227l0.466 1.31zm2.03-1.77h-0.369l1.07 2.91h0.364l1.07-2.91h-0.369l-0.864 2.45h-0.0341zm4.79 0.909c-0.108-0.597-0.585-0.949-1.17-0.949-0.744 0-1.28 0.574-1.28 1.49s0.54 1.49 1.28 1.49c0.585 0 1.06-0.352 1.17-0.949h-0.352c-0.0852 0.403-0.432 0.619-0.818 0.619-0.528 0-0.943-0.409-0.943-1.16s0.415-1.16 0.943-1.16c0.386 0 0.733 0.216 0.818 0.619zm2.91 0c-0.108-0.597-0.585-0.949-1.17-0.949-0.744 0-1.28 0.574-1.28 1.49s0.54 1.49 1.28 1.49c0.585 0 1.06-0.352 1.17-0.949h-0.352c-0.0852 0.403-0.432 0.619-0.818 0.619-0.528 0-0.943-0.409-0.943-1.16s0.415-1.16 0.943-1.16c0.386 0 0.733 0.216 0.818 0.619zm1.64 2h1.84v-0.312h-1.35v-0.0227l0.653-0.699c0.5-0.536 0.648-0.786 0.648-1.11 0-0.449-0.364-0.807-0.875-0.807-0.51 0-0.898 0.347-0.898 0.858h0.335c0-0.331 0.214-0.551 0.551-0.551 0.315 0 0.557 0.193 0.557 0.5 0 0.268-0.158 0.467-0.494 0.835l-0.96 1.05zm3.37 0.0398c0.642 0 1.01-0.544 1.01-1.49 0-0.943-0.375-1.49-1.01-1.49s-1.01 0.551-1.01 1.49c0 0.95 0.369 1.49 1.01 1.49zm0-0.312c-0.423 0-0.67-0.425-0.67-1.18 0-0.756 0.25-1.19 0.67-1.19 0.42 0 0.67 0.432 0.67 1.19 0 0.757-0.247 1.18-0.67 1.18z&quot; removed-label=&quot;AVCC 20&quot;&#x2F;&gt;
		&lt;path d=&quot;m84.5 61.5 0.293-0.824h1.18l0.293 0.824h0.369l-1.07-2.91h-0.364l-1.07 2.91zm0.403-1.14 0.466-1.31h0.0227l0.466 1.31zm2.18 1.14h0.352v-1.14h0.636c0.0256 0 0.0497 0 0.0739-0.0014l0.614 1.14h0.409l-0.658-1.2c0.371-0.126 0.544-0.429 0.544-0.815 0-0.514-0.307-0.892-0.989-0.892h-0.983zm0.352-1.45v-1.14h0.619c0.472 0 0.653 0.23 0.653 0.58 0 0.349-0.182 0.562-0.648 0.562zm2.2 1.45h1.78v-0.312h-1.43v-0.989h1.31v-0.312h-1.31v-0.983h1.4v-0.312h-1.76zm2.39 0h0.352v-1.3h1.26v-0.312h-1.26v-0.983h1.39v-0.312h-1.74zm3.42 0h1.84v-0.312h-1.35v-0.0227l0.653-0.699c0.5-0.536 0.648-0.786 0.648-1.11 0-0.449-0.364-0.807-0.875-0.807-0.51 0-0.898 0.347-0.898 0.858h0.335c0-0.331 0.214-0.551 0.551-0.551 0.315 0 0.557 0.193 0.557 0.5 0 0.268-0.158 0.467-0.494 0.835l-0.96 1.05zm3.44-2.91h-0.352l-0.727 0.483v0.358l0.71-0.472h0.017v2.54h0.352z&quot; removed-label=&quot;AREF 21&quot;&#x2F;&gt;
		&lt;path d=&quot;m86.7 54.5h0.364c-0.108-0.562-0.58-0.949-1.19-0.949-0.746 0-1.27 0.574-1.27 1.49 0 0.92 0.523 1.49 1.3 1.49 0.693 0 1.19-0.462 1.19-1.18v-0.312h-1.11v0.312h0.767c-0.0099 0.523-0.354 0.852-0.847 0.852-0.54 0-0.955-0.409-0.955-1.16s0.415-1.16 0.932-1.16c0.42 0 0.706 0.237 0.83 0.619zm3.29-0.909h-0.347v2.29h-0.0284l-1.59-2.29h-0.341v2.91h0.352v-2.28h0.0284l1.59 2.28h0.341zm1.6 2.91c0.886 0 1.39-0.551 1.39-1.46 0-0.903-0.5-1.45-1.35-1.45h-0.938v2.91zm-0.545-0.312v-2.28h0.562c0.682 0 1.03 0.432 1.03 1.14 0 0.71-0.347 1.15-1.07 1.15zm3.6 0.312h1.84v-0.312h-1.35v-0.0227l0.653-0.699c0.5-0.536 0.648-0.786 0.648-1.11 0-0.449-0.364-0.807-0.875-0.807-0.51 0-0.898 0.347-0.898 0.858h0.335c0-0.331 0.214-0.551 0.551-0.551 0.315 0 0.557 0.193 0.557 0.5 0 0.268-0.158 0.467-0.494 0.835l-0.96 1.05zm2.42 0h1.84v-0.312h-1.35v-0.0227l0.653-0.699c0.5-0.536 0.648-0.786 0.648-1.11 0-0.449-0.364-0.807-0.875-0.807-0.51 0-0.898 0.347-0.898 0.858h0.335c0-0.331 0.214-0.551 0.551-0.551 0.315 0 0.557 0.193 0.557 0.5 0 0.268-0.158 0.467-0.494 0.835l-0.96 1.05z&quot; removed-label=&quot;GND 22&quot;&#x2F;&gt;
		&lt;path d=&quot;m61.1 31.5h1.84v-0.312h-1.35v-0.0227l0.653-0.699c0.5-0.536 0.648-0.786 0.648-1.11 0-0.449-0.364-0.807-0.875-0.807-0.51 0-0.898 0.347-0.898 0.858h0.335c0-0.331 0.214-0.551 0.551-0.551 0.315 0 0.557 0.193 0.557 0.5 0 0.268-0.158 0.467-0.494 0.835l-0.96 1.05zm3.6 0h0.352v-1.14h0.636c0.0256 0 0.0497 0 0.0739-0.0014l0.614 1.14h0.409l-0.658-1.2c0.371-0.126 0.544-0.429 0.544-0.815 0-0.514-0.307-0.892-0.989-0.892h-0.983zm0.352-1.45v-1.14h0.619c0.472 0 0.653 0.23 0.653 0.58 0 0.349-0.182 0.562-0.648 0.562zm2.37-1.45h-0.415l0.938 1.45-0.938 1.45h0.415l0.75-1.19h0.0227l0.75 1.19h0.415l-0.915-1.45 0.915-1.45h-0.415l-0.75 1.21h-0.0227z&quot; removed-label=&quot;2 RX&quot;&#x2F;&gt;
		&lt;path d=&quot;m62.1 36.6c0.564 0 0.983-0.354 0.983-0.83 0-0.369-0.219-0.638-0.585-0.699v-0.0227c0.294-0.0895 0.477-0.331 0.477-0.659 0-0.412-0.325-0.778-0.864-0.778-0.503 0-0.92 0.31-0.938 0.767h0.341c0.0128-0.29 0.288-0.46 0.591-0.46 0.321 0 0.528 0.195 0.528 0.489 0 0.307-0.24 0.506-0.585 0.506h-0.233v0.312h0.233c0.442 0 0.688 0.224 0.688 0.545 0 0.308-0.268 0.517-0.642 0.517-0.337 0-0.604-0.173-0.625-0.455h-0.358c0.0213 0.457 0.425 0.767 0.989 0.767zm2.58-2.64h0.915v2.6h0.352v-2.6h0.915v-0.312h-2.18zm2.9-0.312h-0.415l0.938 1.45-0.938 1.45h0.415l0.75-1.19h0.0227l0.75 1.19h0.415l-0.915-1.45 0.915-1.45h-0.415l-0.75 1.21h-0.0227z&quot; removed-label=&quot;3 TX&quot;&#x2F;&gt;
	&lt;&#x2F;g&gt;
	&lt;ellipse cx=&quot;57.3&quot; cy=&quot;25&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;.5&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
	&lt;path d=&quot;m67.2 46.5 0.31-0.872h1.24l0.31 0.872h0.391l-1.13-3.08h-0.385l-1.13 3.08zm0.427-1.2 0.493-1.39h0.024l0.493 1.39zm1.78-1.55h0.968v2.75h0.373v-2.75h0.968v-0.331h-2.31zm2.6 2.75h0.355v-1.44c0-0.338 0.247-0.577 0.523-0.577 0.269 0 0.457 0.176 0.457 0.439v1.58h0.361v-1.5c0-0.298 0.186-0.517 0.511-0.517 0.253 0 0.469 0.134 0.469 0.475v1.55h0.355v-1.55c0-0.543-0.292-0.794-0.704-0.794-0.331 0-0.573 0.152-0.692 0.391h-0.024c-0.114-0.247-0.316-0.391-0.619-0.391-0.301 0-0.523 0.144-0.619 0.391h-0.0301v-0.361h-0.343zm4.65 0.0481c0.469 0 0.812-0.235 0.92-0.583l-0.343-0.0962c-0.0902 0.241-0.299 0.361-0.577 0.361-0.416 0-0.704-0.269-0.72-0.764h1.68v-0.15c0-0.86-0.511-1.15-0.992-1.15-0.625 0-1.04 0.493-1.04 1.2s0.409 1.18 1.08 1.18zm-0.72-1.39c0.024-0.359 0.278-0.679 0.684-0.679 0.385 0 0.631 0.289 0.631 0.679zm3.15 2.25c0.553 0 0.998-0.253 0.998-0.848v-2.38h-0.343v0.367h-0.0361c-0.0782-0.12-0.222-0.397-0.673-0.397-0.583 0-0.986 0.463-0.986 1.17 0 0.722 0.421 1.13 0.98 1.13 0.451 0 0.595-0.265 0.673-0.391h0.0301v0.469c0 0.385-0.271 0.559-0.643 0.559-0.419 0-0.567-0.221-0.661-0.349l-0.283 0.198c0.144 0.242 0.428 0.463 0.944 0.463zm-0.012-1.27c-0.445 0-0.673-0.337-0.673-0.818 0-0.469 0.222-0.848 0.673-0.848 0.433 0 0.661 0.349 0.661 0.848 0 0.511-0.235 0.818-0.661 0.818zm2.34 0.409c0.403 0 0.613-0.216 0.686-0.367h0.018v0.313h0.355v-1.52c0-0.734-0.559-0.818-0.854-0.818-0.349 0-0.746 0.12-0.926 0.541l0.337 0.12c0.0782-0.168 0.263-0.349 0.601-0.349 0.326 0 0.487 0.173 0.487 0.469v0.012c0 0.171-0.174 0.156-0.595 0.21-0.428 0.0556-0.896 0.15-0.896 0.679 0 0.451 0.349 0.71 0.788 0.71zm0.0541-0.319c-0.283 0-0.487-0.126-0.487-0.373 0-0.271 0.247-0.355 0.523-0.391 0.15-0.018 0.553-0.0601 0.613-0.132v0.325c0 0.289-0.229 0.571-0.649 0.571zm2.69 0.307c0.597 0 1.04-0.374 1.04-0.878 0-0.391-0.232-0.675-0.619-0.74v-0.024c0.311-0.0947 0.505-0.35 0.505-0.698 0-0.436-0.344-0.824-0.914-0.824-0.532 0-0.974 0.328-0.992 0.812h0.361c0.0135-0.307 0.305-0.487 0.625-0.487 0.34 0 0.559 0.206 0.559 0.517 0 0.325-0.254 0.535-0.619 0.535h-0.247v0.331h0.247c0.468 0 0.728 0.238 0.728 0.577 0 0.326-0.284 0.547-0.679 0.547-0.356 0-0.639-0.183-0.661-0.481h-0.379c0.0226 0.484 0.449 0.812 1.05 0.812zm1.65-0.0421h1.94v-0.331h-1.43v-0.024l0.692-0.74c0.529-0.567 0.686-0.831 0.686-1.17 0-0.475-0.385-0.854-0.926-0.854-0.54 0-0.95 0.367-0.95 0.908h0.355c0-0.35 0.227-0.583 0.583-0.583 0.334 0 0.589 0.204 0.589 0.529 0 0.284-0.167 0.495-0.523 0.884l-1.02 1.11zm3.55 0.0421c0.615 0 1.04-0.359 1.05-0.86-3e-3 -0.389-0.263-0.72-0.595-0.782v-0.018c0.289-0.0752 0.478-0.358 0.481-0.686-3e-3 -0.469-0.397-0.818-0.932-0.818-0.541 0-0.935 0.349-0.932 0.818-3e-3 0.328 0.186 0.61 0.481 0.686v0.018c-0.338 0.0616-0.598 0.392-0.595 0.782-3e-3 0.501 0.425 0.86 1.05 0.86zm0-0.331c-0.422 0-0.683-0.216-0.679-0.547-3e-3 -0.347 0.284-0.595 0.679-0.595 0.389 0 0.676 0.248 0.679 0.595-3e-3 0.331-0.263 0.547-0.679 0.547zm0-1.46c-0.337 0-0.574-0.21-0.571-0.529-3e-3 -0.313 0.225-0.517 0.571-0.517 0.34 0 0.568 0.204 0.571 0.517-3e-3 0.319-0.241 0.529-0.571 0.529zm1.68 1.75h0.373v-1.12h0.673c0.717 0 1.04-0.436 1.04-0.98 0-0.544-0.323-0.974-1.05-0.974h-1.04zm0.373-1.46v-1.29h0.655c0.501 0 0.692 0.274 0.692 0.643s-0.191 0.649-0.686 0.649z&quot; fill=&quot;currentColor&quot; stroke-width=&quot;.265&quot; removed-label=&quot;ATmega328P&quot;&#x2F;&gt;
	&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;.5&quot;&gt;
		&lt;path d=&quot;m60 30h-5&quot;&#x2F;&gt;
		&lt;path d=&quot;m60 35h-5&quot;&#x2F;&gt;
		&lt;path d=&quot;m60 55-5-0.0492&quot;&#x2F;&gt;
		&lt;path d=&quot;m60 60h-5&quot;&#x2F;&gt;
		&lt;path d=&quot;m60 65h-5&quot;&#x2F;&gt;
		&lt;path d=&quot;m60 70h-5&quot;&#x2F;&gt;
		&lt;path d=&quot;m100 55h5&quot;&#x2F;&gt;
		&lt;path d=&quot;m100 59.9 5 0.107&quot;&#x2F;&gt;
		&lt;path d=&quot;m100 65h5&quot;&#x2F;&gt;
		&lt;path d=&quot;m36 71h8v3h-8z&quot;&#x2F;&gt;
		&lt;path d=&quot;m35 70h10&quot;&#x2F;&gt;
		&lt;path d=&quot;m45 75h-10&quot;&#x2F;&gt;
		&lt;path d=&quot;m40 70v-5h15&quot;&#x2F;&gt;
		&lt;path d=&quot;m40 75-1e-6 5h10v-10h5&quot;&#x2F;&gt;
	&lt;&#x2F;g&gt;
	&lt;path d=&quot;m41.9 66.9h-0.264l-0.545 0.362v0.268l0.533-0.354h0.0128v1.9h0.264zm1.35 2.21c0.441 0.0053 0.729-0.327 0.729-0.733 0-0.422-0.312-0.724-0.686-0.724-0.225 0-0.418 0.11-0.537 0.29h-0.017c0.0011-0.532 0.208-0.839 0.545-0.839 0.23 0 0.369 0.145 0.418 0.341h0.26c-0.0533-0.339-0.311-0.575-0.678-0.575-0.491 0-0.793 0.435-0.793 1.21 0 0.806 0.401 1.03 0.759 1.04zm0-0.234c-0.274 0-0.474-0.233-0.473-0.494 0.0011-0.261 0.211-0.494 0.482-0.494 0.267 0 0.464 0.219 0.464 0.49 0 0.277-0.206 0.499-0.473 0.499zm1.18-1.98v2.18h0.247v-1.66h0.0213l0.682 1.66h0.239l0.682-1.66h0.0213v1.66h0.247v-2.18h-0.315l-0.741 1.81h-0.0256l-0.741-1.81zm2.67 2.18h0.264v-0.976h1.16v0.976h0.264v-2.18h-0.264v0.972h-1.16v-0.972h-0.264zm2.14 0h1.28v-0.234h-0.933v-0.017l0.903-1.18v-0.2h-1.22v0.234h0.899v0.017l-0.929 1.19z&quot; fill=&quot;currentColor&quot; stroke-width=&quot;.5&quot; removed-label=&quot;16MHz&quot;&#x2F;&gt;
	&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot;&gt;
		&lt;g stroke-width=&quot;.5&quot;&gt;
			&lt;path d=&quot;m40 65h-8&quot;&#x2F;&gt;
			&lt;path d=&quot;m32 62v6&quot;&#x2F;&gt;
			&lt;path d=&quot;m30 62v6&quot;&#x2F;&gt;
			&lt;path d=&quot;m30 77v6&quot;&#x2F;&gt;
			&lt;path d=&quot;m32 77v6&quot;&#x2F;&gt;
			&lt;path d=&quot;m40 80h-8&quot;&#x2F;&gt;
			&lt;path d=&quot;m30 80h-5&quot;&#x2F;&gt;
			&lt;path d=&quot;m30 65h-5&quot;&#x2F;&gt;
			&lt;path d=&quot;m55 60h-30v25&quot;&#x2F;&gt;
			&lt;path d=&quot;m105 55h5v30&quot;&#x2F;&gt;
			&lt;path d=&quot;m105 65h3c6e-5 -1.1 0.896-2 2-2 1.1 6.7e-5 2 0.896 2 2h3v-55&quot;&#x2F;&gt;
			&lt;path d=&quot;m55 55-25 1e-6v-45&quot;&#x2F;&gt;
			&lt;path d=&quot;m34 25h-4&quot;&#x2F;&gt;
			&lt;path d=&quot;m46 25 9.25-3e-6&quot;&#x2F;&gt;
		&lt;&#x2F;g&gt;
		&lt;g stroke-width=&quot;.5&quot;&gt;
			&lt;path d=&quot;m13 37c1.1-6.7e-5 2-0.895 2-2-6.7e-5 -1.1-0.895-2-2-2&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
			&lt;path d=&quot;m13 32c1.1-6.7e-5 2-0.895 2-2-6.7e-5 -1.1-0.895-2-2-2&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
			&lt;path d=&quot;m13 27c1.1-6.7e-5 2-0.895 2-2-6.7e-5 -1.1-0.895-2-2-2&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
			&lt;path d=&quot;m13 22c1.1-6.7e-5 2-0.895 2-2-6.7e-5 -1.1-0.895-2-2-2&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
		&lt;&#x2F;g&gt;
		&lt;g stroke-width=&quot;.5&quot;&gt;
			&lt;path d=&quot;m50 60.1 1.6e-5 -3.12c1.1-6.6e-5 2-0.895 2-2-6.7e-5 -1.1-0.895-2-2-2v-3&quot;&#x2F;&gt;
			&lt;path d=&quot;m30.1 25-3.12-1.6e-5c-6.6e-5 -1.1-0.895-2-2-2-1.1 6.7e-5 -2 0.895-2 2h-8&quot;&#x2F;&gt;
			&lt;path d=&quot;m55.1 35-3.12-1.6e-5c-6.6e-5 -1.1-0.895-2-2-2-1.1 6.7e-5 -2 0.895-2 2h-16c-6.6e-5 -1.1-0.895-2-2-2-1.1 6.7e-5 -2 0.895-2 2h-1c-6.6e-5 -1.1-0.895-2-2-2-1.1 6.7e-5 -2 0.895-2 2l-8 1.21e-4&quot;&#x2F;&gt;
			&lt;path d=&quot;m55.1 30-3.12-1.6e-5c-6.6e-5 -1.1-0.895-2-2-2-1.1 6.7e-5 -2 0.895-2 2l-16 1.21e-4c-6.6e-5 -1.1-0.895-2-2-2-1.1 6.7e-5 -2 0.895-2 2l-1-6.1e-5c-1.28e-4 -1.1-0.895-2-2-2-1.1 6.7e-5 -2 0.895-2 2l-8 1.21e-4&quot;&#x2F;&gt;
		&lt;&#x2F;g&gt;
	&lt;&#x2F;g&gt;
	&lt;g transform=&quot;translate(.5 -29.5)&quot; stroke=&quot;currentColor&quot;&gt;
		&lt;path d=&quot;m47.5 73.5v6&quot; fill=&quot;none&quot; stroke-width=&quot;.463&quot;&#x2F;&gt;
		&lt;g stroke-width=&quot;.5&quot;&gt;
			&lt;path d=&quot;m47.5 76.5h3.25&quot; fill=&quot;none&quot;&#x2F;&gt;
			&lt;ellipse cx=&quot;49.5&quot; cy=&quot;74&quot; rx=&quot;.486&quot; ry=&quot;.5&quot; fill=&quot;currentColor&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
			&lt;ellipse cx=&quot;49.5&quot; cy=&quot;79&quot; rx=&quot;.5&quot; ry=&quot;.5&quot; fill=&quot;currentColor&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
		&lt;&#x2F;g&gt;
	&lt;&#x2F;g&gt;
	&lt;path d=&quot;m50 25v19&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;.5&quot;&#x2F;&gt;
	&lt;path d=&quot;m50 40h-18c-6.6e-5 -1.1-0.895-2-2-2-1.1 6.7e-5 -2 0.895-2 2h-1c-6.6e-5 -1.1-0.895-2-2-2-1.1 6.7e-5 -2 0.895-2 2h-3&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;.5&quot;&#x2F;&gt;
	&lt;path d=&quot;m15.7 45.7h-0.258v-1.5q0-0.129 3e-3 -0.207t9e-3 -0.162q-0.048 0.048-0.087 0.081t-0.099 0.084l-0.228 0.186-0.138-0.177 0.579-0.45h0.219zm2.22-1.07q0 0.345-0.072 0.594-0.072 0.246-0.228 0.378t-0.414 0.132q-0.363 0-0.537-0.291-0.171-0.294-0.171-0.813 0-0.348 0.069-0.594 0.072-0.246 0.228-0.375 0.156-0.132 0.411-0.132 0.36 0 0.537 0.291 0.177 0.288 0.177 0.81zm-1.16 0q0 0.441 0.099 0.66 0.102 0.219 0.345 0.219 0.24 0 0.342-0.216 0.105-0.219 0.105-0.663 0-0.438-0.105-0.657-0.102-0.219-0.342-0.219-0.243 0-0.345 0.219-0.099 0.219-0.099 0.657zm2.87 0q0 0.345-0.072 0.594-0.072 0.246-0.228 0.378t-0.414 0.132q-0.363 0-0.537-0.291-0.171-0.294-0.171-0.813 0-0.348 0.069-0.594 0.072-0.246 0.228-0.375 0.156-0.132 0.411-0.132 0.36 0 0.537 0.291 0.177 0.288 0.177 0.81zm-1.16 0q0 0.441 0.099 0.66 0.102 0.219 0.345 0.219 0.24 0 0.342-0.216 0.105-0.219 0.105-0.663 0-0.438-0.105-0.657-0.102-0.219-0.342-0.219-0.243 0-0.345 0.219-0.099 0.219-0.099 0.657zm2.33-0.564q0.288 0 0.435 0.141 0.147 0.138 0.147 0.45v1.05h-0.261v-1.03q0-0.387-0.36-0.387-0.267 0-0.369 0.15t-0.102 0.432v0.834h-0.264v-1.61h0.213l0.039 0.219h0.015q0.078-0.126 0.216-0.186 0.138-0.063 0.291-0.063zm1.39 1.64h-0.27v-2.14h1.2v0.237h-0.927v0.759h0.87v0.237h-0.87z&quot; fill=&quot;currentColor&quot; stroke-width=&quot;.265&quot; removed-label=&quot;100nF&quot;&#x2F;&gt;
	&lt;path d=&quot;m13 42c1.1-6.7e-5 2-0.895 2-2-6.7e-5 -1.1-0.895-2-2-2&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;.5&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
	&lt;g fill=&quot;currentColor&quot; stroke-width=&quot;.265&quot;&gt;
		&lt;path d=&quot;m6.19 14.5h0.364c-0.108-0.562-0.58-0.949-1.19-0.949-0.746 0-1.27 0.574-1.27 1.49 0 0.92 0.523 1.49 1.3 1.49 0.693 0 1.19-0.462 1.19-1.18v-0.312h-1.11v0.312h0.767c-0.00994 0.523-0.354 0.852-0.847 0.852-0.54 0-0.955-0.409-0.955-1.16 0-0.756 0.415-1.16 0.932-1.16 0.42 0 0.706 0.237 0.83 0.619zm3.29-0.909h-0.347v2.29h-0.0284l-1.59-2.29h-0.341v2.91h0.352v-2.28h0.0284l1.59 2.28h0.341zm1.6 2.91c0.886 0 1.39-0.551 1.39-1.46 0-0.903-0.5-1.45-1.35-1.45h-0.938v2.91zm-0.545-0.312v-2.28h0.562c0.682 0 1.03 0.432 1.03 1.14 0 0.71-0.347 1.15-1.07 1.15z&quot; removed-label=&quot;GND&quot;&#x2F;&gt;
		&lt;path d=&quot;m5.96 41.5c0.886 0 1.39-0.551 1.39-1.46 0-0.903-0.5-1.45-1.35-1.45h-0.938v2.91zm-0.545-0.312v-2.28h0.562c0.682 0 1.03 0.432 1.03 1.14 0 0.71-0.347 1.15-1.07 1.15zm2.24-2.28h0.915v2.6h0.352v-2.6h0.915v-0.312h-2.18zm2.73 2.6h0.352v-1.14h0.636c0.0256 0 0.0497 0 0.0739-0.0014l0.614 1.14h0.409l-0.658-1.2c0.371-0.126 0.544-0.429 0.544-0.815 0-0.514-0.307-0.892-0.989-0.892h-0.983zm0.352-1.45v-1.14h0.619c0.472 0 0.653 0.23 0.653 0.58s-0.182 0.562-0.648 0.562z&quot; removed-label=&quot;DTR&quot;&#x2F;&gt;
		&lt;path d=&quot;m7.36 19.4c-0.108-0.597-0.585-0.949-1.17-0.949-0.744 0-1.28 0.574-1.28 1.49 0 0.92 0.54 1.49 1.28 1.49 0.585 0 1.06-0.352 1.17-0.949h-0.352c-0.0852 0.403-0.432 0.619-0.818 0.619-0.528 0-0.943-0.409-0.943-1.16 0-0.756 0.415-1.16 0.943-1.16 0.386 0 0.733 0.216 0.818 0.619zm0.409-0.597h0.915v2.6h0.352v-2.6h0.915v-0.312h-2.18zm4.32 0.415h0.341c-0.0156-0.439-0.42-0.767-0.983-0.767-0.557 0-0.994 0.324-0.994 0.812 0 0.392 0.284 0.625 0.739 0.756l0.358 0.102c0.307 0.0852 0.58 0.193 0.58 0.483 0 0.318-0.307 0.528-0.71 0.528-0.347 0-0.653-0.153-0.682-0.483h-0.364c0.0341 0.477 0.42 0.801 1.05 0.801 0.67 0 1.05-0.369 1.05-0.841 0-0.545-0.517-0.722-0.818-0.801l-0.295-0.0796c-0.216-0.0568-0.562-0.17-0.562-0.483 0-0.278 0.256-0.483 0.642-0.483 0.352 0 0.619 0.168 0.653 0.455z&quot; removed-label=&quot;CTS&quot;&#x2F;&gt;
		&lt;path d=&quot;m4.77 23.5h-0.369l1.07 2.91h0.364l1.07-2.91h-0.369l-0.864 2.45h-0.0341zm4.79 0.909c-0.108-0.597-0.585-0.949-1.17-0.949-0.744 0-1.28 0.574-1.28 1.49 0 0.92 0.54 1.49 1.28 1.49 0.585 0 1.06-0.352 1.17-0.949h-0.352c-0.0852 0.403-0.432 0.619-0.818 0.619-0.528 0-0.943-0.409-0.943-1.16 0-0.756 0.415-1.16 0.943-1.16 0.386 0 0.733 0.216 0.818 0.619zm2.91 0c-0.108-0.597-0.585-0.949-1.17-0.949-0.744 0-1.28 0.574-1.28 1.49 0 0.92 0.54 1.49 1.28 1.49 0.585 0 1.06-0.352 1.17-0.949h-0.352c-0.0852 0.403-0.432 0.619-0.818 0.619-0.528 0-0.943-0.409-0.943-1.16 0-0.756 0.415-1.16 0.943-1.16 0.386 0 0.733 0.216 0.818 0.619z&quot; removed-label=&quot;VCC&quot;&#x2F;&gt;
		&lt;path d=&quot;m7.64 28.9h0.915v2.6h0.352v-2.6h0.915v-0.312h-2.18zm2.9-0.312h-0.415l0.938 1.45-0.938 1.45h0.415l0.75-1.19h0.0227l0.75 1.19h0.415l-0.915-1.45 0.915-1.45h-0.415l-0.75 1.21h-0.0227z&quot; removed-label=&quot;TX&quot;&#x2F;&gt;
		&lt;path d=&quot;m7.81 36.5h0.352v-1.14h0.636c0.0256 0 0.0497 0 0.0739-0.0014l0.614 1.14h0.409l-0.658-1.2c0.371-0.126 0.544-0.429 0.544-0.815 0-0.514-0.307-0.892-0.989-0.892h-0.983zm0.352-1.45v-1.14h0.619c0.472 0 0.653 0.23 0.653 0.58 0 0.349-0.182 0.562-0.648 0.562zm2.37-1.45h-0.415l0.938 1.45-0.938 1.45h0.415l0.75-1.19h0.0227l0.75 1.19h0.415l-0.915-1.45 0.915-1.45h-0.415l-0.75 1.21h-0.0227z&quot; removed-label=&quot;RX&quot;&#x2F;&gt;
	&lt;&#x2F;g&gt;
	&lt;g transform=&quot;translate(-25 -25)&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;.5&quot;&gt;
		&lt;path d=&quot;m45 62v6&quot;&#x2F;&gt;
		&lt;path d=&quot;m43 62v6&quot;&#x2F;&gt;
	&lt;&#x2F;g&gt;
	&lt;path d=&quot;m18 40-3 2e-6&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;.5&quot;&#x2F;&gt;
	&lt;path d=&quot;m34 23 12 2e-6v4l-12-2e-6z&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;.5&quot;&#x2F;&gt;
	&lt;g fill=&quot;currentColor&quot;&gt;
		&lt;g stroke-width=&quot;.265&quot;&gt;
			&lt;path d=&quot;m37.2 26.1h-0.258v-1.5q0-0.129 3e-3 -0.207t9e-3 -0.162q-0.048 0.048-0.087 0.081t-0.099 0.084l-0.228 0.186-0.138-0.177 0.579-0.45h0.219zm2.22-1.07q0 0.345-0.072 0.594-0.072 0.246-0.228 0.378t-0.414 0.132q-0.363 0-0.537-0.291-0.171-0.294-0.171-0.813 0-0.348 0.069-0.594 0.072-0.246 0.228-0.375 0.156-0.132 0.411-0.132 0.36 0 0.537 0.291 0.177 0.288 0.177 0.81zm-1.16 0q0 0.441 0.099 0.66 0.102 0.219 0.345 0.219 0.24 0 0.342-0.216 0.105-0.219 0.105-0.663 0-0.438-0.105-0.657-0.102-0.219-0.342-0.219-0.243 0-0.345 0.219-0.099 0.219-0.099 0.657zm3.16 1.07h-0.318l-0.759-1.02-0.219 0.192v0.831h-0.27v-2.14h0.27v1.06q0.09-0.102 0.183-0.204t0.186-0.204l0.579-0.648h0.315l-0.849 0.933zm0.075 0v-0.237h0.474q-0.111-0.084-0.216-0.21-0.105-0.129-0.174-0.306-0.066-0.177-0.066-0.399 0-0.294 0.114-0.525t0.333-0.363q0.219-0.135 0.534-0.135 0.318 0 0.537 0.132t0.333 0.363 0.114 0.525q0 0.225-0.066 0.402t-0.171 0.306q-0.102 0.126-0.216 0.21h0.471v0.237h-0.804v-0.216q0.186-0.132 0.297-0.27t0.159-0.297q0.048-0.162 0.048-0.36 0-0.228-0.075-0.408t-0.231-0.282q-0.153-0.105-0.396-0.105-0.24 0-0.396 0.105-0.153 0.102-0.228 0.282-0.072 0.18-0.072 0.408 0 0.198 0.048 0.36 0.048 0.159 0.156 0.297 0.111 0.135 0.297 0.27v0.216z&quot; removed-label=&quot;10KΩ&quot;&#x2F;&gt;
			&lt;path d=&quot;m34.1 64.3h-1.42v-0.219l0.561-0.567q0.162-0.162 0.273-0.288t0.168-0.246q0.057-0.123 0.057-0.267 0-0.177-0.105-0.267-0.105-0.093-0.273-0.093-0.156 0-0.276 0.054-0.117 0.054-0.24 0.15l-0.141-0.177q0.126-0.105 0.288-0.177 0.165-0.075 0.369-0.075 0.3 0 0.474 0.153 0.174 0.15 0.174 0.417 0 0.168-0.069 0.315t-0.192 0.291q-0.123 0.141-0.288 0.303l-0.447 0.441v0.012h1.08zm1.72 0h-1.42v-0.219l0.561-0.567q0.162-0.162 0.273-0.288t0.168-0.246q0.057-0.123 0.057-0.267 0-0.177-0.105-0.267-0.105-0.093-0.273-0.093-0.156 0-0.276 0.054-0.117 0.054-0.24 0.15l-0.141-0.177q0.126-0.105 0.288-0.177 0.165-0.075 0.369-0.075 0.3 0 0.474 0.153 0.174 0.15 0.174 0.417 0 0.168-0.069 0.315t-0.192 0.291q-0.123 0.141-0.288 0.303l-0.447 0.441v0.012h1.08zm1.18-1.64q0.297 0 0.477 0.207 0.183 0.207 0.183 0.624 0 0.411-0.183 0.624-0.18 0.213-0.48 0.213-0.186 0-0.309-0.069-0.12-0.072-0.189-0.165h-0.018q6e-3 0.051 0.012 0.129t6e-3 0.135v0.66h-0.264v-2.33h0.216l0.036 0.219h0.012q0.072-0.105 0.189-0.177t0.312-0.072zm-0.048 0.222q-0.246 0-0.348 0.138-0.099 0.138-0.105 0.42v0.051q0 0.297 0.096 0.459 0.099 0.159 0.363 0.159 0.147 0 0.24-0.081 0.096-0.081 0.141-0.219 0.048-0.141 0.048-0.321 0-0.276-0.108-0.441-0.105-0.165-0.327-0.165zm1.87 0.012h-0.405v1.4h-0.264v-1.4h-0.282v-0.123l0.282-0.09v-0.093q0-0.312 0.138-0.447 0.138-0.138 0.384-0.138 0.096 0 0.174 0.018 0.081 0.015 0.138 0.036l-0.069 0.207q-0.048-0.015-0.111-0.03t-0.129-0.015q-0.132 0-0.198 0.09-0.063 0.087-0.063 0.276v0.105h0.405z&quot; removed-label=&quot;22pf&quot;&#x2F;&gt;
			&lt;path d=&quot;m34.1 83.3h-1.42v-0.219l0.561-0.567q0.162-0.162 0.273-0.288t0.168-0.246q0.057-0.123 0.057-0.267 0-0.177-0.105-0.267-0.105-0.093-0.273-0.093-0.156 0-0.276 0.054-0.117 0.054-0.24 0.15l-0.141-0.177q0.126-0.105 0.288-0.177 0.165-0.075 0.369-0.075 0.3 0 0.474 0.153 0.174 0.15 0.174 0.417 0 0.168-0.069 0.315t-0.192 0.291q-0.123 0.141-0.288 0.303l-0.447 0.441v0.012h1.08zm1.72 0h-1.42v-0.219l0.561-0.567q0.162-0.162 0.273-0.288t0.168-0.246q0.057-0.123 0.057-0.267 0-0.177-0.105-0.267-0.105-0.093-0.273-0.093-0.156 0-0.276 0.054-0.117 0.054-0.24 0.15l-0.141-0.177q0.126-0.105 0.288-0.177 0.165-0.075 0.369-0.075 0.3 0 0.474 0.153 0.174 0.15 0.174 0.417 0 0.168-0.069 0.315t-0.192 0.291q-0.123 0.141-0.288 0.303l-0.447 0.441v0.012h1.08zm1.18-1.64q0.297 0 0.477 0.207 0.183 0.207 0.183 0.624 0 0.411-0.183 0.624-0.18 0.213-0.48 0.213-0.186 0-0.309-0.069-0.12-0.072-0.189-0.165h-0.018q6e-3 0.051 0.012 0.129t6e-3 0.135v0.66h-0.264v-2.33h0.216l0.036 0.219h0.012q0.072-0.105 0.189-0.177t0.312-0.072zm-0.048 0.222q-0.246 0-0.348 0.138-0.099 0.138-0.105 0.42v0.051q0 0.297 0.096 0.459 0.099 0.159 0.363 0.159 0.147 0 0.24-0.081 0.096-0.081 0.141-0.219 0.048-0.141 0.048-0.321 0-0.276-0.108-0.441-0.105-0.165-0.327-0.165zm1.87 0.012h-0.405v1.4h-0.264v-1.4h-0.282v-0.123l0.282-0.09v-0.093q0-0.312 0.138-0.447 0.138-0.138 0.384-0.138 0.096 0 0.174 0.018 0.081 0.015 0.138 0.036l-0.069 0.207q-0.048-0.015-0.111-0.03t-0.129-0.015q-0.132 0-0.198 0.09-0.063 0.087-0.063 0.276v0.105h0.405z&quot; removed-label=&quot;22pf&quot;&#x2F;&gt;
			&lt;g removed-label=&quot;Reset
Button&quot;&gt;
				&lt;path d=&quot;m39.1 46h0.264v-0.852h0.477c0.0192 0 0.0373 0 0.0554-0.0011l0.46 0.853h0.307l-0.493-0.901c0.278-0.0948 0.408-0.322 0.408-0.612 0-0.386-0.23-0.669-0.741-0.669h-0.737zm0.264-1.09v-0.857h0.464c0.354 0 0.49 0.173 0.49 0.435s-0.136 0.422-0.486 0.422zm2.29 1.12c0.332 0 0.575-0.166 0.652-0.413l-0.243-0.0682c-0.0639 0.17-0.212 0.256-0.409 0.256-0.295 0-0.499-0.191-0.51-0.541h1.19v-0.107c0-0.609-0.362-0.818-0.703-0.818-0.443 0-0.737 0.349-0.737 0.852s0.29 0.839 0.763 0.839zm-0.51-0.984c0.017-0.255 0.197-0.482 0.485-0.482 0.273 0 0.447 0.205 0.447 0.482zm2.73-0.32c-0.0788-0.232-0.256-0.388-0.58-0.388-0.345 0-0.601 0.196-0.601 0.473 0 0.226 0.134 0.377 0.435 0.447l0.273 0.0639c0.165 0.0384 0.243 0.117 0.243 0.23 0 0.141-0.149 0.256-0.384 0.256-0.206 0-0.335-0.0884-0.379-0.264l-0.239 0.0597c0.0586 0.278 0.288 0.426 0.622 0.426 0.38 0 0.639-0.208 0.639-0.49 0-0.228-0.143-0.372-0.435-0.443l-0.243-0.0597c-0.194-0.0479-0.281-0.113-0.281-0.239 0-0.141 0.149-0.243 0.349-0.243 0.219 0 0.31 0.121 0.354 0.234zm1.1 1.3c0.332 0 0.575-0.166 0.652-0.413l-0.243-0.0682c-0.0639 0.17-0.212 0.256-0.409 0.256-0.295 0-0.499-0.191-0.51-0.541h1.19v-0.107c0-0.609-0.362-0.818-0.703-0.818-0.443 0-0.737 0.349-0.737 0.852s0.29 0.839 0.763 0.839zm-0.51-0.984c0.017-0.255 0.197-0.482 0.485-0.482 0.273 0 0.447 0.205 0.447 0.482zm2.28-0.686h-0.349v-0.392h-0.251v0.392h-0.247v0.213h0.247v1.02c0 0.286 0.23 0.422 0.443 0.422 0.0938 0 0.153-0.017 0.188-0.0298l-0.0511-0.226c-0.0213 0.0043-0.0554 0.0128-0.111 0.0128-0.111 0-0.217-0.0341-0.217-0.247v-0.955h0.349z&quot;&#x2F;&gt;
				&lt;path d=&quot;m37.8 49.8h0.788c0.516 0 0.729-0.251 0.729-0.58 0-0.345-0.239-0.533-0.439-0.545v-0.0213c0.188-0.0511 0.354-0.175 0.354-0.456 0-0.32-0.213-0.58-0.669-0.58h-0.763zm0.264-0.234v-0.759h0.537c0.286 0 0.464 0.192 0.464 0.413 0 0.192-0.132 0.345-0.477 0.345zm0-0.989v-0.724h0.499c0.29 0 0.418 0.153 0.418 0.345 0 0.23-0.188 0.379-0.426 0.379zm2.68 0.554c0 0.307-0.234 0.447-0.422 0.447-0.209 0-0.358-0.153-0.358-0.392v-1.02h-0.251v1.04c0 0.418 0.222 0.618 0.528 0.618 0.247 0 0.409-0.132 0.486-0.298h0.017v0.277h0.251v-1.64h-0.251zm1.42-0.967h-0.349v-0.392h-0.251v0.392h-0.247v0.213h0.247v1.02c0 0.286 0.23 0.422 0.443 0.422 0.0938 0 0.153-0.017 0.188-0.0298l-0.0511-0.226c-0.0213 0.0043-0.0554 0.0128-0.111 0.0128-0.111 0-0.217-0.0341-0.217-0.247v-0.955h0.349zm1.09 0h-0.349v-0.392h-0.251v0.392h-0.247v0.213h0.247v1.02c0 0.286 0.23 0.422 0.443 0.422 0.0938 0 0.153-0.017 0.188-0.0298l-0.0511-0.226c-0.0213 0.0043-0.0554 0.0128-0.111 0.0128-0.111 0-0.217-0.0341-0.217-0.247v-0.955h0.349zm1.03 1.67c0.443 0 0.741-0.337 0.741-0.844 0-0.511-0.298-0.848-0.741-0.848s-0.741 0.337-0.741 0.848c0 0.507 0.298 0.844 0.741 0.844zm0-0.226c-0.337 0-0.49-0.29-0.49-0.618s0.153-0.622 0.49-0.622c0.337 0 0.49 0.294 0.49 0.622s-0.153 0.618-0.49 0.618zm1.38-0.793c0-0.286 0.177-0.447 0.418-0.447 0.233 0 0.375 0.152 0.375 0.409v1.02h0.251v-1.04c0-0.418-0.223-0.618-0.554-0.618-0.247 0-0.401 0.111-0.477 0.277h-0.0213v-0.256h-0.243v1.64h0.251z&quot;&#x2F;&gt;
			&lt;&#x2F;g&gt;
		&lt;&#x2F;g&gt;
		&lt;g stroke=&quot;currentColor&quot; stroke-width=&quot;.5&quot;&gt;
			&lt;circle cx=&quot;50&quot; cy=&quot;25&quot; r=&quot;.5&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
			&lt;circle cx=&quot;61.8&quot; cy=&quot;21.7&quot; r=&quot;.5&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
			&lt;circle cx=&quot;50&quot; cy=&quot;40&quot; r=&quot;.5&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
			&lt;circle cx=&quot;30&quot; cy=&quot;25&quot; r=&quot;.5&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
			&lt;circle cx=&quot;50&quot; cy=&quot;60&quot; r=&quot;.5&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
			&lt;circle cx=&quot;25&quot; cy=&quot;65&quot; r=&quot;.5&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
			&lt;circle cx=&quot;25&quot; cy=&quot;60&quot; r=&quot;.5&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
			&lt;circle cx=&quot;25&quot; cy=&quot;80&quot; r=&quot;.5&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
			&lt;circle cx=&quot;40&quot; cy=&quot;80&quot; r=&quot;.5&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
			&lt;circle cx=&quot;40&quot; cy=&quot;65&quot; r=&quot;.5&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
		&lt;&#x2F;g&gt;
		&lt;path d=&quot;m36 8.37h0.318v-0.778h0.778v-0.318h-0.778v-0.778h-0.318v0.778h-0.778v0.318h0.778zm2.69 0.551c0.554 0 0.96-0.409 0.96-0.966 0-0.564-0.392-0.977-0.926-0.977-0.196 0-0.386 0.0696-0.506 0.165h-0.017l0.102-0.858h1.22v-0.312h-1.51l-0.176 1.43 0.33 0.0398c0.121-0.0866 0.327-0.149 0.506-0.148 0.371 0.00284 0.642 0.284 0.642 0.665 0 0.374-0.261 0.648-0.619 0.648-0.298 0-0.536-0.192-0.562-0.455h-0.341c0.0213 0.443 0.403 0.767 0.903 0.767zm1.69-2.95h-0.369l1.07 2.91h0.364l1.07-2.91h-0.369l-0.864 2.45h-0.0341z&quot; stroke-width=&quot;.265&quot; removed-label=&quot;+5V&quot;&#x2F;&gt;
		&lt;path d=&quot;m103 8.37h0.318v-0.778h0.778v-0.318h-0.778v-0.778h-0.318v0.778h-0.778v0.318h0.778zm2.69 0.551c0.554 0 0.96-0.409 0.96-0.966 0-0.564-0.392-0.977-0.926-0.977-0.196 0-0.386 0.0696-0.506 0.165h-0.017l0.102-0.858h1.22v-0.312h-1.51l-0.176 1.43 0.33 0.0398c0.121-0.0866 0.327-0.149 0.506-0.148 0.371 0.00284 0.642 0.284 0.642 0.665 0 0.374-0.261 0.648-0.619 0.648-0.298 0-0.536-0.192-0.562-0.455h-0.341c0.0213 0.443 0.403 0.767 0.903 0.767zm1.69-2.95h-0.369l1.07 2.91h0.364l1.07-2.91h-0.369l-0.864 2.45h-0.0341z&quot; stroke-width=&quot;.265&quot; removed-label=&quot;+5V&quot;&#x2F;&gt;
	&lt;&#x2F;g&gt;
	&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot;&gt;
		&lt;path d=&quot;m13 17c1.1-6.7e-5 2-0.895 2-2-6.7e-5 -1.1-0.895-2-2-2&quot; stroke-width=&quot;.5&quot; style=&quot;paint-order:stroke markers fill&quot;&#x2F;&gt;
		&lt;path d=&quot;m15 15h10.1l-0.0912 45&quot; stroke-width=&quot;.5&quot;&#x2F;&gt;
		&lt;path d=&quot;m15 20h5&quot; stroke-width=&quot;.5&quot;&#x2F;&gt;
	&lt;&#x2F;g&gt;
	&lt;metadata&gt;
		&lt;rdf:RDF&gt;
			&lt;cc:License rdf:about=&quot;http:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;
				&lt;cc:permits rdf:resource=&quot;http:&#x2F;&#x2F;creativecommons.org&#x2F;ns#Reproduction&quot;&#x2F;&gt;
				&lt;cc:permits rdf:resource=&quot;http:&#x2F;&#x2F;creativecommons.org&#x2F;ns#Distribution&quot;&#x2F;&gt;
				&lt;cc:requires rdf:resource=&quot;http:&#x2F;&#x2F;creativecommons.org&#x2F;ns#Notice&quot;&#x2F;&gt;
				&lt;cc:requires rdf:resource=&quot;http:&#x2F;&#x2F;creativecommons.org&#x2F;ns#Attribution&quot;&#x2F;&gt;
				&lt;cc:permits rdf:resource=&quot;http:&#x2F;&#x2F;creativecommons.org&#x2F;ns#DerivativeWorks&quot;&#x2F;&gt;
				&lt;cc:requires rdf:resource=&quot;http:&#x2F;&#x2F;creativecommons.org&#x2F;ns#ShareAlike&quot;&#x2F;&gt;
			&lt;&#x2F;cc:License&gt;
			&lt;cc:Work rdf:about=&quot;&quot;&gt;
				&lt;cc:license rdf:resource=&quot;http:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&#x2F;&gt;
				&lt;dc:title&gt;Schematic: Barebones ATmega328P Circuit&lt;&#x2F;dc:title&gt;
				&lt;dc:creator&gt;
					&lt;cc:Agent&gt;
						&lt;dc:title&gt;Slatian&lt;&#x2F;dc:title&gt;
					&lt;&#x2F;cc:Agent&gt;
				&lt;&#x2F;dc:creator&gt;
				&lt;dc:date&gt;2024-04-06&lt;&#x2F;dc:date&gt;
			&lt;&#x2F;cc:Work&gt;
		&lt;&#x2F;rdf:RDF&gt;
	&lt;&#x2F;metadata&gt;
&lt;&#x2F;svg&gt;


	&lt;figcaption&gt;Schematic of the ATmega328P hooked up to power, an external crystal, and a reset circuit as described below. The Serial interface for programming is illustrated as an FTDI compatible layout.&lt;&#x2F;figcaption&gt;


&lt;&#x2F;figure&gt;


&lt;p&gt;For the power Connect &lt;i&gt;VCC&lt;&#x2F;i&gt; and &lt;i&gt;AVCC&lt;&#x2F;i&gt; to 5V and the two &lt;i&gt;GND&lt;&#x2F;i&gt; to Ground. Leave &lt;i&gt;AREF&lt;&#x2F;i&gt; floating unless you need it.&lt;&#x2F;p&gt;
&lt;p&gt;If you want that extra bit of reliability connect a 100nF Capacitor between &lt;i&gt;VCC&lt;&#x2F;i&gt; and &lt;i&gt;GND&lt;&#x2F;i&gt; and between &lt;i&gt;AVCC&lt;&#x2F;i&gt; and &lt;i&gt;GND&lt;&#x2F;i&gt;. The official UNO has a 10μH inductor between &lt;i&gt;AVCC&lt;&#x2F;i&gt; and the 5V to stabilise it even more.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note on AREF:&lt;&#x2F;b&gt; The official Arduino boards connect AREF through 100nF Capacitor to ground. I assume this is to initially pull it to Ground while allowing you to override that setting by simply applying a voltage.&lt;&#x2F;p&gt;
&lt;p&gt;For the Clock connect the 16MHz crystal between &lt;i&gt;XTAL1&lt;&#x2F;i&gt; and &lt;i&gt;XTAL2&lt;&#x2F;i&gt;, and each of &lt;i&gt;XTAL1&lt;&#x2F;i&gt; and &lt;i&gt;XTAL2&lt;&#x2F;i&gt; through a 22pf Capacitor to Ground. Try to no make the circuit paths too long here.&lt;&#x2F;p&gt;
&lt;p&gt;For the Reset Circuit: Connect the &lt;i&gt;Reset&lt;&#x2F;i&gt; pin through a 10KΩ Resistor to 5V, put the button between &lt;i&gt;Reset&lt;&#x2F;i&gt; and Ground.&lt;&#x2F;p&gt;
&lt;p&gt;When using a Serial interface (Make sure it uses 5V logic levels, otherwise you&#x27;ll destroy something), put the 100nF Capacitor between the &lt;i&gt;Ready to Send&lt;&#x2F;i&gt; (&lt;i&gt;RTS&lt;&#x2F;i&gt; or &lt;i&gt;DTR&lt;&#x2F;i&gt;) and the Reset pin, this way the Chip will automatically reset when the Serial line becomes active, which is the exact behaviour you want when programming using the Arduino bootloader.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note on perfboard Layout:&lt;&#x2F;b&gt; On a perfboard I usually try to go as compact as possible and have a male header right up to the microcontrollers pins that gives access to GND, Reset, RX and TX (with VCC being part of a male GND, VCC, GND header). The DTR capacitor is on a little adaptor board along with an indicator LED that then plugs into my FTDI breakout. The reset button is near the Reset pin and the pull-up resistor goes below the IC-Socket.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;flashing-the-bootloader&quot;&gt;Flashing the Bootloader&lt;&#x2F;h3&gt;
&lt;p&gt;To flash the Arduino bootloader onto the ATmega328P you have to hook up the SPI interface to a programmer. See the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.arduino.cc&#x2F;built-in-examples&#x2F;arduino-isp&#x2F;ArduinoToBreadboard&#x2F;&quot;&gt;official Arduino tutorial&lt;&#x2F;a&gt; for this step or my &lt;a href=&quot;&#x2F;notebook&#x2F;arduino-cli&#x2F;#flashing-a-bootloader-using-arduino-cli&quot;&gt;tutorial for doing the same using &lt;code&gt;arduino-cli&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pwm-and-timer-counters&quot;&gt;PWM and Timer&#x2F;Counters&lt;&#x2F;h2&gt;
&lt;p&gt;The ATmega328P has 3 so called &lt;i&gt;Timer&#x2F;Counter&lt;&#x2F;i&gt; units, called &lt;i&gt;Timer&#x2F;Counter0&lt;&#x2F;i&gt;, &lt;i&gt;Timer&#x2F;Counter1&lt;&#x2F;i&gt; and &lt;i&gt;Timer&#x2F;Counter2&lt;&#x2F;i&gt; each can drive 2 of 6 PWM channels.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll abbreviate them as &lt;i&gt;t&#x2F;c&lt;&#x2F;i&gt; in the following.&lt;&#x2F;p&gt;
&lt;p&gt;Oversimplified they are counters hooked up to a configurable Clock source. Every time the clock ticks the counter increments by one. Each counter has two comperators to trigger actions when the counter has reached a configured point.&lt;&#x2F;p&gt;
&lt;p&gt;The actions that can be triggered may be:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Interrupts to the CPU&lt;&#x2F;li&gt;
&lt;li&gt;A PWM Signal that can be output on a pin.&lt;&#x2F;li&gt;
&lt;li&gt;Reset the counter&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;i&gt;t&#x2F;c0&lt;&#x2F;i&gt; and &lt;i&gt;t&#x2F;c2&lt;&#x2F;i&gt; have an 8-bit counter while &lt;i&gt;t&#x2F;c1&lt;&#x2F;i&gt; has a 16-bit counter (it can be put in 8-bit and 12-bit modes too).&lt;&#x2F;p&gt;
&lt;p&gt;The clock source itself is too complicated to explain as there are many options, best explained by the official Datasheet. Usually the system clock is used. Each &lt;i&gt;t&#x2F;c&lt;&#x2F;i&gt; also has a prescaler that can be used to make it run slower than the system by a certain factor.&lt;&#x2F;p&gt;
&lt;p&gt;There are a few modes the counters can be in:&lt;&#x2F;p&gt;




	


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

	
		&lt;dt&gt;
		Normal
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Simply counts up and does something when the timer overflows and wraps back to 0
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Clear Timer on Compare Match (CTC)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Counts up, but wraps to 0 and does something when the value in ine of the control registers matches
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Fast PWM
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Counts up, on a first match the PWM signal is turned off, on a second or overflow it is turned back on.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Phase Correct PWM Mode, Timing Diagram
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Counts up and instead of wrapping down again, the PWM signal is toggled at a comparison point.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;For The Phase Correct PWM Mode, the center of the wave always stays in the same place, which is good for driving motors. It also effectively halfes the frequency of the &lt;i&gt;t&#x2F;c&lt;&#x2F;i&gt; in addition to whatever the prescaler does.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pwm-and-arduino&quot;&gt;PWM and Arduino&lt;&#x2F;h3&gt;
&lt;p&gt;The Arduino has 6 PWM capable pins, each connected to one comparator channel of the three timers.&lt;&#x2F;p&gt;
&lt;p&gt;By default all of the &lt;i&gt;t&#x2F;cs&lt;&#x2F;i&gt; are in 8-bit resolution Phase Correct PWM with clock division by 64 in the prescaler, wrapping at the 8-bit overflow (after 256 cycles), which reults in a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.arduino.cc&#x2F;reference&#x2F;en&#x2F;language&#x2F;functions&#x2F;analog-io&#x2F;analogwrite&#x2F;&quot;&gt;PWM frequency of ~490Hz&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The exception here is &lt;i&gt;t&#x2F;c0&lt;&#x2F;i&gt; driving the counter behind the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.arduino.cc&#x2F;reference&#x2F;en&#x2F;language&#x2F;functions&#x2F;time&#x2F;millis&#x2F;&quot;&gt;&lt;code&gt;millis()&lt;&#x2F;code&gt; function&lt;&#x2F;a&gt; (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arduino&#x2F;ArduinoCore-avr&#x2F;blob&#x2F;master&#x2F;cores&#x2F;arduino&#x2F;wiring.c#L65&quot;&gt;code&lt;&#x2F;a&gt;) is in Fast PWM Mode. This is why Arduino Pins 5 and 6 have double the frequency at ~980Hz.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Calculation of how to get from the System frequency to the PWM frequency.&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;16MHz &#x2F; 64 &#x2F; 2 &#x2F; 256 = ~488Hz&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;h4 id=&quot;tone-generation&quot;&gt;Tone generation&lt;&#x2F;h4&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; uses &lt;i&gt;t&#x2F;c2&lt;&#x2F;i&gt; which is why it messes up the PWM on Arduino Pins 3 and 11.&lt;&#x2F;p&gt;
&lt;p&gt;&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;Tone.cpp#L243&quot;&gt;The underlying code&lt;&#x2F;a&gt; adjusts the clock divider and &lt;i&gt;Compare Registers&lt;&#x2F;i&gt; to select an appropriate one for the desired frequency, and puts the timer into &lt;i&gt;Clear Timer on Compare Match&lt;&#x2F;i&gt; Mode to generate generate an unmodulated square wave.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Assumption:&lt;&#x2F;b&gt; Based on how the code looks it is now capable of using any PWM pin for the tone generating and mess up the the corresponding other PWM pin. I&#x27;ve not bothered to prove or disprove this yet, do your own research.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;avoiding-pwm-flickering&quot;&gt;Avoiding PWM flickering&lt;&#x2F;h4&gt;
&lt;p&gt;When connecting an RGB LED to a combination of &lt;i&gt;t&#x2F;c0&lt;&#x2F;i&gt; and one of the other timers to generate pretty colors you might notice an annoying flicker which comes from you seeing some interference pattern that stems from the different modes.&lt;&#x2F;p&gt;
&lt;p&gt;Since you really shouldn&#x27;t change the configuration of &lt;i&gt;t&#x2F;c0&lt;&#x2F;i&gt; you can put one of the other timers in Fast PWM mode (the LEDs don&#x27;t care).&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example that put&#x27;s &lt;i&gt;t&#x2F;c1&lt;&#x2F;i&gt; into Fast PWM mode given the Arduino default configuration.&lt;&#x2F;figcaption&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;&#x2F;figure&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-action-next&quot; href=&quot;&amp;#x2F;blog&amp;#x2F;fixing-pwm-flicker&amp;#x2F;&quot; &gt;
		See the blogpost on fixing PWM flicker for an explanation why it flickers.
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;useful-resources&quot;&gt;Useful 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;www.microchip.com&#x2F;en-us&#x2F;product&#x2F;atmega328p#document-table&quot;&gt;ATmega328P Datasheet and Application Notes&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;www.microchip.com&#x2F;DS40002061B&quot;&gt;Direct Link to the ATmega328P Datasheet&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.arduino.cc&#x2F;hardware&#x2F;uno-rev3&#x2F;&quot;&gt;Arduino UNO R3 Documentation&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;github.com&#x2F;arduino&#x2F;ArduinoCore-avr&#x2F;tree&#x2F;master&quot;&gt;ArduinoCore-avr Source Code&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&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>XML XPath</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/notebook/xpath/"/>
        <id>https://slatecave.net/notebook/xpath/</id>
        
        <summary type="text">Query elements from XML Documents, on the CLI, in the Browser, everywhere else</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/xpath/">&lt;p&gt;XPath is a way to select tags from an XML Document, it works a bit like a File-path and a bit like a CSS-Selector.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;example-document&quot;&gt;Example Document&lt;&#x2F;h2&gt;
&lt;p&gt;For the Examples on this page the following XML-Document is assumed.&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;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;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;catalog&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;title&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;An Overview of Dataformats.&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;title&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;use&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;Describes Dataformats&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;use&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;item&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;name&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;JSON&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;name&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;use&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;Data Serialization&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;use&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;link&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;website&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;https:&#x2F;&#x2F;www.json.org&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;link&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;wikipedia&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;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;JSON&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;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;item&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;item&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;name&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;XML&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;name&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;use&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;Documents&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;use&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;link&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;website&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;https:&#x2F;&#x2F;www.xml.com&#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; &#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;link&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;wikipedia&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;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;XML&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;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;item&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;catalog&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 class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-action-download&quot; href=&quot;.&amp;#x2F;xpath-example.xml&quot;  download&gt;
		Download Example XML
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;querying-nodes&quot;&gt;Querying Nodes&lt;&#x2F;h2&gt;
&lt;p&gt;XPath works like a file-path.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; To address the &lt;code&gt;title&lt;&#x2F;code&gt; tag (&lt;i&gt;node&lt;&#x2F;i&gt; in XPath terminology).&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;&#x2F;catalog&#x2F;title&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; If you are inside a context (i.e. one of the &lt;code&gt;item&lt;&#x2F;code&gt; elements) you can also use it like a relative file-path: &lt;code&gt;use&lt;&#x2F;code&gt;, &lt;code&gt;.&#x2F;name&lt;&#x2F;code&gt;, &lt;code&gt;..&#x2F;title&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For wildcards on a single level one can use a &lt;code&gt;*&lt;&#x2F;code&gt; for the tag name.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; Same as previous but with a wildcard.&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;&#x2F;*&#x2F;title&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;By using &lt;code&gt;&#x2F;&#x2F;&lt;&#x2F;code&gt; one can select all elements matching the following description no matter where in the document they are.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; Select &lt;em&gt;all&lt;&#x2F;em&gt; three &lt;code&gt;use&lt;&#x2F;code&gt; tags.&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;&#x2F;&#x2F;use&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; Using the &lt;code&gt;&#x2F;&#x2F;&lt;&#x2F;code&gt; like &lt;code&gt;&#x2F;catalog&#x2F;&#x2F;use&lt;&#x2F;code&gt; will also work.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;querying-attributes&quot;&gt;Querying Attributes&lt;&#x2F;h2&gt;
&lt;p&gt;To get the attribute of a tag use the notation &lt;code&gt;&#x2F;path&#x2F;to&#x2F;tag&#x2F;@attribute&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; To get the &lt;code&gt;href&lt;&#x2F;code&gt; attributes:&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;&#x2F;&#x2F;link&#x2F;@href&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;dealing-with-multiple-elements-using-predicates&quot;&gt;Dealing with Multiple Elements using Predicates&lt;&#x2F;h2&gt;
&lt;p&gt;When specifying an XPath that yields multiple results maybe one wants not all elements that match a tag.&lt;&#x2F;p&gt;
&lt;p&gt;Writing a number in square brackets will work similar to an array access in a programming language: &lt;code&gt;tag[1]&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; Select the second &lt;code&gt;item&lt;&#x2F;code&gt; from the catalog:&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;&#x2F;catalog&#x2F;item[2]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In place of the Number on can also use expressions like &lt;code&gt;last()&lt;&#x2F;code&gt;, &lt;code&gt;last()-1&lt;&#x2F;code&gt; or &lt;code&gt;position()&amp;gt;1&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;One can also compare against attributes here.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; Only select the &lt;code&gt;type=&quot;wikipedia&quot;&lt;&#x2F;code&gt; links.&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;&#x2F;&#x2F;link[@type=&amp;#39;wikipedia&amp;#39;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;b&gt;Note on Quoting:&lt;&#x2F;b&gt; XPath uses single quotes so one can easily use it in XML attributes.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Boolean Logic:&lt;&#x2F;b&gt; Boolean logic is implemented with the &lt;code&gt;and&lt;&#x2F;code&gt; and &lt;code&gt;or&lt;&#x2F;code&gt; keywords and the &lt;code&gt;not()&lt;&#x2F;code&gt; function.&lt;&#x2F;p&gt;
&lt;p&gt;These are way more possibilities, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.w3schools.com&#x2F;xml&#x2F;xpath_syntax.asp&quot;&gt;w3schools tutorial has a more complete predicate list&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;getting-node-text&quot;&gt;Getting Node Text&lt;&#x2F;h2&gt;
&lt;p&gt;To get the text inside a node use &lt;code&gt;&#x2F;text()&lt;&#x2F;code&gt; to select it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; Get the text from the first items name:&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;&#x2F;&#x2F;item[1]&#x2F;name&#x2F;text()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;getting-attribute-text&quot;&gt;Getting Attribute Text&lt;&#x2F;h2&gt;
&lt;p&gt;To get the text from an attribute one can prefix the attribute name with an &lt;code&gt;@&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; To get all URLs from the link elements from the example document.&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;&#x2F;&#x2F;link&#x2F;@href&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; This will select the attribute as a key-value pair in some contexts. To only get the value wrap it like this: &lt;code&gt;concat(&#x27;&#x27;,&#x2F;&#x2F;link&#x2F;@href)&lt;&#x2F;code&gt; (only keeps first element) or &lt;code&gt;string-join(&#x2F;&#x2F;link&#x2F;@href,&#x27; &#x27;)&lt;&#x2F;code&gt; (keeps all elements but requires XPath 2 support).&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example using &lt;code&gt;xquilla&lt;&#x2F;code&gt; to output all link URLs from the example document using newlines as delimiters. The &lt;code&gt;printf&lt;&#x2F;code&gt; is used to convert the &lt;code&gt;\n&lt;&#x2F;code&gt; to a real newline.&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-support z-function z-builtin&quot;&gt;printf&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;string-join(&#x2F;&#x2F;link&#x2F;@href,&amp;#39;\n&amp;#39;)&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;xqilla&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;dev&#x2F;stdin&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-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; xpath-example.xml&lt;&#x2F;span&gt;&lt;span&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;combine-multiple-xpaths&quot;&gt;Combine multiple XPaths&lt;&#x2F;h2&gt;
&lt;p&gt;Sometimes the result of one XPath isn&#x27;t enough and you want the combined results of multiple XPaths. (Like a SQL union)&lt;&#x2F;p&gt;
&lt;p&gt;To achieve this you join multiple XPaths using a pipe &lt;code&gt;|&lt;&#x2F;code&gt; character like this: &lt;code&gt; &#x2F;xpath1 | &#x2F;xpath1&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xpath-with-namespaces&quot;&gt;XPath with Namespaces&lt;&#x2F;h2&gt;
&lt;p&gt;If some of your Nodes are part of a namespace, i.e. using the &lt;code&gt;&amp;lt;namespace:tag&amp;gt;&lt;&#x2F;code&gt; syntax or the &lt;code&gt;&amp;lt;tag xmlns=&quot;https:&#x2F;&#x2F;example.org&quot;&amp;gt;&lt;&#x2F;code&gt; attribute&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; When using the &lt;code&gt;xmlns&lt;&#x2F;code&gt; attribute all children of that node are also in that namespace unless declared otherwise.&lt;&#x2F;p&gt;
&lt;p&gt;When a namespace is used you&#x27;ll notice that your usual queries don&#x27;t work for some strange reason.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Snippet of Atom-Feed serving as an example documents here.&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;feed&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; xmlns&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-entity z-other z-attribute-name&quot;&gt; xml&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;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;title&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;slatecave.net&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;title&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 &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;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;feed&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;To get the title one may want to try the XPath &lt;code&gt;&#x2F;feed&#x2F;title&lt;&#x2F;code&gt;, but this will fail because of the namespace.&lt;&#x2F;p&gt;
&lt;p&gt;If it is possible to declare the namespace (i.e. in an XSLT-Sheet) then do that and select the element with the namespace prefix, i.e. &lt;code&gt;&#x2F;atom:feed&#x2F;atom:title&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If it is not possible to declare the Namespace you can work around that by using the &lt;code&gt;local-name()&lt;&#x2F;code&gt; function like this: &lt;code&gt;*[local-name()=&#x27;feed&#x27;]&#x2F;*[local-name()=&#x27;title&#x27;]&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;useful-links&quot;&gt;Useful Links&lt;&#x2F;h2&gt;
&lt;p&gt;All of the following lead to the w3schools page.&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;www.w3schools.com&#x2F;xml&#x2F;xpath_syntax.asp&quot;&gt;XPath Syntax&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;www.w3schools.com&#x2F;xml&#x2F;xpath_operators.asp&quot;&gt;XPath Operators&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;www.w3schools.com&#x2F;xml&#x2F;xsl_functions.asp&quot;&gt;XPath Functions&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;www.w3schools.com&#x2F;xml&#x2F;xpath_intro.asp&quot;&gt;XPath Tutorial&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; There is also the related &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;info&#x2F;rfc9535&quot;&gt;RFC 9535: JSONPath: Query Expressions for JSON&lt;&#x2F;a&gt; that might be interesting.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;playing-with-xpath&quot;&gt;Playing with XPath&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;using-xmllint&quot;&gt;Using &lt;code&gt;xmllint&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;xmllint&quot;&gt;&lt;code&gt;xmllint&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; command line utility can do XPath.&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;xmllint&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;file.xm&lt;&#x2F;span&gt;&lt;span&gt;l&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; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-xpath&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;&amp;lt;xpath&amp;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;&lt;b&gt;Note:&lt;&#x2F;b&gt; &lt;code&gt;xmllint&lt;&#x2F;code&gt; and XPath are the &lt;code&gt;jq&lt;&#x2F;code&gt; of XML, if they seem a bit clunky that is because they have been around for a freaking long time.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Compatibility:&lt;&#x2F;b&gt; &lt;code&gt;xmllint&lt;&#x2F;code&gt; only works with XPath 1.0, if you get an error stating that a function is unregistred, thats why. It mostly affects functions that somehow involve lists.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;using-xquilla&quot;&gt;Using &lt;code&gt;xquilla&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;manpages.org&#x2F;xqilla&quot;&gt;&lt;code&gt;xquilla&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; command line tool supports XPath 2.0 and a whole lot of other XML functionality. It usually reads commands from a file and applies it to an XML-Document.&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;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;&amp;lt;xpath&amp;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; xquilla&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;dev&#x2F;stdin&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-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&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-string&quot;&gt;file.xm&lt;&#x2F;span&gt;&lt;span&gt;l&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;getting-information-from-html&quot;&gt;Getting information from HTML&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; Get the title of the HTML Document.&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;curl&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; https:&#x2F;&#x2F;slatecave.net&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; \&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;xmllint&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;span class=&quot;z-constant&quot;&gt;-html&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-xpath&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;html&#x2F;head&#x2F;title&#x2F;text()&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; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&amp;amp;&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;Example:&lt;&#x2F;b&gt; Get the social media preview description.&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;curl&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; https:&#x2F;&#x2F;slatecave.net&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; \&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;xmllint&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;span class=&quot;z-constant&quot;&gt;-html&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-xpath&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-string&quot;&gt;string(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;	&#x2F;html&#x2F;head&#x2F;meta[@property=&amp;#39;og:description&amp;#39;]&#x2F;@content |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;	&#x2F;html&#x2F;head&#x2F;meta[@property=&amp;#39;og:description&amp;#39;]&#x2F;text()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&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-keyword z-operator&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&amp;amp;&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;Explanation:&lt;&#x2F;b&gt; Both examples use &lt;code&gt;-&lt;&#x2F;code&gt; to tell &lt;code&gt;xmllint&lt;&#x2F;code&gt; to read from its standard input (that is the &lt;code&gt;curl&lt;&#x2F;code&gt; output here), enable the HTML parser with the &lt;code&gt;--html&lt;&#x2F;code&gt; option and discard any errors from &lt;code&gt;xmllint&lt;&#x2F;code&gt; using &lt;code&gt;2&amp;gt;&amp;amp;-&lt;&#x2F;code&gt;, which tells the shell to close the standard error.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Getting your Smartcards and Security Tokens working on Linux</title>
        <published>2023-12-03T00:00:00+00:00</published>
        <updated>2023-12-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/get-security-token-working-on-linux/"/>
        <id>https://slatecave.net/notebook/get-security-token-working-on-linux/</id>
        
        <summary type="text">You just bought a popular Security Token to find it not working out of the box on Linux?</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/get-security-token-working-on-linux/">&lt;p&gt;Most Problems I had with Smartcards or Security Tokens just boiled down to missing packages. (&lt;code&gt;pcsclite&lt;&#x2F;code&gt;, &lt;code&gt;ccid&lt;&#x2F;code&gt; and &lt;code&gt;libfido2&lt;&#x2F;code&gt;)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-are-smartcards-and-security-tokens&quot;&gt;What are Smartcards and Security Tokens?&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Security_token&quot;&gt;Security Tokens&lt;&#x2F;a&gt; are small devices that hold cryptographic keys and implement some number of protocols that make those keys usable without them ever leaving the device.&lt;&#x2F;p&gt;
&lt;p&gt;This behaviour is useful because nobody can steal your key from such a device, even if you plug that Security token into a virus dripping publicly accessible PC (though that doesn&#x27;t mean you should).&lt;&#x2F;p&gt;
&lt;p&gt;Popular security Tokens are: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;YubiKey&quot;&gt;YubiKey&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;Nitrokey&quot;&gt;Nitrokey&lt;&#x2F;a&gt;, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;Tillitis_TKey&quot;&gt;Tillitis TKey&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;Solo&quot;&gt;SoloKey&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; I&#x27;m not qualified to to recommend any Security Tokens, these are just the popular ones I know people will search for.&lt;&#x2F;p&gt;
&lt;p&gt;Smartcards are the same technology in card form, examples include modern bank cards, passports and other credit card sized plastic lumps with a chip baked into them.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll use the terms &lt;i&gt;smartcards&lt;&#x2F;i&gt; and &lt;i&gt;security token&lt;&#x2F;i&gt; interchangeably here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;install-missing-packages&quot;&gt;Install missing Packages&lt;&#x2F;h2&gt;
&lt;p&gt;For some reason the utilities for using Smartcards or  don&#x27;t declare the needed backends as their dependencies, those backends are needed to turn sometime obscure USB or NFC interfaces into APIs that applications can access without worrying about device details.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pcsclite-and-ccid&quot;&gt;&lt;code&gt;pcsclite&lt;&#x2F;code&gt; and &lt;code&gt;ccid&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Middleware to access a smart card using SCard API (PC&#x2F;SC).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pcsclite.apdu.fr&#x2F;&quot;&gt;pcsclite&lt;&#x2F;a&gt; provides a service that takes PC&#x2F;SC hardware and patches it through to userspace. Usually you need a driver for your Smartcards reader which comes in the &lt;code&gt;ccid&lt;&#x2F;code&gt; (sometimes &lt;code&gt;pcsc-ccid&lt;&#x2F;code&gt;) package. (You may need a more specific driver in some cases)&lt;&#x2F;p&gt;
&lt;p&gt;It is needed for the part of the smartcard that can do actual signing and encryption, and is commonly used with OpenPGP or PKCS#11.&lt;&#x2F;p&gt;

	

&lt;ul class=&quot;link-list&quot;&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;Smartcards&quot;&gt;The ArchWiki on Smartcards&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.debian.org&#x2F;Smartcards&quot;&gt;The Debian Wiki on Smartcards&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h3 id=&quot;libfido2&quot;&gt;&lt;code&gt;libfido2&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;FIDO2_Project&quot;&gt;FIDO2&lt;&#x2F;a&gt; (the successor to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Universal_2nd_Factor&quot;&gt;Universal 2nd Factor&lt;&#x2F;a&gt;) provides authentication functionality usually used as a second factor to log into websites.&lt;&#x2F;p&gt;
&lt;p&gt;Usually installing the &lt;code&gt;libfido2&lt;&#x2F;code&gt; package solves the problem that your Security Token is not recognised as it also contains all the needed udev tweaks and so on. Maybe a reboot is necessary.&lt;&#x2F;p&gt;
&lt;p&gt;Testing this functionality with your browser can be done using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;demo.yubico.com&#x2F;&quot;&gt;Yubicos demo page&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;wiki.archlinux.org&amp;#x2F;title&amp;#x2F;Universal_2nd_Factor&quot; &gt;
		The ArchWiki has more information on Universal 2nd Factor and FIDO
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;YubiKey Note:&lt;&#x2F;b&gt; Installing &lt;code&gt;libfido2&lt;&#x2F;code&gt; also fixed the not very helpful &lt;br&gt;&lt;code&gt;WARNING: No OTP HID backend available. OTP protocols will not function&lt;&#x2F;code&gt; &lt;br&gt;for the YubiKey Manager (&lt;code&gt;ykman&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;interesting-things-to-do-with-a-security-token&quot;&gt;Interesting Things to do with a Security Token&lt;&#x2F;h2&gt;
&lt;p&gt;Now that your token is working why not try some interesting things?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Warning:&lt;&#x2F;b&gt; Interesting also means that things can go wrong. Make sure you don&#x27;t lock yourself out of anything important and use a test system if in doubt.&lt;&#x2F;p&gt;
&lt;p&gt;In case you want to use these devices to log into your Linux user or authenticate &lt;code&gt;sudo&lt;&#x2F;code&gt; with one of these Tokens you want to look into PAM Modules. See the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;Universal_2nd_Factor#Authentication_for_user_sessions&quot;&gt;ArchWiki on Authentication for User Sessions with FIDO2&lt;&#x2F;a&gt; for more information.&lt;&#x2F;p&gt;
&lt;p&gt;You can also secure you ssh-keys with these tokens. See the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;SSH_keys#FIDO&#x2F;U2F&quot;&gt;ArchWiki on ssh with FIDO&#x2F;U2F&lt;&#x2F;a&gt; or the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;media.ccc.de&#x2F;v&#x2F;gpn21-28-noch-besser-leben-mit-ssh#t=2116&quot;&gt;Talk &lt;q lang=&quot;de&quot;&gt;Noch besser leben mit SSH&lt;&#x2F;q&gt; (from 35:18)&lt;&#x2F;a&gt; (at the time of writing no English translation is available) held by leyrer at GPN21.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>SQL Cheatsheet</title>
        <published>2023-11-18T00:00:00+00:00</published>
        <updated>2023-11-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/sql/"/>
        <id>https://slatecave.net/notebook/sql/</id>
        
        <summary type="text">Some useful reminders for the occasional SQL user.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/sql/">&lt;h2 id=&quot;sql-query-anatomy&quot;&gt;SQL Query Anatomy&lt;&#x2F;h2&gt;
&lt;p&gt;Most databases don&#x27;t like it when the SQL clauses are out of order.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Operator
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SELECT … FROM …&lt;&#x2F;code&gt; (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.sqlite.org&#x2F;lang_select.html&quot;&gt;SQLite&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;INSERT INTO &amp;lt;table&amp;gt;(…) VALUES (…),…&lt;&#x2F;code&gt; (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.sqlite.org&#x2F;lang_insert.html&quot;&gt;SQLite&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;UPDATE &amp;lt;table&amp;gt; SET …&lt;&#x2F;code&gt; (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.sqlite.org&#x2F;lang_update.html&quot;&gt;SQLite&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;DELETE FROM …&lt;&#x2F;code&gt; (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.sqlite.org&#x2F;lang_delete.html&quot;&gt;SQLite&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;JOIN&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;WHERE&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;GROUP BY&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;HAVING&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;ORDER BY&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;LIMIT&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;OFFSET&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;null-tricks&quot;&gt;NULL Tricks&lt;&#x2F;h2&gt;
&lt;p&gt;Null in SQL is special as it isn&#x27;t equal (&lt;code&gt;=&lt;&#x2F;code&gt;) to anything, not even itself, which seems wiered when coming from programming languages but makes perfect sense when considering that &lt;code&gt;NULL&lt;&#x2F;code&gt; means &quot;unknown value&quot; wich definitely isn&#x27;t equal to another &quot;unknown value&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;If you want null to behave more like in a programming language use the &lt;code&gt;is&lt;&#x2F;code&gt; keyword, where &lt;code&gt;NULL is NULL&lt;&#x2F;code&gt; evaluates to 1 opposed to &lt;code&gt;NULL = NULL&lt;&#x2F;code&gt; which results in a 0.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; For all other values &lt;code&gt;is&lt;&#x2F;code&gt; acts like &lt;code&gt;=&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;To get a default value at the time of a query there is the &lt;code&gt;coalesce()&lt;&#x2F;code&gt; function which takes multiple aruments and returns the first non-null one. (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.sqlite.org&#x2F;lang_corefunc.html#coalesce&quot;&gt;SQLite&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.sqlite.org&#x2F;nulls.html&quot;&gt;SQLite has a page with more null quirks, comparing SQLite to other Database engines.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;join-types&quot;&gt;Join Types&lt;&#x2F;h2&gt;
&lt;figure&gt;
	&lt;figcaption&gt;An example SQL &lt;code&gt;JOIN&lt;&#x2F;code&gt; statement.&lt;&#x2F;figcaption&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&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;FROM&lt;&#x2F;span&gt;&lt;span&gt; bookmark&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 class=&quot;z-keyword&quot;&gt; url&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; url&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;url_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; bookmark&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;url_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;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;What all joins have in common is that they glue the tables together and output the entries where the &lt;code&gt;ON&lt;&#x2F;code&gt; condition is true.&lt;&#x2F;p&gt;
&lt;p&gt;They differ in how they treat entries that don&#x27;t make it into the output because they didn&#x27;t find a &quot;partner&quot; using the &lt;code&gt;ON&lt;&#x2F;code&gt; condition.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


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

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

	
		&lt;dd&gt;
		only returns entries that found a partner.
		&lt;&#x2F;dd&gt;
	

	

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

	
		&lt;dd&gt;
		doesn&#x27;t drop columns from the already present columns, leaving the joined columns &lt;code&gt;NULL&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

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

	
		&lt;dd&gt;
		additionally outputs all entries from the joined table that didn&#x27;t find a partner, leaving the other columns &lt;code&gt;NULL&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

	

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

	
		&lt;dd&gt;
		like a &lt;code&gt;left&lt;&#x2F;code&gt; and &lt;code&gt;right&lt;&#x2F;code&gt; join, returning all of the entries in both tables, joining the ones that are join-able and filling in where joining isn&#x27;t possible.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h3 id=&quot;join-examples&quot;&gt;Join Examples&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; To keep this example as clean as possible types have been omitted, the SQL is intended to be compatible with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sqlite.org&quot;&gt;sqlite3&lt;&#x2F;a&gt;. Use the command &lt;code&gt;sqlite3 :memory:&lt;&#x2F;code&gt; to get an in memory database to play around with.&lt;&#x2F;p&gt;
&lt;p&gt;Assume we have two tables, called &lt;code&gt;left_t&lt;&#x2F;code&gt; and &lt;code&gt;right_t&lt;&#x2F;code&gt; for illustration purposes, bot with a single column.&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; left_t&lt;&#x2F;span&gt;&lt;span&gt;(lc);&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; TABLE&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; right_t&lt;&#x2F;span&gt;&lt;span&gt;(rc);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; left_t &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;VALUES&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;both&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;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;left&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;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; right_t &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;VALUES&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;both&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;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;right&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;Now we can query them an see the results:&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&gt; lc,rc&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; left_t&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; right_t &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;ON&lt;&#x2F;span&gt;&lt;span&gt; lc &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; rc ;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We now performed an inner join of &lt;code&gt;right_t&lt;&#x2F;code&gt; against &lt;code&gt;left_t&lt;&#x2F;code&gt; on the single columns having the same value. The returned record is &lt;code&gt;both|both&lt;&#x2F;code&gt;. Pretty intuitive.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Now we substitute the &lt;code&gt;INNER&lt;&#x2F;code&gt; for a &lt;code&gt;LEFT&lt;&#x2F;code&gt; and the result is:&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;both|both&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;left|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All rows in &lt;code&gt;left_t&lt;&#x2F;code&gt; table mad it through the join, the hole in the &lt;code&gt;right_t&lt;&#x2F;code&gt; was filled with a (blank) &lt;code&gt;NULL&lt;&#x2F;code&gt; field.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; If the &lt;code&gt;right_t&lt;&#x2F;code&gt; table had more columns all of them would be returned as &lt;code&gt;NULL&lt;&#x2F;code&gt;, independent of any &lt;code&gt;DEFAULT&lt;&#x2F;code&gt; or &lt;code&gt;NOT NULL&lt;&#x2F;code&gt; statements.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Similar results for a &lt;code&gt;RIGHT&lt;&#x2F;code&gt; join:&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;both|both&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|right&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now all rows from the &lt;code&gt;right_t&lt;&#x2F;code&gt; table made it through and the missing rows in the &lt;code&gt;left_t&lt;&#x2F;code&gt; table was filled with a &lt;code&gt;NULL&lt;&#x2F;code&gt; entry.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;And for the &lt;code&gt;FULL&lt;&#x2F;code&gt; join:&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;both|both&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;left|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|right&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All rows preserved, the ones that matched got joined that ones without matches had the resulting holes filled with &lt;code&gt;NULL&lt;&#x2F;code&gt; fields.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;database-exploration-commands&quot;&gt;Database Exploration Commands&lt;&#x2F;h2&gt;
&lt;p&gt;You Probably know the problem, a Database you are not familiar with with an engine you don&#x27;t use every day and what were the commands again?&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;caption&gt;Common Tasks and the Corresponding &quot;SQL&quot; Commands.&lt;&#x2F;caption&gt;
&lt;tr&gt;&lt;th&gt;Task&lt;&#x2F;th&gt;&lt;th&gt;MariaDB&lt;&#x2F;th&gt;&lt;th&gt;Postgres&lt;&#x2F;th&gt;&lt;th&gt;SQLite&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;Get Help&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;help&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;\h&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;.help&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;List Data&amp;shy;bases&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;show databases&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;\l&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;.databases&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;Use a Data&amp;shy;base&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;use &amp;lt;data&amp;shy;base&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;\c &amp;lt;data&amp;shy;base&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;Not available&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;List Tables&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;show tables&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;\dt&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;.tables&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;Describe Table&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;describe &amp;lt;table&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;\d &amp;lt;table&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;.schema &amp;lt;table&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;Quit Shell&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;exit&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;\q&lt;&#x2F;code&gt;, &lt;code&gt;quit&lt;&#x2F;code&gt;, &lt;code&gt;exit&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;.q&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Git Cheatsheet(s)</title>
        <published>2023-11-03T00:00:00+00:00</published>
        <updated>2026-05-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/git/"/>
        <id>https://slatecave.net/notebook/git/</id>
        
        <summary type="text">Some of my git knowledge and where it came from.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/git/">&lt;h2 id=&quot;setting-up&quot;&gt;Setting Up&lt;&#x2F;h2&gt;
&lt;p&gt;Before starting to use git one should take the few minutes to set it up.&lt;&#x2F;p&gt;
&lt;p&gt;Set up your personal information that will end up on your commits:&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;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; config&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-global&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; user.email&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;your-mail@example.or&lt;&#x2F;span&gt;&lt;span&gt;g&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;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; config&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-global&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; user.name&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;&amp;lt;Name&amp;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;Set the default branch name to main instead of master
The reasons I do this are because there was some controversy
around the word &quot;master&quot; in this particular context
and and I quickly realised that &quot;main&quot; is way easier to type.&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;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; config&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-global&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; init.defaultBranch&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; main&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You could set your editor for git commit messages (and other git features) using:&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;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; config&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-global&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; core.editor&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;editor-comman&lt;&#x2F;span&gt;&lt;span&gt;d&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;If you (like me) have one editor you want to use for everything: Set the &lt;a href=&quot;&#x2F;notebook&#x2F;env#linux-and-posix&quot;&gt;&lt;code&gt;EDITOR&lt;&#x2F;code&gt; variable in your &lt;code&gt;~&#x2F;.profile&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and git along with a whole range of other programs will pick up on that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;partial-adding&quot;&gt;Partial Adding&lt;&#x2F;h2&gt;
&lt;p&gt;Situation: You have made changes to one file but they should be in two different commits.&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;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; add&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&gt; [&amp;lt;file&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;git will then chop up the changes into &lt;i&gt;hunks&lt;&#x2F;i&gt; and ask interactively if you want to add&#x2F;stage (&lt;code&gt;y&lt;&#x2F;code&gt;) a change, ignore&#x2F;not stage it (&lt;code&gt;n&lt;&#x2F;code&gt;), further subdivide (&lt;code&gt;s&lt;&#x2F;code&gt;) or even edit (&lt;code&gt;e&lt;&#x2F;code&gt;) it. You can use &lt;code&gt;?&lt;&#x2F;code&gt; to get summary of all options currently available.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;undoing-things&quot;&gt;Undoing Things&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;undo-last-commit&quot;&gt;Undo last Commit&lt;&#x2F;h3&gt;
&lt;p&gt;Situation: You just made a git commit but you committed too much by accident.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; If you just messed up the commit message or need to add changes use &lt;code&gt;git commit --amend&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;git reset&lt;&#x2F;code&gt; is your friend here, without any arguments it helps when you &lt;code&gt;git add&lt;&#x2F;code&gt;ed too much, it makes git go into the state it was in at the last commit.&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;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; reset&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; HEAD^1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;book&#x2F;en&#x2F;v2&#x2F;Git-Tools-Revision-Selection#_ancestry_references&quot;&gt;&lt;code&gt;HEAD^1&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; resets git to the parent commit of the last commit. Effectively acting as if the last commit never happened. (You can still recover it using the revlog)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;git reset&lt;&#x2F;code&gt; will only change your git history, not the files you have currently checked out so when using it as described above you won&#x27;t loose any changes.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Warning:&lt;&#x2F;b&gt; &lt;code&gt;git reset --hard&lt;&#x2F;code&gt; is intended to throw changes away and it &lt;em&gt;is&lt;&#x2F;em&gt; possible to loose uncommitted data when using it with the &lt;code&gt;--hard&lt;&#x2F;code&gt; flag enabled.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;restore-file-from-last-commit&quot;&gt;Restore File from last Commit&lt;&#x2F;h3&gt;
&lt;p&gt;Situation: You messed up a file and want to throw away the changes since the last commit.&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;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; restore&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;filenam&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;This is less destructive than &lt;code&gt;git reset --hard&lt;&#x2F;code&gt; which throws away all your changes in a repository since the last commit.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shallow-clone&quot;&gt;Shallow clone&lt;&#x2F;h2&gt;
&lt;p&gt;Situation: You have a very large git repository and limited time and&#x2F;or bandwidth and only need the latest few commits.&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;git clone --depth 3 &amp;lt;url&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This example will only fetch the last 3 commits of the main upstream branch.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shallow-fetch&quot;&gt;Shallow fetch&lt;&#x2F;h3&gt;
&lt;p&gt;In case you need additional branches:&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;git fetch --depth 3 origin &amp;lt;branch&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;git switch --detach FETCH_HEAD&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;git switch --create &amp;lt;branch&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;After that you can use all branches you fetched as usual&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fetching-additional-branches&quot;&gt;Fetching additional Branches&lt;&#x2F;h2&gt;
&lt;p&gt;Sometimes you don&#x27;t get all branches you need with a regular pull, assuming you want to fetch the branch &lt;code&gt;example&lt;&#x2F;code&gt; from &lt;code&gt;origin&lt;&#x2F;code&gt; and use 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-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; fetch the branch&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;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; fetch&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; origin&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; example:example&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; switch to it&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;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; switch&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; example&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You may have noticed that &lt;code&gt;example&lt;&#x2F;code&gt; is mentioned twice in the fetch command, that is because the syntax is &lt;code&gt;{remote branch name}:{local branch name}&lt;&#x2F;code&gt; where you have the opportunity to rename your local branch should you need it (i.e. when working with multiple upstream repositories).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;detecting-changes-in-scripts&quot;&gt;Detecting changes in scripts&lt;&#x2F;h2&gt;
&lt;p&gt;To detect any differences of the working tree to the last commit in shellcripts you can use &lt;code&gt;git status --porcelain=v1&lt;&#x2F;code&gt; which outputs a machine readable summary of the changes or nothing at all if there are no changes.&lt;&#x2F;p&gt;
&lt;p&gt;To only detect changes to files already checked into git, but not new files or changes you have already staged (TL;DR only changes that git diff would show) for the next commit you can use &lt;code&gt;git diff --quiet&lt;&#x2F;code&gt; which exits with a scess exit code when there are no changes and fail if there are changes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;throwing-away-changes&quot;&gt;Throwing away changes&lt;&#x2F;h2&gt;
&lt;p&gt;To get back to the state that is stored in the last commit you can use the following commands.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Warning, destructive commands:&lt;&#x2F;b&gt; These commands arre destructive and you can throw away quite a bit of work with no way to restore it. Use them with care. For a non-destructive version have a look at &lt;code&gt;git stash&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;To revert files already checked into git to their original state if they were altered or deleted you can use &lt;code&gt;git restore &amp;lt;path&amp;gt;&lt;&#x2F;code&gt; where path is a path to a file or whole directory.&lt;&#x2F;p&gt;
&lt;p&gt;To remove new files that are not checked in (&lt;code&gt;git stauts&lt;&#x2F;code&gt; shows them as &quot;Untracked files&quot;) you can use &lt;code&gt;git clean -f &amp;lt;path&amp;gt;&lt;&#x2F;code&gt;, here too, the path argument can point to a file or a directory to operate on.&lt;&#x2F;p&gt;
&lt;p&gt;These commands can be useful if you had all changes checked in and a failed build made a mess of your git worktree.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;other-peoples-git-guides&quot;&gt;Other Peoples Git Guides&lt;&#x2F;h2&gt;
&lt;p&gt;These are guides made by other people that I think are useful.&lt;&#x2F;p&gt;
&lt;p&gt;In &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;drewdevault.com&#x2F;2020&#x2F;04&#x2F;06&#x2F;My-weird-branchless-git-workflow.html&quot;&gt;My unorthodox, branchless git workflow&lt;&#x2F;a&gt; Drew DeVault explains how to make use of the fact that the git history of a repository isn&#x27;t set in stone, I mainly use these tricks to completely avoid a lot of merge conflicts in my personal projects.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git-rebase.io&#x2F;&quot;&gt;Learn to change history with git rebase!&lt;&#x2F;a&gt; is Drew DeVault&#x27;s cheatsheet for altering git history in general and the rebase based workflow in the blogpost liked above.&lt;&#x2F;p&gt;
&lt;p&gt;In &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jvns.ca&#x2F;blog&#x2F;2024&#x2F;02&#x2F;01&#x2F;dealing-with-diverged-git-branches&#x2F;&quot;&gt;Dealing with diverged git branches&lt;&#x2F;a&gt; Julia Evans explains how to deal with the situation that changes happend on both the local and remote git repository. (She explains it better than I do here.)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;media.ccc.de&#x2F;v&#x2F;DiVOC-5-ein_kurzer_blick_in_git#t=0&quot;&gt;Ein kurzer Blick in .git&#x2F;&lt;&#x2F;a&gt; is a talk held at DiVOC in 2020 by Florian Hars diving into the &lt;code&gt;.git&lt;&#x2F;code&gt; directory, unfortunately no one has translated it to English yet.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jvns.ca&#x2F;blog&#x2F;2024&#x2F;01&#x2F;26&#x2F;inside-git&#x2F;&quot;&gt;Inside .git&lt;&#x2F;a&gt; is a blogpost by Julia Evans that also dives into the &lt;code&gt;.git&lt;&#x2F;code&gt; directory and explains what is inside.&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>Environment Variable Zoo</title>
        <published>2023-07-03T00:00:00+00:00</published>
        <updated>2025-10-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/env/"/>
        <id>https://slatecave.net/notebook/env/</id>
        
        <summary type="text">Environment Variables for Linux you might want to know about.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/env/">&lt;p&gt;Environment variables are basically inherited, named arguments to processes with a huge impact on how a Linux or other *nix systems work.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;reading-and-writing-environment-variables&quot;&gt;Reading and Writing Environment Variables&lt;&#x2F;h2&gt;
&lt;p&gt;These commands here should work in almost all shells inspired by the original sh, if you use something else you probably know why and how to use it.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	


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

	
		&lt;dt&gt;
		Read out a single Variable
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;echo &quot;$SHELL&quot;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;printenv SHELL&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		Dump out all variables
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;export&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;printenv&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		Set one or more variable for a single command
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;TZ=UTC LANG=&quot;de_DE.UTF-8&quot; date&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;env TZ=UTC LANG=&quot;de_DE.UTF-8&quot; date&lt;&#x2F;code&gt; Useful program for simpler shells, environment (un)setting and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Chain_loading#Chain_loading_in_Unix&quot;&gt;chain-loading&lt;&#x2F;a&gt;.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		Set a variable for the currently running shell
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;export TZ=UTC&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;declare -x TZ=UTC&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h3 id=&quot;persisting-environment-variables&quot;&gt;Persisting Environment Variables&lt;&#x2F;h3&gt;
&lt;p&gt;For permanently setting some environment variables there are a number of options (Options further up usually can override the ones below them).&lt;&#x2F;p&gt;




	


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

	
		&lt;dt&gt;
		&lt;code&gt;.bashrc&lt;&#x2F;code&gt;, &lt;code&gt;.zshrc&lt;&#x2F;code&gt;, etc.
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Read whenever you start your favourite shell (as a non-login shell), but don&#x27;t assume it has effects on the whole session, it is not made for that.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		Your Desktop Environment
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Desktops like setting environment variables derived from their settings, sometimes for single programs, sometimes for the whole session.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;.profile&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Read on every login (your login manager is responsible for doing this) and applies to your whole session.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;&#x2F;etc&#x2F;profile.d&#x2F;&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;etc&#x2F;profile&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		System-wide &lt;code&gt;.profile&lt;&#x2F;code&gt; equivalent, remember that you are sharing this one with your packages.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		PAM
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;pam&quot;&gt;PAM&lt;&#x2F;a&gt; also sets environment variables.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;The &lt;code&gt;.bashrc&lt;&#x2F;code&gt; (plus friends) and &lt;code&gt;.profile&lt;&#x2F;code&gt; can be reloaded by running &lt;code&gt;source ~&#x2F;&amp;lt;filename&amp;gt;&lt;&#x2F;code&gt; or &lt;code&gt;. ~&#x2F;.filename&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Non POSIX shells usually have their own &lt;code&gt;.profile&lt;&#x2F;code&gt; equivalent, keep that in mind when changing your login shell. Bash for example uses &lt;code&gt;.bash_profile&lt;&#x2F;code&gt; if it exists.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;spying-on-other-processes&quot;&gt;Spying on other Processes&lt;&#x2F;h3&gt;
&lt;p&gt;Sometimes it is useful to know what environment variables a given running process has currently set, if the process belongs to you or you have root rights (use them responsibly) you have access to the file that contains the process environment.&lt;&#x2F;p&gt;
&lt;p&gt;The files path is &lt;code&gt;&#x2F;proc&#x2F;$pid&#x2F;environ&lt;&#x2F;code&gt; (Substitute &lt;code&gt;$pid&lt;&#x2F;code&gt; for the process id), it contains the null separated environment variables.&lt;&#x2F;p&gt;
&lt;p&gt;To get them in a pretty way one can use the command &lt;code&gt;tr &#x27;\0&#x27; &#x27;\n&#x27; &amp;lt; &#x2F;proc&#x2F;$pid&#x2F;environ&lt;&#x2F;code&gt; (pipe it into the tr command which replaces the null separators with newlines).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;htop&lt;&#x2F;code&gt; shows the selected processes environment when pressing &lt;kbd&gt;e&lt;&#x2F;kbd&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; This is the reason why it usually is a bad idea to use environment variables for secrets, they can be read out by any other process running under the same user. When transporting secrets from one program to another use pipes and features of your shell to directly read from and write to them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;finding-documentation&quot;&gt;Finding Documentation&lt;&#x2F;h2&gt;
&lt;p&gt;Outside of the usual online sources: Read the documentation and read the manpages. Most manpages have an &lt;code&gt;ENVIRONMENT&lt;&#x2F;code&gt; section describing which variables the program reads and&#x2F;or sets.&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;environ&quot; &gt;
		environ(7) - The manpage about the basics of environment variables.
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;linux-and-posix&quot;&gt;Linux and Posix&lt;&#x2F;h2&gt;
&lt;p&gt;You can find a more complete list of the standardised variables in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pubs.opengroup.org&#x2F;onlinepubs&#x2F;9799919799&#x2F;basedefs&#x2F;V1_chap08.html&quot;&gt;POSIX (2024, the latest at the time of writing) specification on Environment Variables&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;




	


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

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

	
		&lt;dd&gt;
		Contains the path to your home directory, as simple as that. (Don&#x27;t abuse for constructs like &lt;code&gt;$HOME&#x2F;.config&#x2F;whatever&lt;&#x2F;code&gt;, that&#x27;s what the XDG home variables are for.)
		&lt;&#x2F;dd&gt;
	

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

	
		&lt;dd&gt;
		Contains a colon separated list of paths to directories that contain binary files for running them as commands. Directories closer to the front can override others counting binaries of the same name further back.
		&lt;&#x2F;dd&gt;
	

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

	
		&lt;dd&gt;
		Should contain the currently logged in username. (Fun fact: the kernel doesn&#x27;t know your username)
		&lt;&#x2F;dd&gt;
	

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

	
		&lt;dd&gt;
		Contains the path to the shell binary configured for the current user, this is probably how your favourite terminal emulator knows about your favourite shell without configuration. This may or may not be the shell you are currently using.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;TMPDIR&lt;&#x2F;code&gt; or &lt;code&gt;TMP&lt;&#x2F;code&gt; or &lt;code&gt;TEMP&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Almost always one of them contains a path to the preferred temporary directory. Useful for when every user gets their own directory with proper permissions. &lt;code&gt;TMPDIR&lt;&#x2F;code&gt; is the one in the POSIX standard and should be the preferred one.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;LC_*&lt;&#x2F;code&gt;, &lt;code&gt;LANGUAGE&lt;&#x2F;code&gt;, &lt;code&gt;LANG&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		These contain locale information and are the reason why language settings work across desktop environments. Unfortunately also the reason why you have to restart applications after changing system preferences. For possible values see &lt;code&gt;&#x2F;etc&#x2F;locale.gen&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;etc&#x2F;locale.conf&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Set &lt;code&gt;LANG&lt;&#x2F;code&gt; to &lt;code&gt;C&lt;&#x2F;code&gt; for &quot;no translation at all&quot; usually that results in some form of English.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Standardized in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pubs.opengroup.org&#x2F;onlinepubs&#x2F;9799919799&#x2F;basedefs&#x2F;V1_chap08.html#tag_08_02&quot;&gt;POSIX specification on Internationalization Variables&lt;&#x2F;a&gt;.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;TZ&lt;&#x2F;code&gt; and &lt;code&gt;TZDIR&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		These describe the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;tzset#TZ&quot;&gt;current timezone to use&lt;&#x2F;a&gt; i.e. &lt;code&gt;Europe&#x2F;Berlin&lt;&#x2F;code&gt;. Usually points to a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;tzfile&quot;&gt;tzfile&lt;&#x2F;a&gt; in &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;zoneinfo&#x2F;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

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

	
		&lt;dd&gt;
		A program that can be run using &lt;code&gt;sh -c&lt;&#x2F;code&gt; that shows a textfile (unually in the current terminal)
		&lt;&#x2F;dd&gt;
	

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

	
		&lt;dd&gt;
		Like &lt;code&gt;PAGER&lt;&#x2F;code&gt;, but for your favourite text editor.
		&lt;&#x2F;dd&gt;
	

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

	
		&lt;dd&gt;
		Like &lt;code&gt;PAGER&lt;&#x2F;code&gt;, but for your favourite browser. (Not standardised anywhere, but a well known convention)
		&lt;&#x2F;dd&gt;
	

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

	
		&lt;dd&gt;
		Like &lt;code&gt;PAGER&lt;&#x2F;code&gt;, but for sending E-Mail using a program that is compatible with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pubs.opengroup.org&#x2F;onlinepubs&#x2F;9699919799&#x2F;utilities&#x2F;mailx.html&quot;&gt;&lt;code&gt;mailx&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, originally intended to allow a custom implementation of &lt;code&gt;&#x2F;bin&#x2F;mail&lt;&#x2F;code&gt; on early unixoids. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@inawhilecrocodile&#x2F;115435886425492406&quot;&gt;Thank you to inawhilecrocodile for the reasearch!&lt;&#x2F;a&gt; (It was never standardised)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Contrary to what this page previously stated it does &lt;strong&gt;not&lt;&#x2F;strong&gt; take URLs as argument.
		&lt;&#x2F;dd&gt;
	

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

	
		&lt;dd&gt;
		Contains the name of the terminal, together with the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;terminfo&quot;&gt;terminfo&lt;&#x2F;a&gt; database the capabilities of the terminal can be found out. When remoting into other machines with a less well known terminal &lt;code&gt;xterm&lt;&#x2F;code&gt; is usually a sane fallback.
		&lt;&#x2F;dd&gt;
	

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

	
		&lt;dd&gt;
		When set it indicates who has already shown the motd to prevent showing it twice. Convention, probably best known implementation is the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;man8&#x2F;pam_motd.8&quot;&gt;pam motd module&lt;&#x2F;a&gt;.
		&lt;&#x2F;dd&gt;
	

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

	
		&lt;dd&gt;
		When set to a non-empty string Software shouldn&#x27;t use ANSI-colors unless &lt;strong&gt;explicity&lt;&#x2F;strong&gt; configured to do so. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;no-color.org&#x2F;&quot;&gt;See no-color.org&lt;&#x2F;a&gt;
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; What is meant by run with &lt;code&gt;sh -c&lt;&#x2F;code&gt; is demonstrated pretty well by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;salsa.debian.org&#x2F;debian&#x2F;sensible-utils&#x2F;-&#x2F;blob&#x2F;b555a8db938946c519db3d6d2a25cbe2a39945d2&#x2F;sensible-editor.in#L24&quot;&gt;debians sensible-utils&lt;&#x2F;a&gt;. In a nutshell you lauch your favorite &lt;code&gt;EDITOR&lt;&#x2F;code&gt; using &lt;code&gt;sh -c &quot;$EDITOR \&quot;\$@\&quot;&quot; EDITOR &amp;lt;filepath-goes-here&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; I&#x27;m trying to move the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; and &lt;code&gt;MAILER&lt;&#x2F;code&gt; variables to the XDG section. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lists.freedesktop.org&#x2F;archives&#x2F;xdg&#x2F;2023-October&#x2F;014662.html&quot;&gt;See the related discussion on the xdg mailing list.&lt;&#x2F;a&gt; You&#x27;ll have to dig around a bit to find all mails 😞.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xdg-freedesktop-org&quot;&gt;XDG freedesktop.org&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;wiki&#x2F;&quot;&gt;freedesktop.org&lt;&#x2F;a&gt; hosts quite a few specifications ensuring interoperability of free (software) desktop envoirnments, these specifications happen to contain quite a lot of envoirnment variables.&lt;&#x2F;p&gt;
&lt;p&gt;XDG stands for Cross Desktop Group.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;base-directories&quot;&gt;Base Directories&lt;&#x2F;h3&gt;
&lt;p&gt;These are defines in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;wiki&#x2F;Specifications&#x2F;basedir-spec&#x2F;&quot;&gt;Desktop Base Directories Specification&lt;&#x2F;a&gt;, while the below is an overview and you should always read the specification.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; while one can usually guess where those directories are, it is &lt;em&gt;always&lt;&#x2F;em&gt; a good idea to respect the environment variables instead of blindly writing to some guessed default path, People trying to organise their Home folder will thank you.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	


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

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

	
		&lt;dd&gt;
		Path where applications can dump their non-configuration and non-cache persistence.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Default: &lt;code&gt;$HOME&#x2F;.local&#x2F;share&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

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

	
		&lt;dd&gt;
		Path for storing configuration files (and &lt;em&gt;nothing&lt;&#x2F;em&gt; else!)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Default: &lt;code&gt;$HOME&#x2F;.config&#x2F;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

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

	
		&lt;dd&gt;
		colon &lt;code&gt;:&lt;&#x2F;code&gt; separated list of fallback directories for &lt;code&gt;XDG_DATA_HOME&lt;&#x2F;code&gt; containing default data files. (similar to how &lt;code&gt;$PATH&lt;&#x2F;code&gt; works)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Default: &lt;code&gt; &#x2F;usr&#x2F;local&#x2F;share&#x2F;:&#x2F;usr&#x2F;share&#x2F;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

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

	
		&lt;dd&gt;
		colon &lt;code&gt;:&lt;&#x2F;code&gt; separated list of fallback directories for &lt;code&gt;XDG_CONFIG_HOME&lt;&#x2F;code&gt; containing default configuration files. (similar to how &lt;code&gt;$PATH&lt;&#x2F;code&gt; works). This behaviour is very useful when administrating large desktop systems.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Default: &lt;code&gt;&#x2F;etc&#x2F;xdg&#x2F;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

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

	
		&lt;dd&gt;
		Path for storing cache files, may contain files specific to the current architecture details, may need cleaning up when on a shared filesystem.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Also be used for files that better get lost rather than ending up in a backup by accident, &lt;code&gt;keepassxc&lt;&#x2F;code&gt; is an example for that.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Default: &lt;code&gt;$HOME&#x2F;.cache&#x2F;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

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

	
		&lt;dd&gt;
		For temporary runtime files like sockets, pipes, etc.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Usually: &lt;code&gt;&#x2F;run&#x2F;user&#x2F;&amp;lt;userid&amp;gt;&#x2F;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h3 id=&quot;user-directories&quot;&gt;User Directories&lt;&#x2F;h3&gt;
&lt;p&gt;There is also the possibility to define User directories, these have the form of &lt;code&gt;XDG_&amp;lt;name&amp;gt;_DIR&lt;&#x2F;code&gt; and are configured using the &lt;code&gt;$XDG_CONFIG_HOME&#x2F;user-dirs.dirs&lt;&#x2F;code&gt; and taken care of by the &lt;code&gt;xdg-user-dirs-update&lt;&#x2F;code&gt; utility&lt;&#x2F;p&gt;
&lt;p&gt;Commonly specified ones are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DESKTOP&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;DOWNLOAD&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;TEMPLATES&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;PUBLICSHARE&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;DOCUMENTS&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;MUSIC&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;PICTURES&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;VIDEOS&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;SCREENSHOTS&lt;&#x2F;code&gt; - Used by some popular screenshot scripts&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;current-desktop&quot;&gt;Current Desktop&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;specifications.freedesktop.org&#x2F;desktop-entry-spec&#x2F;desktop-entry-spec-latest.html#recognized-keys&quot;&gt;Freedesktop &quot;Desktop Entry Specification&quot;&lt;&#x2F;a&gt; specifies a &lt;code&gt;XDG_CURRENT_DESKTOP&lt;&#x2F;code&gt; variable that is set from the &lt;code&gt;DesktopNames&lt;&#x2F;code&gt; value in desktops session file (the ones in &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;xsessions&#x2F;&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;wayland-sessions&#x2F;&lt;&#x2F;code&gt; and whatever &lt;code&gt;XDG_DATA_DIRS&lt;&#x2F;code&gt; points to).&lt;&#x2F;p&gt;
&lt;p&gt;It contains a colon separated list of names for the current desktop, primarily intended for activating or deactivating desktop entries (i.e. you don&#x27;t want the gnome settings cluttering up you KDE menus and vice versa).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;xdg-session&quot;&gt;XDG Session&lt;&#x2F;h3&gt;
&lt;p&gt;These variables are &lt;em&gt;not&lt;&#x2F;em&gt; part of a specification, the best source for them is a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;wiki&#x2F;Software&#x2F;systemd&#x2F;writing-display-managers&#x2F;&quot;&gt;guide for &quot;Writing Display Managers&quot; with systemd&lt;&#x2F;a&gt; and the manpage for the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;software&#x2F;systemd&#x2F;man&#x2F;pam_systemd.html&quot;&gt;&lt;code&gt;pam_systemd&lt;&#x2F;code&gt; module&lt;&#x2F;a&gt;. Some of them happen to be useful.&lt;&#x2F;p&gt;




	


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

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

	
		&lt;dd&gt;
		By definition set to &lt;code&gt;seat0&lt;&#x2F;code&gt;, probably reserved for future use in multiseat scenarios.
		&lt;&#x2F;dd&gt;
	

	

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

	
		&lt;dd&gt;
		The best documentation for this one is probably the &lt;code&gt;type&lt;&#x2F;code&gt; argument for the pam_systemd module, roughly it is the display server used for the session. (&lt;code&gt;x11&lt;&#x2F;code&gt;,&lt;code&gt;wayland&lt;&#x2F;code&gt;,&lt;code&gt;mir&lt;&#x2F;code&gt;,&lt;code&gt;tty&lt;&#x2F;code&gt;,&lt;code&gt;unknown&lt;&#x2F;code&gt;)
		&lt;&#x2F;dd&gt;
	

	

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

	
		&lt;dd&gt;
		either &lt;code&gt;user&lt;&#x2F;code&gt; or &lt;code&gt;greeter&lt;&#x2F;code&gt; depending on whether the session is primarily logged in or for logging in.
		&lt;&#x2F;dd&gt;
	

	

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

	
		&lt;dd&gt;
		set to the number of the TTY the current session runs in.
		&lt;&#x2F;dd&gt;
	

	

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

	
		&lt;dd&gt;
		Either the &lt;code&gt;&#x2F;proc&#x2F;self&#x2F;sessionid&lt;&#x2F;code&gt; of the auditing session or an &quot;independent counter&quot;.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;graphical-environment&quot;&gt;Graphical Environment&lt;&#x2F;h2&gt;




	


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

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

	
		&lt;dd&gt;
		The id of your X-Display, on your laptop it is probably &lt;code&gt;:0&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

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

	
		&lt;dd&gt;
		The name of your wayland socket file in &lt;code&gt;$XDG_RUNTIME_DIR&#x2F;&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;XCURSOR_SIZE&lt;&#x2F;code&gt;, &lt;code&gt;XCURSOR_THEME&lt;&#x2F;code&gt;, &lt;code&gt;XCURSOR_PATH&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		One way to configure how your cursor looks in most applications. It even works with wayland for when other options are not available. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;Cursor_themes&quot;&gt;The Arch Wiki has more information on cursor configuration&lt;&#x2F;a&gt;.
		&lt;&#x2F;dd&gt;
	

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

	
		&lt;dd&gt;
		Contains information on how to connect to the dbus session bus. Usually set by the &lt;code&gt;dbus-run-session&lt;&#x2F;code&gt; wrapper.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;mozilla&quot;&gt;Mozilla&lt;&#x2F;h2&gt;
&lt;p&gt;These are used for Firefox, Thunderbird and other programs that are built on top of Firefox.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	


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

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

	
		&lt;dd&gt;
		If set it enables wayland support for all mozilla programs (if not already enabled by default) typically set to &lt;code&gt;1&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;java&quot;&gt;Java&lt;&#x2F;h2&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	


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

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

	
		&lt;dd&gt;
		Tells java if you have a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Re-parenting_window_manager&quot;&gt;non-reparenting window manager&lt;&#x2F;a&gt;, usually set to 0 on modern Xorg, to 1 on wayland. If your window stays blank that is an indicator this one is wrong. (Yes the underscore at the start is intentional.)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Possible Values: &lt;code&gt;0&lt;&#x2F;code&gt;, &lt;code&gt;1&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;More Java environment variables will be added when I run into them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;kubernetes&quot;&gt;Kubernetes&lt;&#x2F;h2&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	


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

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

	
		&lt;dd&gt;
		The path to a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;concepts&#x2F;configuration&#x2F;organize-cluster-access-kubeconfig&#x2F;&quot;&gt;yaml file containing connection information for a Kubernetes cluster&lt;&#x2F;a&gt; that tools like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;reference&#x2F;kubectl&#x2F;&quot;&gt;&lt;code&gt;kubectl&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;helm.sh&quot;&gt;&lt;code&gt;helm&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; can use to connect to the cluster.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		k3s generates a &lt;code&gt;&#x2F;etc&#x2F;rancher&#x2F;k3s&#x2F;k3s.yaml&lt;&#x2F;code&gt; file you can point this at.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Fontconfig</title>
        <published>2023-06-20T00:00:00+00:00</published>
        <updated>2024-08-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/fontconfig/"/>
        <id>https://slatecave.net/notebook/fontconfig/</id>
        
        <summary type="text">Some knowledge that makes your life easier when working with fonts on Linux.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/fontconfig/">&lt;h2 id=&quot;what-is-fontconfig&quot;&gt;What is Fontconfig?&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Fontconfig is a library for configuring and customising font access.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;It is built into almost every program that runs on Linux and has a GUI (even in into old java software!) and makes it very easy to make configure fonts and then forget about it.&lt;&#x2F;p&gt;
&lt;p&gt;You can find the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;wiki&#x2F;Software&#x2F;fontconfig&#x2F;&quot;&gt;Fontconfig documentation over on freedesktop.org&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fontconfig-cli-tools&quot;&gt;Fontconfig CLI Tools&lt;&#x2F;h2&gt;
&lt;p&gt;You average Linux distribution ships Fontconfig along with some commands to poke it.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


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

	
		&lt;dt&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fc-cache&quot;&gt;fc-cache&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		for rebuilding the Fontconfig cache (after installing a font or changing configuration).&lt;br&gt;Usually used like this: &lt;code&gt;fc-cache -f -v&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fc-conflist&quot;&gt;fc-conflist&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		gives you a list of configuration files found by Fontconfig.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fc-match&quot;&gt;fc-match&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		for looking up which exact font a given name resolves to, &lt;em&gt;very&lt;&#x2F;em&gt; useful for debugging.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fc-query&quot;&gt;fc-query&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		to print out metadata for a font file.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fc-validate&quot;&gt;fc-validate&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		can give a hint abut missing glyphs in a font file for a given language.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fc-cat&quot;&gt;fc-cat&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		for having a look at the Fontconfig cache.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fc-list&quot;&gt;fc-list&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		to list all fonts Fontconfig knows about.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fc-pattern&quot;&gt;fc-pattern&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		helps you by parsing &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;FcPatternFormat&quot;&gt;Fontconfig patterns&lt;&#x2F;a&gt; , useful for debugging.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fc-scan&quot;&gt;fc-scan&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		similar to &lt;code&gt;fc-query&lt;&#x2F;code&gt; but has the same output format as &lt;code&gt;fc-pattern&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;font-family-names&quot;&gt;Font (Family) Names&lt;&#x2F;h2&gt;
&lt;p&gt;What one usually calls the fonts name actually is the font family name. This is because of historical reasons from back when a font was a case of lead letters for printing and one needed an extra case full of letters for the bold or italic styles.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;family&lt;&#x2F;code&gt; name is usually what is accepted whenever you have some configuration file and it wants a font name. Usually the postscript name works too. Both can be found out using &lt;code&gt;fc-query&lt;&#x2F;code&gt; on a given font file. &lt;code&gt;fc-list&lt;&#x2F;code&gt; also shows the family name by default.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; &lt;code&gt;fc-query -b&lt;&#x2F;code&gt; omits language and character set information resulting in a more readable output.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Fontconfig also has a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;software&#x2F;fontconfig&#x2F;fontconfig-user.html#AEN36&quot;&gt;specific font name&lt;&#x2F;a&gt; that includes the family name, size, style and every other possible font setting.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;system-fonts&quot;&gt;System Fonts&lt;&#x2F;h2&gt;
&lt;p&gt;System fonts using Fontconfig are just configuration aliasing generic names to your favourite font.&lt;&#x2F;p&gt;
&lt;p&gt;The most common are &lt;code&gt;monopace&lt;&#x2F;code&gt;, &lt;code&gt;sans&lt;&#x2F;code&gt; or &lt;code&gt;sans-serif&lt;&#x2F;code&gt; and &lt;code&gt;serif&lt;&#x2F;code&gt;. If your desktop environment has a font preference dialog in the system settings it is changing which font names those aliases point to.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; There is also a convention to alias &lt;code&gt;emoji&lt;&#x2F;code&gt; to your preferred emoji font.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;finding-fonts-that-support-a-codepoint&quot;&gt;Finding Fonts that Support a Codepoint&lt;&#x2F;h2&gt;
&lt;p&gt;To query fonts that support a given character, you need to know its codepoint.&lt;&#x2F;p&gt;
&lt;p&gt;For the following examples I&#x27;ll use the 😃 emoji which has the codepoint &lt;code&gt;U+1f603&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you have a unicode aware &lt;code&gt;printf&lt;&#x2F;code&gt; command (if you don&#x27;t know simply try):&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;printf&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; %x&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;&amp;#39;😃&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;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which outputs &lt;code&gt;1f603&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There is also the possibility to use the &lt;code&gt;uni&lt;&#x2F;code&gt; 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;uni&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; identify&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;It also lists &lt;code&gt;U+1F603&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_laptop.png&quot; alt=&quot;Less techy neofox says:&quot; class=&quot;chat-avatar&quot; width=&quot;64&quot; height=&quot;64&quot;&gt;
	&lt;blockquote&gt;
&lt;p&gt;You could also look it up using a search engine, Wikipedia or one of those Emoji lookup sites.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;&#x2F;div&gt;



&lt;hr &#x2F;&gt;
&lt;p&gt;You have the codepoint? Great, now you can ask the &lt;code&gt;fc-list&lt;&#x2F;code&gt; command (replace the example &lt;code&gt;1f603&lt;&#x2F;code&gt; with the codepoint you are interested in.)&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;fc-list&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; :charset=1f603&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You should get a list of all fonts on your system that support that codepoint, if that codepoint isn&#x27;t supported by a font on your system the output stays empty.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;configuration-files&quot;&gt;Configuration Files&lt;&#x2F;h2&gt;
&lt;p&gt;You can find your Fontconfig configuration file in &lt;code&gt;$XDG_CONFIG_DIR&#x2F;fontconfig&lt;&#x2F;code&gt; which in most cases is &lt;code&gt;~&#x2F;.config&#x2F;fontconfig&#x2F;&lt;&#x2F;code&gt;. In there you can either directly edit the &lt;code&gt;fonts.conf&lt;&#x2F;code&gt; file or a file in the &lt;code&gt;conf.d&lt;&#x2F;code&gt; folder.&lt;&#x2F;p&gt;
&lt;p&gt;Global Configuration is in &lt;code&gt;&#x2F;etc&#x2F;fonts&#x2F;&lt;&#x2F;code&gt;. It usually consists of files linked from &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;fontconfig&#x2F;&lt;&#x2F;code&gt; where configuration snippets for common settings are installed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;using-predefined-configuration&quot;&gt;Using Predefined Configuration&lt;&#x2F;h3&gt;
&lt;p&gt;fc-conflist will list all configuration files for fontconfig, the active ones prefixed with &lt;code&gt;+&lt;&#x2F;code&gt; the inactive ones in &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;fontconfig&#x2F;&lt;&#x2F;code&gt; prefixed with a &lt;code&gt;-&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;To use the predefined configuration symlink 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;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;usr&#x2F;share&#x2F;fontconfig&#x2F;conf.avail&#x2F;70-no-bitmaps.conf&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;etc&#x2F;fonts&#x2F;conf.d&#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;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Regenerate the fonconfig cache&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;fc-cache&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;fv&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; The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.voidlinux.org&#x2F;config&#x2F;graphical-session&#x2F;fonts.html&quot;&gt;Void Linux Documentation on Fonts&lt;&#x2F;a&gt; gives a similar example on linking the &lt;code&gt;70-no-bitmaps.conf&lt;&#x2F;code&gt; to &lt;code&gt;&#x2F;etc&#x2F;fonts&#x2F;conf.d&#x2F;&lt;&#x2F;code&gt; to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;void-linux&#x2F;void-packages&#x2F;issues&#x2F;22477&quot;&gt;mitigate the common issue that Firefox picks pixelated bitmap fonts&lt;&#x2F;a&gt; over higher quality ones.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;writing-custom-configuration&quot;&gt;Writing Custom Configuration&lt;&#x2F;h3&gt;
&lt;p&gt;To make a valid Fontconfig configuration file it needs some &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;software&#x2F;fontconfig&#x2F;fontconfig-user.html#AEN53&quot;&gt;XML scaffolding&lt;&#x2F;a&gt;:&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;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-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-keyword&quot;&gt;DOCTYPE&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; fontconfig&lt;&#x2F;span&gt;&lt;span&gt; SYSTEM &amp;quot;urn:fontconfig:fonts.dtd&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;fontconfig&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; actual configuration goes 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;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;fontconfig&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;That scaffold can now be filled with some rules.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; In case you are in need of an example, have a look at the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;software&#x2F;fontconfig&#x2F;fontconfig-user.html#AEN252&quot;&gt;Fontconfig documentation examples&lt;&#x2F;a&gt; or &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;fontconfig&quot;&gt;my Fontconfig&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Those rule could be disabling fonts without having to uninstall them, useful when one doesn&#x27;t have root access or wants to keep the font files for some reason.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example rule for disabling FontAwesome to prevent it from interfering with other icon-fonts.&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;selectfont&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;rejectfont&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;pattern&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;patelt&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; name&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;family&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;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;string&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;FontAwesome&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;string&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;patelt&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;pattern&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; More pattern instances for other fonts can go 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;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;rejectfont&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;selectfont&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;Another application is to configure the system fonts mentioned before using &lt;code&gt;alias&lt;&#x2F;code&gt; rules. For setting the &lt;code&gt;emoji&lt;&#x2F;code&gt; (or any other) font one simply maps the family name &lt;code&gt;emoji&lt;&#x2F;code&gt; to the family name of the preferred font.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;An example rule for aliasing &lt;code&gt;emoji&lt;&#x2F;code&gt; to &lt;code&gt;Twemoji&lt;&#x2F;code&gt;.&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;alias&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;family&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;emoji&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;family&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;prefer&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;family&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;Twemoji&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;family&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;prefer&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;alias&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;figcaption&gt;The same mechanism could be used to alias something like &lt;code&gt;Helvetica&lt;&#x2F;code&gt; to your favourite sans font family.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The &lt;code&gt;prefer&lt;&#x2F;code&gt; part of the alias also accepts multiple font families, this can be used to apply the &lt;code&gt;emoji&lt;&#x2F;code&gt; alias to system fonts by telling Fontconfig to prefer your favourite monospace, sans or serif font &lt;b&gt;and&lt;&#x2F;b&gt; whatever is aliased to &lt;code&gt;emoji&lt;&#x2F;code&gt; over all other installed fonts.&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;alias&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;family&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;monospace&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;family&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;prefer&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;family&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;Noto Sans Mono&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;family&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;family&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;emoji&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;family&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;prefer&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;alias&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;Of course there are more applications for Fontconfig configurations, but those probably deserve more specific pages.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; I&#x27;m using Twemoji and Noto Sans, not because I like the companies that made them, but because they are under a permissive license are mostly complete, look okay and are one of the very few things those companies are actually doing good for humanity.&lt;&#x2F;p&gt;
&lt;p&gt;There are community made fonts like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rsms.me&#x2F;inter&#x2F;&quot;&gt;Inter&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;openmoji.org&#x2F;&quot;&gt;OpenMoji&lt;&#x2F;a&gt; (plus some more) out there with an impressive coverage and feature set that shouldn&#x27;t go unmentioned here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;installing-fonts&quot;&gt;Installing fonts&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;from-packages&quot;&gt;From packages&lt;&#x2F;h3&gt;
&lt;p&gt;Usually the packages do everything for you and even install some sane Fontconfig rules.&lt;&#x2F;p&gt;
&lt;p&gt;If they don&#x27;t, have a look at the Fontconfig files they install and check if they clash with your own configuration or if the package description, documentation or comment in its Fontconfig file mentions additional required configuration. This is usually the case for emoji fonts to get them as the default font.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;from-files&quot;&gt;From files&lt;&#x2F;h3&gt;
&lt;p&gt;In case you have a modern Linux distribution you can install a font by dropping the ttf or otf file into &lt;code&gt;~&#x2F;.local&#x2F;share&#x2F;fonts&#x2F;&lt;&#x2F;code&gt; or &lt;code&gt;~&#x2F;.fonts&#x2F;&lt;&#x2F;code&gt;, the latter works on older distributions too.&lt;&#x2F;p&gt;
&lt;p&gt;To apply the changes run &lt;code&gt;fc-cache -f -v&lt;&#x2F;code&gt; to rebuild the Fontconfig cache and restart your applications (or log out and back in).&lt;&#x2F;p&gt;
&lt;p&gt;Before restarting applications you could verify that the font was installed using the &lt;code&gt;fc-match&lt;&#x2F;code&gt; and &lt;code&gt;fc-list&lt;&#x2F;code&gt; commands.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;converting-woff2-to-ttf&quot;&gt;Converting woff2 to ttf&lt;&#x2F;h4&gt;
&lt;p&gt;Google (who else 🙄) made a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;google&#x2F;woff2&quot;&gt;tool for converting woff2 fonts to ttf and back&lt;&#x2F;a&gt;, the package containing it is simply called &lt;code&gt;woff2&lt;&#x2F;code&gt; in most Linux distributions.&lt;&#x2F;p&gt;
&lt;p&gt;It contains the &lt;code&gt;woff2_decompress&lt;&#x2F;code&gt; command, give it a path to a woff2 font file and it will convert it to ttf. There is also a &lt;code&gt;woff2_compress&lt;&#x2F;code&gt; that works in the other direction.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>ANSI Escape Sequences</title>
        <published>2023-05-28T00:00:00+00:00</published>
        <updated>2024-04-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/ansi-escape-sequences/"/>
        <id>https://slatecave.net/notebook/ansi-escape-sequences/</id>
        
        <summary type="text">The things that make your Terminal colourful</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/ansi-escape-sequences/">&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ANSI_escape_code&quot;&gt;ANSI Escape sequences&lt;&#x2F;a&gt; are some standardised byte sequences that make most Terminals do some thing other than just printing characters on screen. Like printing colourful characters!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;using-ansi-escape-sequences&quot;&gt;Using ANSI-Escape Sequences&lt;&#x2F;h2&gt;
&lt;p&gt;ANSI-Escape sequences can be used by writing an ASCII-Escape characters followed by some instructions to your Programs standard output or standard error.&lt;&#x2F;p&gt;
&lt;p&gt;The codes can be produced by any string escaping mechanism. In proper programming and scripting languages the syntax for the escape character in double quoted strings is usually &lt;code&gt;\x1b&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The shell being a bit different you should use either &lt;code&gt;printf&lt;&#x2F;code&gt; or &lt;code&gt;echo -e&lt;&#x2F;code&gt;. While most systems support both the chance of &lt;code&gt;printf&lt;&#x2F;code&gt; being supported is usually higher.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;using-ansi-escape-sequences-responsibly&quot;&gt;Using ANSI-Escape Sequences Responsibly&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;b&gt;While colors and text effects are pretty:&lt;&#x2F;b&gt; You should consider the cases where they shouldn&#x27;t be enabled and respect user choices.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Disable color output by default when the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;no-color.org&quot;&gt;&lt;code&gt;NO_COLOR&lt;&#x2F;code&gt; environment variable is non-empty&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Disable use of escape codes by default when the output isn&#x27;t a terminal.&lt;&#x2F;li&gt;
&lt;li&gt;Don&#x27;t overuse color, it gets confusing pretty quickly.&lt;&#x2F;li&gt;
&lt;li&gt;Make sure no information gets lost when disabling escape codes.
&lt;ul&gt;
&lt;li&gt;Does using grep on the output work to find information?&lt;&#x2F;li&gt;
&lt;li&gt;Does piping it into &lt;code&gt;espeak-ng&lt;&#x2F;code&gt; give a sensible result?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;When writing &lt;q&gt;by default&lt;&#x2F;q&gt; that means unless &lt;strong&gt;explicitly&lt;&#x2F;strong&gt; requested (i.e. through a &lt;code&gt;--color always&lt;&#x2F;code&gt; CLI option).&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;seirdy.one&amp;#x2F;posts&amp;#x2F;2022&amp;#x2F;06&amp;#x2F;10&amp;#x2F;cli-best-practices&amp;#x2F;&quot; &gt;
		Seirdy about &lt;q&gt;Best practices for inclusive CLIs&lt;&#x2F;q&gt;.
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Thank you to Seirdy!&lt;&#x2F;b&gt; Most of the above is a summarized and filtered TL;DR of Seirdys article, you should definitely read it!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-text-color-and-effects&quot;&gt;Setting Text Color and Effects&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ll focus on Select Graphic Rendition (SGR) codes here, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ANSI_escape_code#CSIsection&quot;&gt;Wikipedia has a more complete list&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;An example for the shell which prints out &lt;q&gt;_FOOBAR_&lt;&#x2F;q&gt; in bright (&lt;code&gt;01&lt;&#x2F;code&gt;) red (&lt;code&gt;31&lt;&#x2F;code&gt;). After that, it resets to default settings (&lt;code&gt;00&lt;&#x2F;code&gt;) and prints a newline character (&lt;code&gt;\n&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-support z-function z-builtin&quot;&gt;printf&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;\033[01;31m_FOOBAR_\033[00m\n&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-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; With %s placeholder&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;printf&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;\033[01;31m%s\033[00m\n&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;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;_FOOBAR_&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;As you can see in the example, multiple escape-codes can be chained in one sequence by delimiting them with a &lt;code&gt;;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;\033&lt;&#x2F;code&gt; part will turn into an ASCII escape character, the &lt;code&gt;[&lt;&#x2F;code&gt; tells the terminal that this is an ANSI Control Sequence Initiator (CSI) after that can come one or more codes. The sequence is finished by an &lt;code&gt;m&lt;&#x2F;code&gt; to tell the terminal that everything between the &lt;abbr title=&quot;Control Sequence Initiator&quot;&gt;CSI&lt;&#x2F;abbr&gt; and it are &lt;abbr title=&quot;Select Graphic Rendition&quot;&gt;SGR&lt;&#x2F;abbr&gt; codes.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note on the &lt;code&gt;\033&lt;&#x2F;code&gt; and compatibility:&lt;&#x2F;b&gt; The &lt;code&gt;\033&lt;&#x2F;code&gt; is the octal representation of the Escape ASCII-Character and is the most widely supported one across &lt;code&gt;printf&lt;&#x2F;code&gt; implementations. (The dash printf implementation for example doesn&#x27;t support hexadecimal representation.)&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;caption&gt;Most commonly used codes&lt;&#x2F;caption&gt;
&lt;tr&gt;
&lt;th&gt;Code&lt;&#x2F;th&gt;
&lt;th&gt;Code to reverse&lt;&#x2F;th&gt;
&lt;th&gt;Effect&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;00&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;Reset all effects&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;3&quot; class=&quot;table-subheading&quot;&gt;Foreground color&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;30-37&lt;&#x2F;td&gt;&lt;td&gt;39&lt;&#x2F;td&gt;&lt;td&gt;Set the Foreground color&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;90-97&lt;&#x2F;td&gt;&lt;td&gt;39&lt;&#x2F;td&gt;&lt;td&gt;Set the Foreground color, bright variation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;3&quot; class=&quot;table-subheading&quot;&gt;Background color&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;40-47&lt;&#x2F;td&gt;&lt;td&gt;49&lt;&#x2F;td&gt;&lt;td&gt;Set the Background color&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;100-107&lt;&#x2F;td&gt;&lt;td&gt;49&lt;&#x2F;td&gt;&lt;td&gt;Set the Background color, bright variation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;3&quot; class=&quot;table-subheading&quot;&gt;Font effects&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;01&lt;&#x2F;td&gt;&lt;td&gt;22&lt;&#x2F;td&gt;&lt;td&gt;Bright text (usually bold and brighter color)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;02&lt;&#x2F;td&gt;&lt;td&gt;22&lt;&#x2F;td&gt;&lt;td&gt;Dim text (usually only effect on color)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;03&lt;&#x2F;td&gt;&lt;td&gt;23&lt;&#x2F;td&gt;&lt;td&gt;Italic text&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;04&lt;&#x2F;td&gt;&lt;td&gt;24&lt;&#x2F;td&gt;&lt;td&gt;Underlined text&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;05&lt;&#x2F;td&gt;&lt;td&gt;25&lt;&#x2F;td&gt;&lt;td&gt;Blinking text&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;07&lt;&#x2F;td&gt;&lt;td&gt;27&lt;&#x2F;td&gt;&lt;td&gt;Reverse foreground and background color&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;08&lt;&#x2F;td&gt;&lt;td&gt;28&lt;&#x2F;td&gt;&lt;td&gt;Concealed&#x2F;Hidden text&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;09&lt;&#x2F;td&gt;&lt;td&gt;29&lt;&#x2F;td&gt;&lt;td&gt;Strikethrough text&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; The leading zeros are not needed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;colors&quot;&gt;Colors&lt;&#x2F;h3&gt;
&lt;p&gt;You may have noticed that the &lt;code&gt;3x&lt;&#x2F;code&gt;, &lt;code&gt;4x&lt;&#x2F;code&gt;, &lt;code&gt;9x&lt;&#x2F;code&gt;, and &lt;code&gt;10x&lt;&#x2F;code&gt; family of codes all use the last digit in the range from 0 to 7 as a color parameter.&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;caption&gt;Table of colors&lt;&#x2F;caption&gt;
&lt;tr&gt;&lt;th&gt;Code&lt;&#x2F;th&gt;&lt;th&gt;Color&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;Black or Dark Grey&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;Red&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;td&gt;Green&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;&#x2F;td&gt;&lt;td&gt;Yellow&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;4&lt;&#x2F;td&gt;&lt;td&gt;Blue&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;5&lt;&#x2F;td&gt;&lt;td&gt;Violet &#x2F; Purple&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;6&lt;&#x2F;td&gt;&lt;td&gt;Cyan or Turquoise&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;7&lt;&#x2F;td&gt;&lt;td&gt;Light Grey or White&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;Please keep in mind that different people have different color palettes when you want to write reusable scripts.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Unobtanium Search</title>
        <published>2023-05-27T00:00:00+00:00</published>
        <updated>2023-05-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/unobtanium/"/>
        <id>https://slatecave.net/creations/unobtanium/</id>
        
        <summary type="text"> A stand alone and independent search engine with its own crawler.</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/unobtanium/">&lt;h2 id=&quot;what-is-unobtanium&quot;&gt;What is Unobtanium?&lt;&#x2F;h2&gt;
&lt;p&gt;Unobtanium is a stand alone web search engine for making a searchbox for your own little corner of the web. It tries to be easy to self-host and maintain.&lt;&#x2F;p&gt;
&lt;p&gt;It doesn&#x27;t independent on external services and uses SQLite as its database.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Why SQLite?:&lt;&#x2F;b&gt; SQLite is great because it doesn&#x27;t require most of the typical database administration chores, it is simply a file that is managed by unobtanium that requires no special tools for backing it up, moving, etc.&lt;&#x2F;p&gt;
&lt;p&gt;Under the hood there is a three step pipeline of crawling, summarising and serving the search frontend. (No feedback loop yet …, though the crawler can remember the last crawl)&lt;&#x2F;p&gt;
&lt;p&gt;Th search algorithm is currently a pretty simple &quot;bag of words&quot; type, ranked using bm25 and a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.unobtanium.rocks&#x2F;algorithm&#x2F;calculating-indexiness&#x2F;&quot;&gt;custom score that prefers leaf pages over navigation pages&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For more information please refer to the technical documentation as it is likely more up to date.&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;doc.unobtanium.rocks&quot; &gt;
		Unobtanium Documentation
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;unobtanium-rocks&quot;&gt;unobtanium.rocks&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;unobtanium.rocks&quot;&gt;unobtanium.rocks&lt;&#x2F;a&gt; is the &quot;official&quot; instance that is also a scalability experiment, it has an index focused on small websites, technology, the hacking community (CCC, Freifunk, hackspaces, FabLabs, …), independent wikis, and some other topics in between.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;unobtanium&#x2F;unobtanium-rocks-index&quot;&gt;index configuration is public and open to contributions&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;i-want-to-selfhost&quot;&gt;I want to selfhost!&lt;&#x2F;h2&gt;
&lt;p&gt;Great! Though there are a few things to consider …&lt;&#x2F;p&gt;
&lt;p&gt;While unobtanium tries to be as easy to use as possible, there are some challenges that are inherent to how the modern web works that one should be aware of. For just a few sites that don&#x27;t change very often (i.e. a blog) these are almost nonexistent, but they grow with your index and how dynamic the websites are.&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;doc.unobtanium.rocks&amp;#x2F;manual&amp;#x2F;selfhosting&quot; &gt;
		Selfhosting guide
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-can-i-help&quot;&gt;How can I help?&lt;&#x2F;h2&gt;
&lt;p&gt;You want to help? Great!&lt;&#x2F;p&gt;
&lt;p&gt;You want to write rust code?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;unobtanium&#x2F;&quot;&gt;Help unobtanium on Codeberg&lt;&#x2F;a&gt; it is made of one big repository and multiple smaller parts that help with various aspects of the search engine, any improvement there can almost directly improve unobtanium as a whole.&lt;&#x2F;li&gt;
&lt;li&gt;Help with the dependencies! Unobtanium depends on a lot of external crates to do its job. &lt;a href=&quot;&#x2F;about&#x2F;me#contact&quot;&gt;Let me know&lt;&#x2F;a&gt; if you contributed to one because of this 😃.&lt;&#x2F;li&gt;
&lt;li&gt;Help with code documentation: Software maintainers are Humans too, they sometimes forget or don&#x27;t have enough time for good documentation, any improvement there is welcome, even if you &quot;just&quot; edited some comments!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Code reading and markdown?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;unobtanium&#x2F;unobtanium-documentation&quot;&gt;Help with doc.unobtanium.rocks&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;how-it-started&quot;&gt;How it Started&lt;&#x2F;h2&gt;
&lt;p&gt;Unobtanium started in May of 2023 as a simple web crawler out of curiosity and boredom.&lt;&#x2F;p&gt;
&lt;p&gt;The original plan was to create a dead link checker, once that worked the scope was expanded to a simple search application, first with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.meilisearch.com&#x2F;&quot;&gt;Meilisearch&lt;&#x2F;a&gt; as a backend (around September of 2023), later with SQLite fts5.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Shell Null Termination &#x2F; Separation</title>
        <published>2023-05-27T00:00:00+00:00</published>
        <updated>2025-06-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/shell-null-termination/"/>
        <id>https://slatecave.net/notebook/shell-null-termination/</id>
        
        <summary type="text">What is null termination, why and how to use it in shell pipelines.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/shell-null-termination/">&lt;p&gt;Null or zero termination or separation is when one uses a null byte to separate records, which is different from the newline usually used in shell pipelines.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why&quot;&gt;Why&lt;&#x2F;h2&gt;
&lt;p&gt;Usually operating things by a newline is good enough and easy to debug, but even a little shell pipeline like a &lt;code&gt;ls | sort&lt;&#x2F;code&gt; can blow up when it encounters unexpected data like a filename that contains a newline character.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Fun fact:&lt;&#x2F;b&gt; Most filesystems actually allow you to use all kinds of characters from emoji to non-printables and control characters. Including newlines. One could even put ASCII-art in a filename (I&#x27;ve already done that)!&lt;&#x2F;p&gt;
&lt;p&gt;Using a null character saves you there as it is the only character guaranteed to never occur inside a filename and this becomes more important with other sources of (untrusted) input.&lt;&#x2F;p&gt;
&lt;p&gt;The improved version would be &lt;code&gt;ls --zero | sort -z&lt;&#x2F;code&gt; and a &lt;code&gt;| tr &#x27;\0&#x27; &#x27;\n&#x27;&lt;&#x2F;code&gt; to convert to newlines for the human that still wants the illusion of line separation.&lt;&#x2F;p&gt;
&lt;p&gt;Examples of inputs you shouldn&#x27;t trust:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The human in front of the machine (I know myself)&lt;&#x2F;li&gt;
&lt;li&gt;Your filesystem (because USB sticks, downloads, the human, etc.)&lt;&#x2F;li&gt;
&lt;li&gt;Anything that comes in over the network (Not even your own service, not the service you are paying for, …)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;translating-between-worlds&quot;&gt;Translating between Worlds&lt;&#x2F;h2&gt;
&lt;p&gt;To translate between newline separated and the null separated world there is a little utility called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;tr.1&quot;&gt;&lt;code&gt;tr&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


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

	
		&lt;dt&gt;
		null to newline
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;tr &#x27;\0&#x27; &#x27;\n&#x27;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		newline to null
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;tr &#x27;\n&#x27; &#x27;\0&#x27;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		swap newline and null
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;tr &#x27;\n\0&#x27; &#x27;\0\n&#x27;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		remove potential nulls
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;tr -d &#x27;\0&#x27;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;Make sure to actually remove the characters you assume to not be in your input!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;getting-a-program-into-null-separation-mode&quot;&gt;Getting a Program into Null-Separation Mode&lt;&#x2F;h2&gt;
&lt;p&gt;Unfortunately there is no &quot;the one way&quot; to make a program use nulls instead of newlines so here is a a hopefully useful table. Please note that some programs only have the option available in the gnu version, but not in i.e. the busybox version.&lt;&#x2F;p&gt;
&lt;p&gt;If a program has some kind of &lt;code&gt;printf&lt;&#x2F;code&gt; option one can use that to make the output null separated.&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;&#x2F;th&gt;&lt;th&gt;Version&lt;&#x2F;th&gt;&lt;th&gt;Input Flag(s)&lt;&#x2F;th&gt;&lt;th&gt;Output Flag(s)&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;awk&lt;&#x2F;td&gt;&lt;td&gt;gnu, other modern&lt;&#x2F;td&gt;&lt;td&gt;-v &#x27;RS=\0&#x27;&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;cut&lt;&#x2F;td&gt;&lt;td&gt;gnu&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;find&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;-print0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;fzf&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;--read0&lt;&#x2F;td&gt;&lt;td&gt;--print0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;grep -r&lt;&#x2F;td&gt;&lt;td&gt;gnu&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;-Z&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;grep&lt;&#x2F;td&gt;&lt;td&gt;gnu&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;inotifywait&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;--format &#x27;…%0&#x27; --no-newline&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;ls&lt;&#x2F;td&gt;&lt;td&gt;gnu&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;--zero&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;rg&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;-0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;sed&lt;&#x2F;td&gt;&lt;td&gt;gnu&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;sort&lt;&#x2F;td&gt;&lt;td&gt;gnu, busybox&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;tar -T&lt;&#x2F;td&gt;&lt;td&gt;gnu&lt;&#x2F;td&gt;&lt;td&gt;--null&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;uniq&lt;&#x2F;td&gt;&lt;td&gt;gnu, busybox&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;xargs&lt;&#x2F;td&gt;&lt;td&gt;gnu, busybox&lt;&#x2F;td&gt;&lt;td&gt;-0&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;read&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;IFS=&quot;&quot; read&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; The best way to automatically find out if a program supports an option is to &lt;code&gt;grep -q&lt;&#x2F;code&gt; across the output of its &lt;code&gt;--help&lt;&#x2F;code&gt;. Just make sure to choose a specific enough regex to avoid false positives. Trying out is also an option but that usually is a bit more complex.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; &lt;code&gt;read&lt;&#x2F;code&gt; is a shell builtin, which is why you don&#x27;t have to export the &lt;code&gt;IFS&lt;&#x2F;code&gt; (information separator) variable. This also affects other shell builtins like for-loops.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shell-variables-and-null-bytes&quot;&gt;Shell Variables and Null Bytes&lt;&#x2F;h2&gt;
&lt;p&gt;Shells usually drop null-characters when storing them into variables, so don&#x27;t rely on that happening and pipe information directly or, &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;portable-shell&#x2F;&quot;&gt;if your constraints allow&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;shell&#x2F;#bash-arrays&quot;&gt;use bash arrays&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Because of this limitation for-loops can&#x27;t make use of null separation, use &lt;code&gt;while read&lt;&#x2F;code&gt; (will probably result in a subshell though) and &lt;code&gt;xargs&lt;&#x2F;code&gt; instead.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note on &lt;code&gt;IFS&lt;&#x2F;code&gt;:&lt;&#x2F;b&gt; Setting the &lt;code&gt;IFS&lt;&#x2F;code&gt; to a newline by &lt;code&gt;IFS=&quot;&amp;lt;newline&amp;gt;&quot;&lt;&#x2F;code&gt; where &lt;code&gt;&amp;lt;newline&amp;gt;&lt;&#x2F;code&gt; is a literal line break (as in: the closing quote being the first character of the next line) you at least get clean newline separation if a for loop is really needed.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Speech Recognition Frontends</title>
        <published>2023-05-09T00: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/notebook/speech-recognition-frontends/"/>
        <id>https://slatecave.net/notebook/speech-recognition-frontends/</id>
        
        <summary type="text">Projects making use of speech recognition</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/speech-recognition-frontends/">&lt;p&gt;This page intends to give an overview what kind of projects out there are open source (or preferably free software) making use of speech recognition without relying on some well known cloud providers.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;The projects listed here fall into 3 broader categories.&lt;&#x2F;p&gt;
&lt;p&gt;Build your own voice something:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;speech-recognition-frontends&#x2F;#voice2json&quot;&gt;voice2json&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;speech-recognition-frontends&#x2F;#dragonfly&quot;&gt;Dragonfly&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Voice Input Methods that could replace Mouse and Keyboard:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;speech-recognition-frontends&#x2F;#easyspeak&quot;&gt;EasySpeak&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;speech-recognition-frontends&#x2F;#numen&quot;&gt;Numen&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;speech-recognition-frontends&#x2F;#simon-kde&quot;&gt;Simon&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Voice assistants:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;speech-recognition-frontends&#x2F;#rhasspy-voice-assistant&quot;&gt;Rhasspy&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;speech-recognition-frontends&#x2F;#mycroft-voice-assistant&quot;&gt;Mycroft&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;voice2json&quot;&gt;voice2json&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;voice2json&lt;&#x2F;code&gt; is a collection of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;voice2json.org&#x2F;commands.html&quot;&gt;command-line tools&lt;&#x2F;a&gt; for offline speech&#x2F;intent recognition on Linux. It is free, open source (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;opensource.org&#x2F;licenses&#x2F;MIT&quot;&gt;MIT&lt;&#x2F;a&gt;), and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;voice2json.org&#x2F;#supported-languages&quot;&gt;supports 18 human languages&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;voice2json.org&amp;#x2F;&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;voice2json.org&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;It can make use of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kaldi-asr.org&#x2F;&quot;&gt;CMU Sphinx&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kaldi-asr.org&#x2F;&quot;&gt;Kaldi&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mozilla&#x2F;DeepSpeech&quot;&gt;Mozilla DeepSpeech&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;julius-speech&#x2F;julius&quot;&gt;Julius&lt;&#x2F;a&gt;, depending on the used language profile.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dragonfly&quot;&gt;Dragonfly&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Dragonfly is a speech recognition framework for Python that makes it convenient to create custom commands to use with speech recognition software.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;dictation-toolbox&amp;#x2F;dragonfly&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;dictation-toolbox&amp;#x2F;dragonfly
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;I learned about this from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;handsfree.dev&quot;&gt;handsfree.dev&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;easyspeak&quot;&gt;EasySpeak&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Voice control for Linux desktops. Fully local, no cloud, Wayland-native.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;ctsdownloads&amp;#x2F;easyspeak&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;ctsdownloads&amp;#x2F;easyspeak
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Tries to be a blend of input method and voice assistant.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;numen&quot;&gt;Numen&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Numen is voice control for computing without a keyboard. It works system-wide on Linux and the speech recognition runs locally.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;numenvoice.org&amp;#x2F;&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;numenvoice.org&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;johngebbie.com&#x2F;&quot;&gt;John Gebbie&lt;&#x2F;a&gt; also has some &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~geb&#x2F;numen#see-also&quot;&gt;related projects linked in the README&lt;&#x2F;a&gt;, he also made &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;handsfree.dev&#x2F;&quot;&gt;handsfree.dev&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;simon-kde&quot;&gt;Simon (KDE)&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Simon is an open source speech recognition program that can replace your mouse and keyboard. The system is designed to be as flexible as possible and will work with any language or dialect.&lt;&#x2F;p&gt;
&lt;p&gt;Simon uses the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kde.org&quot;&gt;KDE libraries&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cmusphinx.github.io&#x2F;&quot;&gt;CMU SPHINX&lt;&#x2F;a&gt; and &#x2F; or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;julius.osdn.jp&#x2F;en_index.php&quot;&gt;Julius&lt;&#x2F;a&gt; coupled with the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;htk.eng.cam.ac.uk&#x2F;&quot;&gt;HTK&lt;&#x2F;a&gt; and runs on Windows and Linux.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;simon.kde.org&amp;#x2F;&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;simon.kde.org&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt;Some links have been replaced with their redirect targets.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rhasspy-voice-assistant&quot;&gt;Rhasspy Voice Assistant&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Rhasspy (ɹˈæspi) is an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rhasspy&quot;&gt;open source&lt;&#x2F;a&gt;, fully offline set of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rhasspy.readthedocs.io&#x2F;en&#x2F;latest&#x2F;#services&quot;&gt;voice assistant services&lt;&#x2F;a&gt; for many human languages […]&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;rhasspy.readthedocs.io&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;rhasspy.readthedocs.io
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mycroft-voice-assistant&quot;&gt;Mycroft Voice Assistant&lt;&#x2F;h2&gt;
&lt;p&gt;Mycroft is an open source voice Assistant that makes use of a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;MycroftAI&#x2F;mycroft-precise&quot;&gt;custom wakeword listener&lt;&#x2F;a&gt; and Mozilla &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mozilla&#x2F;DeepSpeech&quot;&gt;DeepSpeech&lt;&#x2F;a&gt; together with some custom components that has no easy-to-find non-marketing short description.&lt;&#x2F;p&gt;
&lt;p&gt;If you are looking for the technical stuff instead of company selling things stuff, you&#x27;ll find it in the Documentation menu of their page.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;mycroft.ai&amp;#x2F;&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;mycroft.ai&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;feedback&quot;&gt;Feedback&lt;&#x2F;h2&gt;
&lt;p&gt;If some of the information here is outdated, incorrect or you know a project that is not in this list please &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;site-source.slatecave-net&#x2F;issues&quot;&gt;open an issue over on Codeberg&lt;&#x2F;a&gt; or &lt;a href=&quot;&#x2F;about&#x2F;me#contact&quot;&gt;contact me in some other way&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Scanning, OCR and PDF</title>
        <published>2023-04-24T00:00:00+00:00</published>
        <updated>2024-05-19T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/scanning-ocr-and-pdf/"/>
        <id>https://slatecave.net/notebook/scanning-ocr-and-pdf/</id>
        
        <summary type="text">A cheatsheet for scanning and and OCRing documents on Linux with sane and tesseract</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/scanning-ocr-and-pdf/">&lt;h2 id=&quot;scanning&quot;&gt;Scanning&lt;&#x2F;h2&gt;
&lt;p&gt;Under Linux one can scan an image with the &lt;code&gt;scanimage&lt;&#x2F;code&gt; command provided by the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;sane-project.org&#x2F;&quot;&gt;SANE framework&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;A helper function that scans an image from the default scanner to the given path with settings suitable for OCR&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 z-function&quot;&gt;scan_image_to&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-support z-function z-builtin&quot;&gt;	printf&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;Scanning Image …\n&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-name&quot;&gt;	scanimage&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-mode&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; Gray&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-format&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; png&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-resolution&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 300&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;o&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-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;p&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;&lt;b&gt;Note:&lt;&#x2F;b&gt; The &lt;code&gt;--mode&lt;&#x2F;code&gt; and &lt;code&gt;--resolution&lt;&#x2F;code&gt; options are scanner specific and my work different for you. See &lt;code&gt;scanimage --help&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;

	
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;unpaper&quot;&gt;Unpaper&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;unpaper&#x2F;unpaper&quot;&gt;Unpaper&lt;&#x2F;a&gt; (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;unpaper&quot;&gt;manpage&lt;&#x2F;a&gt;) is a scan post-processing tool which can remove paper and scan artefacts and also do alignment of carelessly scanned pages. Which is great when preparing them for OCR.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks to a friend of mine for mentioning it!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ocr-using-tesseract-and-imagemagick&quot;&gt;OCR using Tesseract and imagemagick&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tesseract-ocr&#x2F;tesseract&quot;&gt;Tesseract&lt;&#x2F;a&gt; is both, a library and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;tesseract&quot;&gt;command line tool&lt;&#x2F;a&gt; for Optical Character Recognition (OCR). Image in, text in image out.&lt;&#x2F;p&gt;
&lt;p&gt;It best works with greyscale images with low background noise and high contrast. Getting a scan closer to that can be done using unpaper or - for scans of known quality - imagemagick using its builtin contrast filter and the level clipping, which is faster than any fully automatic tool.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;A function that can be used to OCR a given prerotated image that does some preprocessing with imagemagick to improve noise (the -level option) and contrast for scans, the output is the OCR as plaintext to stdout.&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; Usage: ocr_image &amp;lt;image&amp;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;ocr_image&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-entity z-name&quot;&gt;	convert&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-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;colorspace&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; Gray&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;contrast&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;level&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; 20%,100%&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;contrast&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;contrast&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&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-constant&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;	tesseract&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;l&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; deu+eng&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-oem&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-psm&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&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;&#x2F;figure&gt;
&lt;p&gt;To explain the most important tesseract options:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


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

	
		&lt;dt&gt;
		-l
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		To set the used &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;tesseract#LANGUAGES_AND_SCRIPTS&quot;&gt;language or script&lt;&#x2F;a&gt;. Accepts multiple values separated by a &lt;code&gt;+&lt;&#x2F;code&gt;. i.e. &lt;code&gt;-l deu+eng&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		--oem
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		To set the mode of operation, meaning whether or not to use the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Long_short-term_memory&quot;&gt;LSTM&lt;&#x2F;a&gt; based approach or the original tesseract OCR. At the time of writing I had the best results with option 2, enabling both.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		--psm
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Tells tesseract what &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;tesseract#OPTIONS&quot;&gt;kind of layout analysis&lt;&#x2F;a&gt; to run. I prefer to do a separate OSD (Orientation and Script Detection) only run in mode 0 rotating the image and then using the full automatic layout mode 3 without OSD to do the actual OCR.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		--user-words
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		To give tesseract a text file with a list of words that are likely to occur in the document, this helps avoiding the OCR equivalent to a typo. There is also a pattern file (note that these are &lt;b&gt;not&lt;&#x2F;b&gt; regexes!). &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;tesseract#CONFIG_FILES_AND_AUGMENTING_WITH_USER_DATA&quot;&gt;More on word and pattern lists in the tesseract manpage.&lt;&#x2F;a&gt;
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;&lt;b&gt;Note for Void-Linux users:&lt;&#x2F;b&gt; The package name and command for calling tesseract is &lt;code&gt;tesseract-ocr&lt;&#x2F;code&gt; on Void.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;This function uses tesseract with the psm 0 mode to correctly orientate a given image file that is assumed to be German or English text. The file is then replaced.&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; Usage: rotate_scanned_image &amp;lt;image&amp;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;rotate_scanned_image&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;    #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Replace or leve out the -l option here if you work with other languages&#x2F;scripts&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;	PAGE_OSD&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;tesseract&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;l&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; deu+eng&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-oem&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-psm&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; &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;span class=&quot;z-keyword z-operator&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&amp;amp;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;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;if DEBUG_OSD is set print some debug output.&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-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;DEBUG_OSD&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-support z-function z-builtin&quot;&gt; printf&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;PAGE OSD =====\n%s\n==============\n&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-other&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;PAGE_OSD&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; &amp;gt;&amp;amp;2&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;	APPLY_ROTATION&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-support z-function z-builtin&quot;&gt; printf&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\n&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-other&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;PAGE_OSD&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; 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;^Rotate:&#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-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;span class=&quot;z-entity z-name&quot;&gt;	convert&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-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;rotate&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;span class=&quot;z-variable z-other&quot;&gt;APPLY_ROTATION&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;0&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-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;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;&lt;b&gt;Note on DPI settings:&lt;&#x2F;b&gt; Tesseract assumes the images to be scanned at 300dpi, if you are using a different resolution make sure to tell tesseract using the &lt;code&gt;--dpi&lt;&#x2F;code&gt; option.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pdf-creation-and-manipulation&quot;&gt;PDF creation and manipulation&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;creating-a-pdf-from-images&quot;&gt;Creating a PDF from images&lt;&#x2F;h3&gt;
&lt;p&gt;To create a PDF from images without OCR one can use imagemagick.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example that uses the &lt;code&gt;convert&lt;&#x2F;code&gt; command to convert two png files to an pdf with a pagesize of DIN A4.&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;convert&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;page&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a4&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; page_1.png&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; page_2.png&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; document.pdf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;Also see the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;imagemagick.org&#x2F;script&#x2F;command-line-options.php#page&quot;&gt;imagemagick documentation on the -page option.&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ocr-using-ocrmypdf&quot;&gt;OCR using OCRmyPDF&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ocrmypdf.readthedocs.io&#x2F;en&#x2F;latest&#x2F;index.html&quot;&gt;OCRmyPDF&lt;&#x2F;a&gt; is a python wrapper for tesseract that makes it pretty painless to run OCR on a PDF and turn PDFs into &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ocrmypdf.readthedocs.io&#x2F;en&#x2F;latest&#x2F;introduction.html#about-pdf-a&quot;&gt;standards compliant PDF&#x2F;A&lt;&#x2F;a&gt; and optimize PDFs to reduce filesize.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;An example for OCRing a PDF file that contains German and English text as images.&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;ocrmypdf&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;l&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; deu+eng&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; input.pdf&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; output.pdf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;&lt;b&gt;Package Note:&lt;&#x2F;b&gt; The package is called &lt;code&gt;ocrmypdf&lt;&#x2F;code&gt; for all major Linux distributions (AUR on Arch).&lt;&#x2F;p&gt;
&lt;p&gt;Thank you (again) to the friend who found this tool.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;creating-pdfs-with-ocr-using-tesseract-and-poppler&quot;&gt;Creating PDFs with OCR using tesseract and poppler&lt;&#x2F;h3&gt;
&lt;p&gt;To create PDFs with an OCR overlay one can delegate the image to PDF step to tesseract using its &lt;code&gt;pdf&lt;&#x2F;code&gt; preset and join the resulting documents after the OCR step with the poppler &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;pdfunite&quot;&gt;&lt;code&gt;pdfunite&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; tool.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Tesseract command to generate a PDF with OCR overlay for a single page with the options left out (they are the same as with the plaintext preset)&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;tesseract page_n.png page_n [options...] pdf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;figure&gt;
	&lt;figcaption&gt;pdfunite command to join multiple PDFs together. Btw. these can be any PDF documents.&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;pdfunite&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; page_&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;.pdf&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; document.pdf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;&lt;b&gt;Accessibility Note:&lt;&#x2F;b&gt; These OCR overlays while pretty good are intended for making the documents searchable, they are not a replacement for proper semantic and accessible documents.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;getting-text-out-of-pdf-files&quot;&gt;Getting text out of PDF files&lt;&#x2F;h3&gt;
&lt;p&gt;To get text out of a PDF one can use the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;pdftotext&quot;&gt;&lt;code&gt;pdftotext&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;pdftohtml&quot;&gt;&lt;code&gt;pdftohtml&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; command (which come with poppler).&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;pdftotext&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; file.pdf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;packages&quot;&gt;Packages&lt;&#x2F;h2&gt;
&lt;p&gt;To get the commandline utilities on some distributions one should also install the &lt;code&gt;&amp;lt;packagename&amp;gt;-bin&lt;&#x2F;code&gt; package.&lt;&#x2F;p&gt;
&lt;p&gt;Imagemagick is sometimes just called &lt;code&gt;magick&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Tesseract usually comes without the needed data files, these probably come in packages called like &lt;code&gt;tesseract-data-&amp;lt;lang&amp;gt;&lt;&#x2F;code&gt;, if you just want everything there is probably a &lt;code&gt;tesseract-data-all&lt;&#x2F;code&gt; metapackage.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;pdfunite&lt;&#x2F;code&gt; and &lt;code&gt;pdfto*&lt;&#x2F;code&gt; commands are either directly in the &lt;code&gt;poppler&lt;&#x2F;code&gt; package or in &lt;code&gt;poppler-utils&lt;&#x2F;code&gt;.&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>IP-Address Ranges</title>
        <published>2023-03-08T00:00:00+00:00</published>
        <updated>2025-03-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/ip-ranges/"/>
        <id>https://slatecave.net/notebook/ip-ranges/</id>
        
        <summary type="text">Overview of reserved IP-Address ranges for IPv4 and IPv6</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/ip-ranges/">&lt;h2 id=&quot;in-general&quot;&gt;In General&lt;&#x2F;h2&gt;
&lt;p&gt;While IPv4 and IPv6 have different terminology, they share some concepts. Scope and Delivery schemes.&lt;&#x2F;p&gt;
&lt;p&gt;Both IP-Versions share the following delivery schemes:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


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

	
		&lt;dt&gt;
		Unicast
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		one to one, the default
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Multicast
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		one to many, v6 had a huge Update there
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Broadcast
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		usually inside the scope of a single network
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;Scope is a bit version dependant, usually you have:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


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

	
		&lt;dt&gt;
		Global
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		These addresses are globally routable and assigned by the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.iana.org&#x2F;&quot;&gt;IANA&lt;&#x2F;a&gt; and the local Internet registries.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Private
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		These addresses are reserved for private use and you are free to use the in your own network, just be aware, that you can&#x27;t use them on the internet.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Loopback
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		These addresses always route back to the sending machine, usually &lt;code&gt;localhost&lt;&#x2F;code&gt; resolves to them.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Link-Local
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		These adresses are automatically assigned so that host on the same network can talk to each other.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Documentation
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		These addresses are reserved for documentation purposes.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Other reserved
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		These adresses are reserved and have some conditions on when you are supposed to use them.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;ipv6-adress-ranges&quot;&gt;IPv6 Adress Ranges&lt;&#x2F;h2&gt;
&lt;p&gt;By default IPv6 adresses are unassigned global unicast adresses and therefore reserved for future use. The IANA has a complete list with the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.iana.org&#x2F;assignments&#x2F;iana-ipv6-special-registry&#x2F;iana-ipv6-special-registry.xhtml&quot;&gt;IANA IPv6 Special-Purpose Address Registry&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


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

	
		&lt;dt&gt;
		Global Unicast (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc4291.html#section-2.5.4&quot;&gt;RFC 4291, Section 2.5.4&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;2000::&#x2F;3&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Unique Local (Private) (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc4193&quot;&gt;RFC 4193&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;fd00::&#x2F;8&lt;&#x2F;code&gt; (with Random Global ID)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;fc00::&#x2F;8&lt;&#x2F;code&gt; (reserved for future use)
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Loopback (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc4291.html#section-2.5.3&quot;&gt;RFC 4291, Section 2.5.3&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;::1&#x2F;128&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Link-Local (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc4291.html#section-2.5.6&quot;&gt;RFC 4291, Section 2.5.6&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;fe80::&#x2F;10&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Documentation (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc3849.html&quot;&gt;RFC 3849&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;2001:db8::&#x2F;32&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Multicast (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc4291.html#section-2.7&quot;&gt;RFC 4291, Section 2.7&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;ff00::&#x2F;8&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		IETF Protocol Assignments (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc6890.html#section-2.1&quot;&gt;RFC 6890&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;2001:0000::&#x2F;23&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		NAT64 Well-Known Prefix (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc6052#section-2&quot;&gt;RFC 6052, Section 2&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;64:ff9b::&#x2F;96&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Site-Local IPv6 Unicast Addresses (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc4291.html#section-2.5.7&quot;&gt;RFC 4291, Section 2.5.7&lt;&#x2F;a&gt;) have been deprecated in favour of Unique Local Addresses (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc4193&quot;&gt;RFC 4193&lt;&#x2F;a&gt;) which are pretty similar, but better defined.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ipv4-address-ranges&quot;&gt;IPv4 Address Ranges&lt;&#x2F;h2&gt;
&lt;p&gt;By default IPv4 adresses are adresses that fall in the global unicast category. The IANA has a complete List with the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.iana.org&#x2F;assignments&#x2F;iana-ipv4-special-registry&#x2F;iana-ipv4-special-registry.xhtml#iana-ipv4-special-registry-1&quot;&gt;IANA IPv4 Special-Purpose Address Registry&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


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

	
		&lt;dt&gt;
		Private (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc1918.html&quot;&gt;RFC 1918&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;192.168.0.0&#x2F;16&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;172.16.0.0&#x2F;12&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;10.0.0.0&#x2F;8&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Loopback (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc1122.html#page-31&quot;&gt;RFC 1122, Section 3.2.1.3&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;127.0.0.0&#x2F;8&lt;&#x2F;code&gt; Note that this is an entire network.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;127.0.0.1&lt;&#x2F;code&gt; is usually used.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Link-Local (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc3927.html&quot;&gt;RFC 2927&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;169.254.0.0&#x2F;16&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Documentation (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc5737.html#section-3&quot;&gt;RFC 5737&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;192.0.2.0&#x2F;24&lt;&#x2F;code&gt; (TEST-NET-1)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;198.51.100.0&#x2F;24&lt;&#x2F;code&gt; (TEST-NET-2)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;203.0.113.0&#x2F;24&lt;&#x2F;code&gt; (TEST-NET-3)
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Shared (&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;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;100.64.0.0&#x2F;10&lt;&#x2F;code&gt; &lt;br&gt;&lt;q&gt;used as Shared Address Space to accommodate the needs of Carrier-Grade NAT (CGN) devices.&lt;&#x2F;q&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Benchmarking (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc2544.html#appendix-C.2.2.1&quot;&gt;RFC 2544&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;198.18.0.0&#x2F;15&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Multicast
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;240.0.0.0&#x2F;4&lt;&#x2F;code&gt; Multicast &quot;Namespace&quot; (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc1112.html#section-4&quot;&gt;RFC 1112&lt;&#x2F;a&gt;)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;192.52.193.0&#x2F;24&lt;&#x2F;code&gt; Automatic Multicast Tunneling (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc7450.html&quot;&gt;RFC 7450&lt;&#x2F;a&gt;)
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		IETF Protocol Assignments (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc6890.html#section-2.1&quot;&gt;RFC 6890&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;192.0.0.0&#x2F;24&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h3 id=&quot;broadcast&quot;&gt;Broadcast&lt;&#x2F;h3&gt;
&lt;p&gt;Broadcast addresses with IPv4 work by setting the host-part of the address to all ones (in the binary representation) they send to all hosts in the local network.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; In &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc919.html&quot;&gt;RFC 919&lt;&#x2F;a&gt; &lt;code&gt;255.255.255.255&lt;&#x2F;code&gt; is defined as &lt;q&gt;broadcast to all of its immediate neighbors&lt;&#x2F;q&gt;.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>echoip.slatecave.net</title>
        <published>2023-02-26T00:00:00+00:00</published>
        <updated>2023-02-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/echoip-slatecave/"/>
        <id>https://slatecave.net/creations/echoip-slatecave/</id>
        
        <summary type="text">A web-service for looking up IPs and domain names</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/echoip-slatecave/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;echoip.slatecave.net&quot; &gt;
		Visit echoip.slatecave.net
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;This service was born out of a combination of wanting to learn rust, not being very happy with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mpolden&#x2F;echoip&quot;&gt;the echoip service by mpolden&lt;&#x2F;a&gt; which had some features missing that I wanted in my echoip service.&lt;&#x2F;p&gt;
&lt;p&gt;So what echoip-slatecave do?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Look up IP-Addresses in a mmdb database and return the resulting geoinformation.&lt;&#x2F;li&gt;
&lt;li&gt;Reverse DNS lookup&lt;&#x2F;li&gt;
&lt;li&gt;DNS forward lookup for the most common record types, all on one page&lt;&#x2F;li&gt;
&lt;li&gt;Has a text view that is made for commandline use&lt;&#x2F;li&gt;
&lt;li&gt;Links to other services that may be useful in the HTML version&lt;&#x2F;li&gt;
&lt;li&gt;Has a builtin rate-limiter that makes easier to protect a small self hosted instance&lt;&#x2F;li&gt;
&lt;li&gt;Interlinked IP-Address and domain lookup to make navigating easier&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;With that feature set it should be a useful tool where one can enter any kind of address and navigate straight to the needed information or start exploring (which works pretty well, when the IP and domain lookup are interlinked).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why&quot;&gt;Why?&lt;&#x2F;h2&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.png&quot; alt=&quot;Neofox asks:&quot; class=&quot;chat-avatar&quot; width=&quot;64&quot; height=&quot;64&quot;&gt;
	&lt;blockquote&gt;
&lt;p&gt;But slatian, why? And why include a DNS lookup? I can do this on the commandline!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;&#x2F;div&gt;



&lt;p&gt;Besides the fact that exploring is easier with a web interface that does multiple lookup with which is one of the reasons I built it …&lt;&#x2F;p&gt;
&lt;p&gt;Yes YOU can use the commandline and make sense of it, I can too. But not everyone has the skill or system to use a capable commandline, also I don&#x27;t want to link to some random non-free wannabe monopoly service because if someone didn&#x27;t have a tool they&#x27;ll continue using that.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Serial Port Troubleshooting on Linux</title>
        <published>2023-02-14T00:00:00+00:00</published>
        <updated>2023-02-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/serial-not-working/"/>
        <id>https://slatecave.net/notebook/serial-not-working/</id>
        
        <summary type="text">How to find out why your serial Port isn&#x27;t working</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/serial-not-working/">&lt;p&gt;A serial port (i.e. for programming a microcontroller) can have multiple reasons why it is not working. This guide is based on my experience and will try to cover most cases, FAQ style.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hints&quot;&gt;Hints&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;em&gt;It is always a good idea to try to look up the problem in your distributions wiki&#x2F;documentation first!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can manually connect to a serial port using &lt;code&gt;screen &#x2F;dev&#x2F;tty&amp;lt;name&amp;gt;&lt;&#x2F;code&gt;.
Quit with &lt;kbd&gt;Ctrl+a&lt;&#x2F;kbd&gt; and pressing &lt;kbd&gt;k&lt;&#x2F;kbd&gt; after that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;is-your-serial-device-showing-up&quot;&gt;Is your Serial device showing up?&lt;&#x2F;h2&gt;
&lt;p&gt;Use &lt;code&gt;ls &#x2F;dev&#x2F;tty*&lt;&#x2F;code&gt; to get a list of terminal devices on your Linux machine. If you find a &lt;code&gt;ttyUSBn&lt;&#x2F;code&gt; or a &lt;code&gt;ttyACMn&lt;&#x2F;code&gt; (where &lt;code&gt;n&lt;&#x2F;code&gt; is a number) that appears when you plug your device in and disappears when unplugging (yes that&#x27;s the easiest way) that is your device with hardware and drivers working, skip this section.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Builtin serial ports are sometimes also called &lt;code&gt;&#x2F;dev&#x2F;Sn&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;is-the-hardware-recognised&quot;&gt;Is the Hardware recognised?&lt;&#x2F;h3&gt;
&lt;p&gt;Try running &lt;code&gt;lsusb&lt;&#x2F;code&gt;, it will give you a list of connected USB devices, if your device is not among them you either have a bad cable, a bad microcontroller or (for some micros) the controller is not in programming mode.&lt;&#x2F;p&gt;
&lt;p&gt;If the device shows up in &lt;code&gt;lsusb&lt;&#x2F;code&gt; but there is no serial port, your hardware is working but there is a driver problem. (or the hardware doesn&#x27;t provide a serial port.)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;would-a-reboot-fix-the-problem&quot;&gt;Would a reboot fix the problem?&lt;&#x2F;h3&gt;
&lt;p&gt;This only the case if there has been a system update since the last reboot.&lt;&#x2F;p&gt;
&lt;p&gt;(Depending on your definition of a shortcut: just reboot)&lt;&#x2F;p&gt;
&lt;p&gt;If you are not using a very uncommon setup check if &lt;code&gt;&#x2F;lib&#x2F;modules&lt;&#x2F;code&gt; (the directory kernel modules (drivers) are in) contains a directory called the same as the output of &lt;code&gt;uname -r&lt;&#x2F;code&gt; (the version of the currently running kernel).&lt;&#x2F;p&gt;
&lt;p&gt;If that is not the case your package manager has cleaned up the old modules during an update and you have not rebooted yet (still running the old kernel). The solution in this case is a reboot, then the hardware management will be able to find the modules the kernel needs because the running version matches the version of the modules in the filesystem.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;missing-package-or-driver-error&quot;&gt;Missing Package or Driver Error&lt;&#x2F;h3&gt;
&lt;p&gt;If a reboot did not or would not fix your problem try searching for a driver package (usually the package name contains &lt;code&gt;ftdi&lt;&#x2F;code&gt; for USB to serial adaptors).&lt;&#x2F;p&gt;
&lt;p&gt;If you find the necessary package already installed, running &lt;code&gt;dmesg -w&lt;&#x2F;code&gt; as root and then plugging in the device is also worth a look at this point. &lt;code&gt;dmesg&lt;&#x2F;code&gt; prints logs from your kernel, including potential information and errors from relevant drivers.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;do-you-have-permission-for-accessing-serial-ports&quot;&gt;Do you have permission for accessing serial ports?&lt;&#x2F;h2&gt;
&lt;p&gt;Run &lt;code&gt;ls -l &#x2F;dev&#x2F;tty*&lt;&#x2F;code&gt;, this will tell you who owns the device files, file permissions apply like with regular files.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;An example output line, the file belongs to the &lt;code&gt;root&lt;&#x2F;code&gt; user and the &lt;code&gt;dialout&lt;&#x2F;code&gt; group, both are allowed to read and write.&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;crw-rw---- 1 root dialout 4, 67  5. Sep 12:25 &#x2F;dev&#x2F;ttyS0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#^  ^ The 2 rw here indicate that owner and group may read from and write to this device.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;By default on many distributions the hardware management comes preconfigured in a way that only permits users in the &lt;code&gt;dialout&lt;&#x2F;code&gt; group to use serial ports. Check if you are a member of that group using the &lt;code&gt;groups&lt;&#x2F;code&gt; command.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; The &lt;code&gt;dialout&lt;&#x2F;code&gt; group may have a different name on some distributions if the &lt;code&gt;ls -l&lt;&#x2F;code&gt; didn&#x27;t indicate that the serial port belongs to the &lt;code&gt;dialout&lt;&#x2F;code&gt; group this is the case for you. (look up serial ports in the distributions wiki).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note on Arch derived Distributions:&lt;&#x2F;b&gt; On Archlinux and derived distributions the relevant group is called &lt;code&gt;uucp&lt;&#x2F;code&gt; instead of &lt;code&gt;dialout&lt;&#x2F;code&gt;. (UUCP stands for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;UUCP&quot;&gt;Unix-to-Unix Copy&lt;&#x2F;a&gt; which was common to be used over a serial link.)&lt;&#x2F;p&gt;
&lt;p&gt;You can give a user permission by running &lt;code&gt;sudo usermod -a -G dialout &amp;lt;username&amp;gt;&lt;&#x2F;code&gt; and logging out and back in after that.&lt;&#x2F;p&gt;
&lt;p&gt;In case your system looks completely different you may want to investigate &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;udev&quot;&gt;&lt;code&gt;udev&lt;&#x2F;code&gt; and udev rules&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;is-something-else-using-the-port&quot;&gt;Is something else using the port?&lt;&#x2F;h2&gt;
&lt;p&gt;Since a program doesn&#x27;t really know what is on the other side of a serial port some programs simply connect and try to find out. This may interfere with whatever you are trying to do.&lt;&#x2F;p&gt;
&lt;p&gt;One of these programs is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;packages.debian.org&#x2F;bullseye&#x2F;brltty&quot;&gt;brltty&lt;&#x2F;a&gt; which is preconfigured by most big distributions. It tries very hard to make sure plugging a braille display into an USB port will make it immediately available. (which is very good, your little Arduino isn&#x27;t nearly as important!) Unfortunately it has to open every serial device that connects to the system to find out whether it is a braille display or not.&lt;&#x2F;p&gt;
&lt;p&gt;To fix that behaviour either configure the &quot;offending&quot; program or stop (and disable) the service it belongs to if you don&#x27;t need it.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example of how to disable and stop the &lt;code&gt;brltty&lt;&#x2F;code&gt; service on most systemd based distributions. &lt;br&gt;&lt;strong&gt;Do not do this if you rely on a working braille display!&lt;&#x2F;strong&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;sudo&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; systemctl&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; disable&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-now&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; brltty&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;did-that-fix-it&quot;&gt;Did that fix it?&lt;&#x2F;h2&gt;
&lt;p&gt;If this guide helped you please consider sharing it.&lt;&#x2F;p&gt;
&lt;p&gt;In case you found a mistake  or ended up in a dead end, don&#x27;t hesitate to &lt;a href=&quot;&#x2F;about&#x2F;me#contact&quot;&gt;contact me&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>arduino-cli</title>
        <published>2023-02-13T00:00:00+00:00</published>
        <updated>2023-02-13T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/arduino-cli/"/>
        <id>https://slatecave.net/notebook/arduino-cli/</id>
        
        <summary type="text">Some arduino-cli common patterns and tricks</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/arduino-cli/">&lt;p&gt;While &lt;code&gt;arduino-cli&lt;&#x2F;code&gt; is an awesome tool, unfortunately most guides skip right past it and assume you use the Arduino IDE, this page is a collection of common patterns for &lt;code&gt;arduino-cli&lt;&#x2F;code&gt;. It assumes that you know a few things about how Arduino works.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;basics&quot;&gt;Basics&lt;&#x2F;h2&gt;
&lt;p&gt;To create a sketch &lt;code&gt;arduino-cli&lt;&#x2F;code&gt; assumes that your main &lt;code&gt;.ino&lt;&#x2F;code&gt; file has the same name as the directory containing it.&lt;&#x2F;p&gt;
&lt;p&gt;To build and upload use &lt;code&gt;arduino-cli compile --fqbn &amp;lt;fqbn&amp;gt;&lt;&#x2F;code&gt; and &lt;code&gt;arduino-cli upload -p &amp;lt;serial-device&amp;gt; --fqbn &amp;lt;fqbn&amp;gt;&lt;&#x2F;code&gt; in your code directory.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Hint:&lt;&#x2F;b&gt; On Linux your serial devices are called &lt;code&gt;&#x2F;dev&#x2F;ttyUSBn&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;dev&#x2F;ttyACMn&lt;&#x2F;code&gt; where &lt;code&gt;n&lt;&#x2F;code&gt; is a number.&lt;br&gt; &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;serial-not-working&#x2F;&quot;&gt;Make sure you have the permissions to access them and nothing else is trying to use those ports.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;You&#x27;ve got an unhelpful error message?&lt;&#x2F;b&gt; Try adding the &lt;code&gt;--log&lt;&#x2F;code&gt; flag, it will cause &lt;code&gt;arduino-cli&lt;&#x2F;code&gt; to print out whatever it is currently doing.&lt;br&gt; This way one can get some context for the error.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fqbn-fully-qualified-board-name&quot;&gt;FQBN? Fully Qualified Board Name!&lt;&#x2F;h2&gt;
&lt;p&gt;The Fully qualified Board name consists of what Arduino calls the &lt;i&gt;Platform ID&lt;&#x2F;i&gt; and a name for the board. You can find it out either by using &lt;code&gt;arduino-cli board search &amp;lt;searchterm&amp;gt;&lt;&#x2F;code&gt;, &lt;code&gt;arduino-cli board listall&lt;&#x2F;code&gt; or by appending the official board name in lowercase to the Platform ID.&lt;&#x2F;p&gt;
&lt;p&gt;Examples include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;arduino:avr:uno&lt;&#x2F;code&gt; (Arduino Uno or another board with an ATMega328P running at 16MHz)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;esp8266:esp8266:generic&lt;&#x2F;code&gt; (A generic esp8266 Module)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;esp32:esp32:esp32&lt;&#x2F;code&gt; (A generic esp32 module)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Please note that the esp boards are not available by default.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;compile-and-upload-a-sketch-using-arduino-cli&quot;&gt;Compile and Upload a Sketch using arduino-cli&lt;&#x2F;h2&gt;
&lt;p&gt;I usually create a &lt;code&gt;compile-and-upload.sh&lt;&#x2F;code&gt; script to make development and testing easier.&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-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;span class=&quot;z-entity z-name&quot;&gt;arduino-cli&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; compile&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-fqbn&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; arduino:avr:uno&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-constant&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;arduino-cli&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; upload&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-fqbn&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; arduino:avr:uno&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-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;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;1&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;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;dev&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;ttyUSB0&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&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;figcaption&gt;A simple compile and upload script with a fixed board and an optional argument for the serial port that defaults to &lt;code&gt;&#x2F;dev&#x2F;ttyUSB0&lt;&#x2F;code&gt;&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; When given the &lt;code&gt;-u&lt;&#x2F;code&gt; flag and the options needed for uploading &lt;code&gt;arduino-cli compile --fqbn arduino:avr:uno -u -p &quot;${1:-&#x2F;dev&#x2F;ttyUSB0}&quot;&lt;&#x2F;code&gt; behaves like the above example script.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note on shell syntax:&lt;&#x2F;b&gt; The &lt;code&gt;&quot;${1:-&#x2F;dev&#x2F;ttyUSB0}&quot;&lt;&#x2F;code&gt; is read by your shell as replace this with the first argument given to the script or with &lt;code&gt;&#x2F;dev&#x2F;ttyUSB0&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;adding-more-boards&quot;&gt;Adding more Boards&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;code&gt;arduino-cli&lt;&#x2F;code&gt; is not just a wrapper for a compiler and whatever you prefer for uploading … it also is a package manager. If you want more boards (or libraries) you can search for and install them.&lt;&#x2F;p&gt;
&lt;p&gt;But if you want to write code for a non-Arduino branded board (i.e. an ESP8266 or ESP32) those packages are not in the Arduino package repositories.&lt;&#x2F;p&gt;
&lt;p&gt;Adding an extra repository can be done by letting &lt;code&gt;arduino-cli&lt;&#x2F;code&gt; do the work …&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;arduino-cli&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; config&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; board_manager.additional_urls&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;url:&#x2F;&#x2F;to&#x2F;index.json&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;… or by manually editing &lt;code&gt;~&#x2F;.arduino15&#x2F;arduino-cli.yaml&lt;&#x2F;code&gt; and adding the URL pinting to the respositories &lt;code&gt;*_index.json&lt;&#x2F;code&gt; file there.&lt;&#x2F;p&gt;
&lt;figure&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 class=&quot;z-entity z-name z-tag&quot;&gt;b&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;oard_manager&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;  a&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;dditional_urls&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-string&quot;&gt; h&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ttps:&#x2F;&#x2F;arduino.esp8266.com&#x2F;stable&#x2F;package_esp8266com_index.json&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-string&quot;&gt; h&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ttps:&#x2F;&#x2F;dl.espressif.com&#x2F;dl&#x2F;package_esp32_index.json&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-string&quot;&gt; h&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ttps:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;espressif&#x2F;arduino-esp32&#x2F;gh-pages&#x2F;package_esp32_dev_index.json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	&lt;figcaption&gt;An excerpt from the configuration file from my laptop with additional URL to the stable esp8266 and esp32 repositories in addition to the development version of the esp32 repository.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Don&#x27;t forget to &lt;code&gt;arduino-cli update&lt;&#x2F;code&gt; and &lt;code&gt;arduino-cli upgrade&lt;&#x2F;code&gt;!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;flashing-a-bootloader-using-arduino-cli&quot;&gt;Flashing a Bootloader using arduino-cli&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;arduino-cli&lt;&#x2F;code&gt; already knows how to flash a new bootloader with the &lt;code&gt;burn-bootloader&lt;&#x2F;code&gt; subcommand, you have to tell it for which board you want the bootloader using &lt;code&gt;--fqbn&lt;&#x2F;code&gt; and which programmer you are using with &lt;code&gt;-P&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Programmers usually aren&#x27;t quite cheap, but if you have another Arduino (compatible) with UART (serial) and SPI interface you can use that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.arduino.cc&#x2F;built-in-examples&#x2F;arduino-isp&#x2F;ArduinoISP&quot;&gt;Arduino as an &lt;abbr title=&quot;In-circuit Serial Programmer&quot;&gt;ISP&lt;&#x2F;abbr&gt; &lt;&#x2F;a&gt;. (use &lt;code&gt;-P arduinoasisp&lt;&#x2F;code&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;It may be necessary to provide the port the programmer is on via the &lt;code&gt;-p&lt;&#x2F;code&gt; option. (like with the &lt;code&gt;upload&lt;&#x2F;code&gt; command)&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example using the official AVRISP mkII programmer for AVR chips for an ATMEL 328P in an Arduino Uno like configuration&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;arduino-cli&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; burn-bootloader&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-fqbn&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; arduino:avr:uno&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; avrispmkii&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;To find out which programmer string you need, go to the &lt;code&gt;ArduinoCore-*&lt;&#x2F;code&gt; repository of your favourite architecture (See &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arduino&quot;&gt;github.com&#x2F;arduino&lt;&#x2F;a&gt; for the official ones) and open the &lt;code&gt;programmers.txt&lt;&#x2F;code&gt; file (i.e. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arduino&#x2F;ArduinoCore-avr&#x2F;blob&#x2F;master&#x2F;programmers.txt&quot;&gt;ArduinoCore-avr&#x2F;programmers.txt&lt;&#x2F;a&gt;), the keys used there are the needed ids for the &lt;code&gt;-P&lt;&#x2F;code&gt; option.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; An interesting option might be to use an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.arduino.cc&#x2F;built-in-examples&#x2F;arduino-isp&#x2F;ArduinoISP&#x2F;&quot;&gt;Arduino Microcontroller as your programmer&lt;&#x2F;a&gt;, use &lt;code&gt;-P arduinoisp&lt;&#x2F;code&gt; in this case. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arduino&#x2F;arduino-examples&#x2F;blob&#x2F;main&#x2F;examples&#x2F;11.ArduinoISP&#x2F;ArduinoISP&#x2F;ArduinoISP.ino&quot;&gt;The neccessary code is also in a public repository.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-do-i-need-a-bootloader&quot;&gt;Why do I need a Bootloader?&lt;&#x2F;h3&gt;
&lt;p&gt;To be able to seamlessly upload code with just a USB to serial converter like in the example above your microcontroller needs to have the Arduino bootloader installed, a small piece of software that runs when the controller starts (that&#x27;s the reason you need to hook up the reset line when programming) that can be told to write a new program to the controller instead of starting the already existing one.&lt;&#x2F;p&gt;
&lt;p&gt;The boards you can buy online come with a bootloader preinstalled. But maybe you want to build your own Arduino compatible or released the magic smoke and want to just buy the chip itself without a development-board, that is when you want to flash the bootloader yourself.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;do-i-need-a-bootloader-to-use-the-arduino-framwork&quot;&gt;Do I need a Bootloader to use the Arduino Framwork?&lt;&#x2F;h3&gt;
&lt;p&gt;No, you can use the programmer of your choice (just use the &lt;code&gt;-P&lt;&#x2F;code&gt; option with the upload command) to flash whatever you coded up directly into the controller at the cost of having to use the programmer every time you upload a new program. (Unless that program is a bootloader 😉)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;using-sketch-local-libraries&quot;&gt;Using sketch-local Libraries&lt;&#x2F;h2&gt;
&lt;p&gt;To use liberies stored anywhere but the default location (i.e. in a &lt;code&gt;libs&lt;&#x2F;code&gt; folder next to your &lt;code&gt;.ino&lt;&#x2F;code&gt; files) the &lt;code&gt;--library&lt;&#x2F;code&gt; and &lt;code&gt;--libraries&lt;&#x2F;code&gt; can be used. Both accept a comma seperated list of filepaths.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;--library&lt;&#x2F;code&gt; points to individual libraries and &lt;code&gt;--libraries&lt;&#x2F;code&gt; points to folders of libraries.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example to compile a sketch with a &lt;code&gt;libs&lt;&#x2F;code&gt; folder&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;arduino-cli&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; compile&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-libraries&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; libs&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-fqbn&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;fqb&lt;&#x2F;span&gt;&lt;span&gt;n&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;&#x2F;figure&gt;
&lt;h2 id=&quot;you-may-also-be-interested-in&quot;&gt;You may also be interested in&lt;&#x2F;h2&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;arduino.github.io&amp;#x2F;arduino-cli&amp;#x2F;latest&amp;#x2F;getting-started&amp;#x2F;&quot; &gt;
		The official arduino-cli documantation
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;serial-not-working&#x2F;&quot; &gt;
		Serial Port Troublehooting on Linux
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Resistor Value Table</title>
        <published>2023-01-14T00:00:00+00:00</published>
        <updated>2023-01-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/resistors/"/>
        <id>https://slatecave.net/notebook/resistors/</id>
        
        <summary type="text">For when those Rings don&#x27;t make sense again.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/resistors/">&lt;h2 id=&quot;how-to-read-resistors&quot;&gt;How To Read Resistors&lt;&#x2F;h2&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th&gt;Color&lt;&#x2F;th&gt;&lt;th&gt;Value&lt;&#x2F;th&gt;&lt;th&gt;Multiplier&lt;&#x2F;th&gt;&lt;th&gt;Tolerance&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row black&quot;&gt;&lt;td&gt;Black&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;1Ω&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row brown&quot;&gt;&lt;td&gt;Brown&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;10Ω&lt;&#x2F;td&gt;&lt;td&gt;± 1%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row red&quot;&gt;&lt;td&gt;Red&lt;&#x2F;td&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;td&gt;100Ω&lt;&#x2F;td&gt;&lt;td&gt;± 2%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row orange&quot;&gt;&lt;td&gt;Orange&lt;&#x2F;td&gt;&lt;td&gt;3&lt;&#x2F;td&gt;&lt;td&gt;1KΩ&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row yellow&quot;&gt;&lt;td&gt;Yellow&lt;&#x2F;td&gt;&lt;td&gt;4&lt;&#x2F;td&gt;&lt;td&gt;10KΩ&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row green&quot;&gt;&lt;td&gt;Green&lt;&#x2F;td&gt;&lt;td&gt;5&lt;&#x2F;td&gt;&lt;td&gt;100KΩ&lt;&#x2F;td&gt;&lt;td&gt;± 0.5%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row blue&quot;&gt;&lt;td&gt;Blue&lt;&#x2F;td&gt;&lt;td&gt;6&lt;&#x2F;td&gt;&lt;td&gt;1MΩ&lt;&#x2F;td&gt;&lt;td&gt;± 0.25%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row violet&quot;&gt;&lt;td&gt;Violet&lt;&#x2F;td&gt;&lt;td&gt;7&lt;&#x2F;td&gt;&lt;td&gt;10MΩ&lt;&#x2F;td&gt;&lt;td&gt;± 0.10%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row gray&quot;&gt;&lt;td&gt;Gray&lt;&#x2F;td&gt;&lt;td&gt;8&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;± 0.05%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row white&quot;&gt;&lt;td&gt;White&lt;&#x2F;td&gt;&lt;td&gt;9&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row gold&quot;&gt;&lt;td&gt;Gold&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;0.1Ω&lt;&#x2F;td&gt;&lt;td&gt;± 5%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row silver&quot;&gt;&lt;td&gt;Silver&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;0.01Ω&lt;&#x2F;td&gt;&lt;td&gt;± 10%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;Resistors are usually marked with 4 or 5 colored rings to indicate value and tolerance. To read the code in the right direction you have to know that the order is:&lt;&#x2F;p&gt;
&lt;p&gt;1st Ring, 2nd Ring, 3rd Ring (only when you have a 5-Ring code), Multiplier Ring, a larger gap (sometimes not), Tolerance Ring&lt;&#x2F;p&gt;
&lt;p&gt;To decode, read the 1st to 3rd Ring as if they were decimals and then multiply that number with the Multiplier.&lt;&#x2F;p&gt;
&lt;p&gt;For SMD resistors the pattern apparently repeats itself where the last digit on the package is a Multiplier for the other digits, resulting in a value in ohms. Also the form where there is an &lt;code&gt;R&lt;&#x2F;code&gt; in place of the decimal point is common for smaller values.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>FFTFM</title>
        <published>2022-12-11T00:00:00+00:00</published>
        <updated>2022-12-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/fftfm/"/>
        <id>https://slatecave.net/creations/fftfm/</id>
        
        <summary type="text">A fuzzyfinder script that can search for manpages and RFCs</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/fftfm/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;This script uses &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;junegunn&#x2F;fzf&quot;&gt;&lt;code&gt;fzf&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; hooks to make it possible to search with &quot;live&quot; result preview for manpages or RFCs, it also contains some experimental special syntax logic which is supposed to be used for filtering sections of manuals but that part doesn&#x27;t work very well. It also has issues sorting a particualr manpage to the top when an exact name in typed in (probably due to how &lt;code&gt;apropos&lt;&#x2F;code&gt; works)&lt;&#x2F;p&gt;
&lt;p&gt;Note that for the RFCs to work it assumes they are in the place and structure the archlinux &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;archlinux.org&#x2F;packages&#x2F;community&#x2F;any&#x2F;rfc&#x2F;&quot;&gt;&lt;code&gt;rfc&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; package places them.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Podcasts</title>
        <published>2022-11-06T00:00:00+00:00</published>
        <updated>2022-11-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/podcasts/"/>
        <id>https://slatecave.net/notebook/podcasts/</id>
        
        <summary type="text">A list of podcasts I listen to and can recommend</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/podcasts/">&lt;p&gt;Note that these are mostly about IT and (I think) well known podcasts.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;english-language-podcasts&quot;&gt;English Language Podcasts&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;foss-and-crafts&quot;&gt;FOSS and Crafts&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;fossandcrafts.org&amp;#x2F;&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;fossandcrafts.org&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;FOSS and Crafts is an interdisciplinary exploration of collaborative creation. […]  We explore such topics as computer programming, craft production, user freedom and agency, especially informed by the principles of the F(L)OSS (Free, Libre, and Open Source Software) and free culture movements.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;postmarketos-podcast&quot;&gt;postmarketOS Podcast&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;cast.postmarketos.org&amp;#x2F;&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;cast.postmarketos.org&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;You just found the corner of the Internet, where postmarketOS team members discuss whatever crazy development details we encounter while cooking up our favorite mobile Linux distribution. Besides news and anecdotes, once in a while we throw in an interview with an exciting guest who brings us their unique view on the wider mobile Linux scene.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;deutschsprachige-podcasts&quot;&gt;Deutschsprachige Podcasts&lt;&#x2F;h2&gt;
&lt;!--LANGUAGE:de--&gt;
&lt;h3 id=&quot;chaosradio&quot;&gt;Chaosradio&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;chaosradio.de&amp;#x2F;&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;chaosradio.de&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Chaosradio ist der Podcast des Chaos Computer Club Berlin (CCCB). In dem (bis zu) zweistündigen Format informieren wir seit 1995 über technische und gesellschaftliche Themen. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;chaosradio.de&#x2F;about&quot;&gt;Weiterlesen …&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;c-radar&quot;&gt;C-RadaR&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.c-radar.de&amp;#x2F;&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;www.c-radar.de&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Monatliche Radiosendung des Chaos Computer Clubs auf Radio Darmstadt. Jeden 2ten Donnerstag im Monat, 21-23 Uhr. 103,4 MHz UKW &#x2F; DAB+ &#x2F; Stream. Tune In!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;pentaradio&quot;&gt;Pentaradio&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.c3d2.de&amp;#x2F;radio.html&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;www.c3d2.de&amp;#x2F;radio.html
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Magazin für Netzkultur und Politik des Chaos Computer Clubs Dresden C3D2.
jeden vierten Dienstag im Monat 21:30 – 23:00 Uhr
&lt;cite&gt;(Beschreibung: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;coloradio.org&#x2F;?page_id=5199&quot;&gt;coloRadio&lt;&#x2F;a&gt;)&lt;&#x2F;cite&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Webdirectories</title>
        <published>2022-11-03T00: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/notebook/webdirectories/"/>
        <id>https://slatecave.net/notebook/webdirectories/</id>
        
        <summary type="text">A collection of link collections I consider worth sharing</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/webdirectories/">&lt;h2 id=&quot;webrings&quot;&gt;Webrings&lt;&#x2F;h2&gt;
&lt;p&gt;Listed roughly in order of preference, it is &lt;em&gt;NOT&lt;&#x2F;em&gt; a ranking. Prefernce in this case means: In therms of how many sites I personally conider interesting. Webrings I found more recently or just don&#x27;t explore as often will naturally rank lower.&lt;&#x2F;p&gt;
&lt;p&gt;In case you are new to webrings: Webrings are sometimes members of other webrings and a few sites are members of a lot of webrings so the below are just some possible entry points.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;geekring&quot;&gt;Geekring&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;geekring.net&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;geekring.net
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;For all those funny, quirky, interesting, personal, strange, offbeat websites that&#x27;s not page 1 on the search engines.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;hotline-webring&quot;&gt;Hotline Webring&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;b&gt;Has no restrictions for joining and may contain objectionable content.&lt;&#x2F;b&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;hotlinewebring.club&amp;#x2F;&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;hotlinewebring.club&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Geekring with more retro themed websites.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-retronaut-webring&quot;&gt;The retronaut webring&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;b&gt;Has no restrictions for joining and may contain objectionable content.&lt;&#x2F;b&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;webring.dinhe.net&amp;#x2F;&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;webring.dinhe.net&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;A webring that contains all kinds of sites.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-yesterweb-ring&quot;&gt;The Yesterweb Ring&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;yesterweb.org&amp;#x2F;webring&amp;#x2F;&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;yesterweb.org&amp;#x2F;webring&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;This webring is for anyone who is tired of how boring and same-y the internet is today. It&#x27;s for anyone who is sick of seeing websites used purely to drive monetization, informative blogs that ask you to subscribe to see content.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;xxiivv-webring&quot;&gt;XXIIVV Webring&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;webring.xxiivv.com&amp;#x2F;&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;webring.xxiivv.com&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;This webring is an attempt to inspire artists &amp;amp; developers to build their websites and share traffic amongst each other. The ring welcomes hand-crafted wikis and portfolios.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;the-512kb-club&quot;&gt;The 512KB Club&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;512kb.club&amp;#x2F;&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;512kb.club&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The 512KB Club is a collection of performance-focused web pages from across the Internet.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;More of a directory but sometimes considered a webring (it&#x27;s missin the ring-part though)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;envs-webring&quot;&gt;envs - webring&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;envs.net&amp;#x2F;ring&amp;#x2F;&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;envs.net&amp;#x2F;ring&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;this webring can be joined by any user on envs.net.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;actual-web-directories&quot;&gt;Actual web directories&lt;&#x2F;h2&gt;
&lt;p&gt;The real phonebooks of the internet!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ooh-directory&quot;&gt;ooh.directory&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;ooh.directory&amp;#x2F;&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;ooh.directory&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;A collection of 2,295 blogs about every topic&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The counter in the description is probably outdated by the time you are reding this 😃.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;sites-with-lots-of-interesting-links&quot;&gt;Sites with lots of interesting links&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;seirdy-s-home&quot;&gt;Seirdy’s Home&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;seirdy.one&amp;#x2F;&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;seirdy.one&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Not only a source of great links but also great articles containing them.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tinygem&quot;&gt;TinyGem&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;tinygem.org&amp;#x2F;&quot; &gt;
		https:&amp;#x2F;&amp;#x2F;tinygem.org&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Have you ever bookmarked a page and worried that you will not find the same stuff next time you are there? Keep the pages on the web saved forever.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;A searchable link aggregator for links people find interesting.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>ASCII Table</title>
        <published>2022-10-16T00:00:00+00:00</published>
        <updated>2024-08-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/ascii/"/>
        <id>https://slatecave.net/notebook/ascii/</id>
        
        <summary type="text">A simple, plain ASCII-Table</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/ascii/">&lt;p&gt;This ASCII-Table is mostly derived from the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.unicode.org&#x2F;Public&#x2F;UCD&#x2F;latest&#x2F;&quot;&gt;Unicode 15.0 Documentation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mostly-printable-characters&quot;&gt;Mostly Printable Characters&lt;&#x2F;h2&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-action-next&quot; href=&quot;#control-characters&quot; &gt;
		Skip to the control characters table
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Codes 0x20 to 0x7E are referred to as the printable characters, 0x7F is a control character. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ASCII#Printable_characters&quot;&gt;Wikipedia has more background information.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;div class=&quot;character-tables&quot;&gt;
&lt;table class=&quot;character-table&quot;&gt;
&lt;caption&gt;Numbers and Specials&lt;&#x2F;caption&gt;
&lt;tr&gt;&lt;th&gt;Char&lt;&#x2F;th&gt;&lt;th&gt;Hex&lt;&#x2F;th&gt;&lt;th&gt;Dec&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SPACE&lt;&#x2F;td&gt;&lt;td&gt;20&lt;&#x2F;td&gt;&lt;td&gt;32&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;!&lt;&#x2F;td&gt;&lt;td&gt;21&lt;&#x2F;td&gt;&lt;td&gt;33&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&quot;&lt;&#x2F;td&gt;&lt;td&gt;22&lt;&#x2F;td&gt;&lt;td&gt;34&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;#&lt;&#x2F;td&gt;&lt;td&gt;23&lt;&#x2F;td&gt;&lt;td&gt;35&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;$&lt;&#x2F;td&gt;&lt;td&gt;24&lt;&#x2F;td&gt;&lt;td&gt;36&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;%&lt;&#x2F;td&gt;&lt;td&gt;25&lt;&#x2F;td&gt;&lt;td&gt;37&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;&lt;&#x2F;td&gt;&lt;td&gt;26&lt;&#x2F;td&gt;&lt;td&gt;38&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&#x27;&lt;&#x2F;td&gt;&lt;td&gt;27&lt;&#x2F;td&gt;&lt;td&gt;39&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;(&lt;&#x2F;td&gt;&lt;td&gt;28&lt;&#x2F;td&gt;&lt;td&gt;40&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;)&lt;&#x2F;td&gt;&lt;td&gt;29&lt;&#x2F;td&gt;&lt;td&gt;41&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;*&lt;&#x2F;td&gt;&lt;td&gt;2a&lt;&#x2F;td&gt;&lt;td&gt;42&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;+&lt;&#x2F;td&gt;&lt;td&gt;2b&lt;&#x2F;td&gt;&lt;td&gt;43&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;,&lt;&#x2F;td&gt;&lt;td&gt;2c&lt;&#x2F;td&gt;&lt;td&gt;44&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;-&lt;&#x2F;td&gt;&lt;td&gt;2d&lt;&#x2F;td&gt;&lt;td&gt;45&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;.&lt;&#x2F;td&gt;&lt;td&gt;2e&lt;&#x2F;td&gt;&lt;td&gt;46&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&#x2F;&lt;&#x2F;td&gt;&lt;td&gt;2f&lt;&#x2F;td&gt;&lt;td&gt;47&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;30&lt;&#x2F;td&gt;&lt;td&gt;48&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;31&lt;&#x2F;td&gt;&lt;td&gt;49&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;td&gt;32&lt;&#x2F;td&gt;&lt;td&gt;50&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;&#x2F;td&gt;&lt;td&gt;33&lt;&#x2F;td&gt;&lt;td&gt;51&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;4&lt;&#x2F;td&gt;&lt;td&gt;34&lt;&#x2F;td&gt;&lt;td&gt;52&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;5&lt;&#x2F;td&gt;&lt;td&gt;35&lt;&#x2F;td&gt;&lt;td&gt;53&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;6&lt;&#x2F;td&gt;&lt;td&gt;36&lt;&#x2F;td&gt;&lt;td&gt;54&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;7&lt;&#x2F;td&gt;&lt;td&gt;37&lt;&#x2F;td&gt;&lt;td&gt;55&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;8&lt;&#x2F;td&gt;&lt;td&gt;38&lt;&#x2F;td&gt;&lt;td&gt;56&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;9&lt;&#x2F;td&gt;&lt;td&gt;39&lt;&#x2F;td&gt;&lt;td&gt;57&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;:&lt;&#x2F;td&gt;&lt;td&gt;3a&lt;&#x2F;td&gt;&lt;td&gt;58&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;;&lt;&#x2F;td&gt;&lt;td&gt;3b&lt;&#x2F;td&gt;&lt;td&gt;59&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;lt;&lt;&#x2F;td&gt;&lt;td&gt;3c&lt;&#x2F;td&gt;&lt;td&gt;60&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;=&lt;&#x2F;td&gt;&lt;td&gt;3d&lt;&#x2F;td&gt;&lt;td&gt;61&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;gt;&lt;&#x2F;td&gt;&lt;td&gt;3e&lt;&#x2F;td&gt;&lt;td&gt;62&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;?&lt;&#x2F;td&gt;&lt;td&gt;3f&lt;&#x2F;td&gt;&lt;td&gt;63&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;table class=&quot;character-table&quot;&gt;
&lt;caption&gt;Uppercase&lt;&#x2F;caption&gt;
&lt;tr&gt;&lt;th&gt;Char&lt;&#x2F;th&gt;&lt;th&gt;Hex&lt;&#x2F;th&gt;&lt;th&gt;Dec&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;@&lt;&#x2F;td&gt;&lt;td&gt;40&lt;&#x2F;td&gt;&lt;td&gt;64&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;&#x2F;td&gt;&lt;td&gt;41&lt;&#x2F;td&gt;&lt;td&gt;65&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;B&lt;&#x2F;td&gt;&lt;td&gt;42&lt;&#x2F;td&gt;&lt;td&gt;66&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;C&lt;&#x2F;td&gt;&lt;td&gt;43&lt;&#x2F;td&gt;&lt;td&gt;67&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;D&lt;&#x2F;td&gt;&lt;td&gt;44&lt;&#x2F;td&gt;&lt;td&gt;68&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;E&lt;&#x2F;td&gt;&lt;td&gt;45&lt;&#x2F;td&gt;&lt;td&gt;69&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;F&lt;&#x2F;td&gt;&lt;td&gt;46&lt;&#x2F;td&gt;&lt;td&gt;70&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;G&lt;&#x2F;td&gt;&lt;td&gt;47&lt;&#x2F;td&gt;&lt;td&gt;71&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;H&lt;&#x2F;td&gt;&lt;td&gt;48&lt;&#x2F;td&gt;&lt;td&gt;72&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;&#x2F;td&gt;&lt;td&gt;49&lt;&#x2F;td&gt;&lt;td&gt;73&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;J&lt;&#x2F;td&gt;&lt;td&gt;4a&lt;&#x2F;td&gt;&lt;td&gt;74&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;K&lt;&#x2F;td&gt;&lt;td&gt;4b&lt;&#x2F;td&gt;&lt;td&gt;75&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;L&lt;&#x2F;td&gt;&lt;td&gt;4c&lt;&#x2F;td&gt;&lt;td&gt;76&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;M&lt;&#x2F;td&gt;&lt;td&gt;4d&lt;&#x2F;td&gt;&lt;td&gt;77&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;N&lt;&#x2F;td&gt;&lt;td&gt;4e&lt;&#x2F;td&gt;&lt;td&gt;78&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;O&lt;&#x2F;td&gt;&lt;td&gt;4f&lt;&#x2F;td&gt;&lt;td&gt;79&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;P&lt;&#x2F;td&gt;&lt;td&gt;50&lt;&#x2F;td&gt;&lt;td&gt;80&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Q&lt;&#x2F;td&gt;&lt;td&gt;51&lt;&#x2F;td&gt;&lt;td&gt;81&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;R&lt;&#x2F;td&gt;&lt;td&gt;52&lt;&#x2F;td&gt;&lt;td&gt;82&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;S&lt;&#x2F;td&gt;&lt;td&gt;53&lt;&#x2F;td&gt;&lt;td&gt;83&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;T&lt;&#x2F;td&gt;&lt;td&gt;54&lt;&#x2F;td&gt;&lt;td&gt;84&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;U&lt;&#x2F;td&gt;&lt;td&gt;55&lt;&#x2F;td&gt;&lt;td&gt;85&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;V&lt;&#x2F;td&gt;&lt;td&gt;56&lt;&#x2F;td&gt;&lt;td&gt;86&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;W&lt;&#x2F;td&gt;&lt;td&gt;57&lt;&#x2F;td&gt;&lt;td&gt;87&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;X&lt;&#x2F;td&gt;&lt;td&gt;58&lt;&#x2F;td&gt;&lt;td&gt;88&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;59&lt;&#x2F;td&gt;&lt;td&gt;89&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Z&lt;&#x2F;td&gt;&lt;td&gt;5a&lt;&#x2F;td&gt;&lt;td&gt;90&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;[&lt;&#x2F;td&gt;&lt;td&gt;5b&lt;&#x2F;td&gt;&lt;td&gt;91&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;\&lt;&#x2F;td&gt;&lt;td&gt;5c&lt;&#x2F;td&gt;&lt;td&gt;92&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;]&lt;&#x2F;td&gt;&lt;td&gt;5d&lt;&#x2F;td&gt;&lt;td&gt;93&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;^&lt;&#x2F;td&gt;&lt;td&gt;5e&lt;&#x2F;td&gt;&lt;td&gt;94&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;_&lt;&#x2F;td&gt;&lt;td&gt;5f&lt;&#x2F;td&gt;&lt;td&gt;95&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;table class=&quot;character-table&quot;&gt;
&lt;caption&gt;Lowercase&lt;&#x2F;caption&gt;
&lt;tr&gt;&lt;th&gt;Char&lt;&#x2F;th&gt;&lt;th&gt;Hex&lt;&#x2F;th&gt;&lt;th&gt;Dec&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;#96;&lt;&#x2F;td&gt;&lt;td&gt;60&lt;&#x2F;td&gt;&lt;td&gt;96&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;a&lt;&#x2F;td&gt;&lt;td&gt;61&lt;&#x2F;td&gt;&lt;td&gt;97&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;b&lt;&#x2F;td&gt;&lt;td&gt;62&lt;&#x2F;td&gt;&lt;td&gt;98&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;c&lt;&#x2F;td&gt;&lt;td&gt;63&lt;&#x2F;td&gt;&lt;td&gt;99&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;d&lt;&#x2F;td&gt;&lt;td&gt;64&lt;&#x2F;td&gt;&lt;td&gt;100&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;e&lt;&#x2F;td&gt;&lt;td&gt;65&lt;&#x2F;td&gt;&lt;td&gt;101&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;f&lt;&#x2F;td&gt;&lt;td&gt;66&lt;&#x2F;td&gt;&lt;td&gt;102&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;g&lt;&#x2F;td&gt;&lt;td&gt;67&lt;&#x2F;td&gt;&lt;td&gt;103&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;h&lt;&#x2F;td&gt;&lt;td&gt;68&lt;&#x2F;td&gt;&lt;td&gt;104&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;i&lt;&#x2F;td&gt;&lt;td&gt;69&lt;&#x2F;td&gt;&lt;td&gt;105&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;j&lt;&#x2F;td&gt;&lt;td&gt;6a&lt;&#x2F;td&gt;&lt;td&gt;106&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;k&lt;&#x2F;td&gt;&lt;td&gt;6b&lt;&#x2F;td&gt;&lt;td&gt;107&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;l&lt;&#x2F;td&gt;&lt;td&gt;6c&lt;&#x2F;td&gt;&lt;td&gt;108&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;m&lt;&#x2F;td&gt;&lt;td&gt;6d&lt;&#x2F;td&gt;&lt;td&gt;109&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;n&lt;&#x2F;td&gt;&lt;td&gt;6e&lt;&#x2F;td&gt;&lt;td&gt;110&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;o&lt;&#x2F;td&gt;&lt;td&gt;6f&lt;&#x2F;td&gt;&lt;td&gt;111&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;p&lt;&#x2F;td&gt;&lt;td&gt;70&lt;&#x2F;td&gt;&lt;td&gt;112&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;q&lt;&#x2F;td&gt;&lt;td&gt;71&lt;&#x2F;td&gt;&lt;td&gt;113&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;r&lt;&#x2F;td&gt;&lt;td&gt;72&lt;&#x2F;td&gt;&lt;td&gt;114&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;s&lt;&#x2F;td&gt;&lt;td&gt;73&lt;&#x2F;td&gt;&lt;td&gt;115&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;t&lt;&#x2F;td&gt;&lt;td&gt;74&lt;&#x2F;td&gt;&lt;td&gt;116&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;u&lt;&#x2F;td&gt;&lt;td&gt;75&lt;&#x2F;td&gt;&lt;td&gt;117&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;v&lt;&#x2F;td&gt;&lt;td&gt;76&lt;&#x2F;td&gt;&lt;td&gt;118&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;w&lt;&#x2F;td&gt;&lt;td&gt;77&lt;&#x2F;td&gt;&lt;td&gt;119&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;x&lt;&#x2F;td&gt;&lt;td&gt;78&lt;&#x2F;td&gt;&lt;td&gt;120&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;y&lt;&#x2F;td&gt;&lt;td&gt;79&lt;&#x2F;td&gt;&lt;td&gt;121&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;z&lt;&#x2F;td&gt;&lt;td&gt;7a&lt;&#x2F;td&gt;&lt;td&gt;122&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;{&lt;&#x2F;td&gt;&lt;td&gt;7b&lt;&#x2F;td&gt;&lt;td&gt;123&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;|&lt;&#x2F;td&gt;&lt;td&gt;7c&lt;&#x2F;td&gt;&lt;td&gt;124&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;}&lt;&#x2F;td&gt;&lt;td&gt;7d&lt;&#x2F;td&gt;&lt;td&gt;125&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;~&lt;&#x2F;td&gt;&lt;td&gt;7e&lt;&#x2F;td&gt;&lt;td&gt;126&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;DEL&lt;&#x2F;td&gt;&lt;td&gt;7f&lt;&#x2F;td&gt;&lt;td&gt;127&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;&lt;b&gt;Note on the &lt;code&gt;DEL&lt;&#x2F;code&gt; code:&lt;&#x2F;b&gt;The DEL being 0x7F, meaning it has all 7 bits set, probably originates from how one &quot;deleted&quot; something from paper tape, by just punching all of the holes one could &quot;erase&quot; the original character. Its meaning is ambigious, but it usually refers to a backspace.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Fun Fact:&lt;&#x2F;b&gt; The lowercase characters in ASCII were an &quot;aftertought&quot; and only added later in 1965, two years after the initial release.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;control-characters&quot;&gt;Control characters&lt;&#x2F;h2&gt;
&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Char&lt;&#x2F;th&gt;&lt;th&gt;Hex&lt;&#x2F;th&gt;&lt;th&gt;Oct&lt;&#x2F;th&gt;&lt;th&gt;Dec&lt;&#x2F;th&gt;&lt;th&gt;Name&lt;&#x2F;th&gt;&lt;th&gt;C Esc&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;NUL&lt;&#x2F;td&gt;&lt;td&gt;00&lt;&#x2F;td&gt;&lt;td&gt;000&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Null&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SOH&lt;&#x2F;td&gt;&lt;td&gt;01&lt;&#x2F;td&gt;&lt;td&gt;001&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Start Of Heading&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;STX&lt;&#x2F;td&gt;&lt;td&gt;02&lt;&#x2F;td&gt;&lt;td&gt;002&lt;&#x2F;td&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Start Of Text&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;ETX&lt;&#x2F;td&gt;&lt;td&gt;03&lt;&#x2F;td&gt;&lt;td&gt;003&lt;&#x2F;td&gt;&lt;td&gt;3&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;End Of Text&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;EOT&lt;&#x2F;td&gt;&lt;td&gt;04&lt;&#x2F;td&gt;&lt;td&gt;004&lt;&#x2F;td&gt;&lt;td&gt;4&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;End Of Transmission&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;ENQ&lt;&#x2F;td&gt;&lt;td&gt;05&lt;&#x2F;td&gt;&lt;td&gt;005&lt;&#x2F;td&gt;&lt;td&gt;5&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Enquiry&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;ACK&lt;&#x2F;td&gt;&lt;td&gt;06&lt;&#x2F;td&gt;&lt;td&gt;006&lt;&#x2F;td&gt;&lt;td&gt;6&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Acknowledge&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;BEL&lt;&#x2F;td&gt;&lt;td&gt;07&lt;&#x2F;td&gt;&lt;td&gt;007&lt;&#x2F;td&gt;&lt;td&gt;7&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Bell &#x2F; Alert&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;\a&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;BS&lt;&#x2F;td&gt;&lt;td&gt;08&lt;&#x2F;td&gt;&lt;td&gt;010&lt;&#x2F;td&gt;&lt;td&gt;8&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;BAckspace&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;HT&lt;&#x2F;td&gt;&lt;td&gt;09&lt;&#x2F;td&gt;&lt;td&gt;011&lt;&#x2F;td&gt;&lt;td&gt;9&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;HOrizontal Tab&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;\t&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;LF&lt;&#x2F;td&gt;&lt;td&gt;0a&lt;&#x2F;td&gt;&lt;td&gt;012&lt;&#x2F;td&gt;&lt;td&gt;10&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Line Feed&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;\n&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;VT&lt;&#x2F;td&gt;&lt;td&gt;0b&lt;&#x2F;td&gt;&lt;td&gt;013&lt;&#x2F;td&gt;&lt;td&gt;11&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Vertical Tab&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;\v&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;FF&lt;&#x2F;td&gt;&lt;td&gt;0c&lt;&#x2F;td&gt;&lt;td&gt;014&lt;&#x2F;td&gt;&lt;td&gt;12&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Form Feed&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;\f&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;CR&lt;&#x2F;td&gt;&lt;td&gt;0d&lt;&#x2F;td&gt;&lt;td&gt;015&lt;&#x2F;td&gt;&lt;td&gt;13&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Carriage Return&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;\r&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SO&lt;&#x2F;td&gt;&lt;td&gt;0e&lt;&#x2F;td&gt;&lt;td&gt;016&lt;&#x2F;td&gt;&lt;td&gt;14&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Shift Out&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SI&lt;&#x2F;td&gt;&lt;td&gt;0f&lt;&#x2F;td&gt;&lt;td&gt;017&lt;&#x2F;td&gt;&lt;td&gt;15&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Shift In&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;DLE&lt;&#x2F;td&gt;&lt;td&gt;10&lt;&#x2F;td&gt;&lt;td&gt;020&lt;&#x2F;td&gt;&lt;td&gt;16&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Data Link Escape&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;DC1&lt;&#x2F;td&gt;&lt;td&gt;11&lt;&#x2F;td&gt;&lt;td&gt;021&lt;&#x2F;td&gt;&lt;td&gt;17&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Device Control 1&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;DC2&lt;&#x2F;td&gt;&lt;td&gt;12&lt;&#x2F;td&gt;&lt;td&gt;022&lt;&#x2F;td&gt;&lt;td&gt;18&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Device Control 2&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;DC3&lt;&#x2F;td&gt;&lt;td&gt;13&lt;&#x2F;td&gt;&lt;td&gt;023&lt;&#x2F;td&gt;&lt;td&gt;19&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Device Control 3&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;DC4&lt;&#x2F;td&gt;&lt;td&gt;14&lt;&#x2F;td&gt;&lt;td&gt;024&lt;&#x2F;td&gt;&lt;td&gt;20&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Device Control 4&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;NAK&lt;&#x2F;td&gt;&lt;td&gt;15&lt;&#x2F;td&gt;&lt;td&gt;025&lt;&#x2F;td&gt;&lt;td&gt;21&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Negative Acknowledge&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SYN&lt;&#x2F;td&gt;&lt;td&gt;16&lt;&#x2F;td&gt;&lt;td&gt;026&lt;&#x2F;td&gt;&lt;td&gt;22&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Synchronous Idle&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;ETB&lt;&#x2F;td&gt;&lt;td&gt;17&lt;&#x2F;td&gt;&lt;td&gt;027&lt;&#x2F;td&gt;&lt;td&gt;23&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;End Of Transmission Block&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;CAN&lt;&#x2F;td&gt;&lt;td&gt;18&lt;&#x2F;td&gt;&lt;td&gt;030&lt;&#x2F;td&gt;&lt;td&gt;24&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Cancel&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;EM&lt;&#x2F;td&gt;&lt;td&gt;19&lt;&#x2F;td&gt;&lt;td&gt;031&lt;&#x2F;td&gt;&lt;td&gt;25&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;End Of Medium&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SUB&lt;&#x2F;td&gt;&lt;td&gt;1a&lt;&#x2F;td&gt;&lt;td&gt;032&lt;&#x2F;td&gt;&lt;td&gt;26&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Substitute&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;ESC&lt;&#x2F;td&gt;&lt;td&gt;1b&lt;&#x2F;td&gt;&lt;td&gt;033&lt;&#x2F;td&gt;&lt;td&gt;27&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Escape&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;\e&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;FS&lt;&#x2F;td&gt;&lt;td&gt;1c&lt;&#x2F;td&gt;&lt;td&gt;034&lt;&#x2F;td&gt;&lt;td&gt;28&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;File Seperator&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;GS&lt;&#x2F;td&gt;&lt;td&gt;1d&lt;&#x2F;td&gt;&lt;td&gt;035&lt;&#x2F;td&gt;&lt;td&gt;29&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Group Seperator&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;RS&lt;&#x2F;td&gt;&lt;td&gt;1e&lt;&#x2F;td&gt;&lt;td&gt;036&lt;&#x2F;td&gt;&lt;td&gt;30&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Record Seperator&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;US&lt;&#x2F;td&gt;&lt;td&gt;1f&lt;&#x2F;td&gt;&lt;td&gt;037&lt;&#x2F;td&gt;&lt;td&gt;31&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Unit Seperator&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; The Names have been titlecased to make them easier to read.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;notation&quot;&gt;Notation&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;escaping-in-scripting-and-programming&quot;&gt;Escaping in Scripting and Programming&lt;&#x2F;h3&gt;
&lt;p&gt;Sometimes in languages one can&#x27;t use all the characters available in the source file, especially when using control characters. Encoding these as other characters is called &lt;i&gt;escaping&lt;&#x2F;i&gt;. This usually happens with text between double quotes &lt;code&gt;&quot;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There are differences between languages and implementations but the general rules are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Special characters preceeded by a backslash &lt;code&gt;\&lt;&#x2F;code&gt; are taken literally. (i.e. &lt;code&gt;\&quot;&lt;&#x2F;code&gt; or &lt;code&gt;\\&lt;&#x2F;code&gt;) (The exact behaviour varies &lt;em&gt;a lot&lt;&#x2F;em&gt; by language.)&lt;&#x2F;li&gt;
&lt;li&gt;Latin characters preceeded by a backslash to generate control characters (i.e. &lt;code&gt;\n&lt;&#x2F;code&gt;) (The &lt;i&gt;C Esc&lt;&#x2F;i&gt; column)&lt;&#x2F;li&gt;
&lt;li&gt;Hexadecimal value of the character prefixed by &lt;code&gt;\x&lt;&#x2F;code&gt; (i.e. &lt;code&gt;0x1b&lt;&#x2F;code&gt;) (should work almost everywhere)&lt;&#x2F;li&gt;
&lt;li&gt;Octal encoding prefixed by just a backslash (i.e. &lt;code&gt;\033&lt;&#x2F;code&gt;) (&lt;a href=&quot;&#x2F;notebook&#x2F;ansi-escape-sequences&#x2F;#setting-text-color-and-effects&quot;&gt;Use this when using &lt;code&gt;printf&lt;&#x2F;code&gt; on the command line&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; &lt;code&gt;C Esc&lt;&#x2F;code&gt; here is short C-Escape as the C language apparantly started this way of excaping characters in strings. It is now used by almost all modern languages and in other contexts. i.e. &lt;code&gt;printf&lt;&#x2F;code&gt;, &lt;code&gt;sed&lt;&#x2F;code&gt;, etc. exact support may vary.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;escaping-in-xml-and-html&quot;&gt;Escaping in XML and HTML&lt;&#x2F;h3&gt;
&lt;p&gt;XML and html have their own way of escaping non-printable characters, this involves a sequence sandwiched between an ampersand &lt;code&gt;&amp;amp;&lt;&#x2F;code&gt; and a semicolon &lt;code&gt;;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In general Characters can be escaped using &lt;code&gt;&amp;amp;#&amp;lt;dec&amp;gt;;&lt;&#x2F;code&gt; where &lt;code&gt;&amp;lt;dec&amp;gt;&lt;&#x2F;code&gt; is replaced by the decimal value associted with the character. (i.e, &lt;code&gt;&amp;amp;#38;&lt;&#x2F;code&gt; to encode an ampersand &lt;code&gt;&amp;amp;&lt;&#x2F;code&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;There are also named escapes to make remembering them easier:&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Character&lt;&#x2F;th&gt;&lt;th&gt;XML-escape&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;&amp;amp;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;&amp;amp;amp;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;&amp;lt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;&amp;amp;lt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;&amp;amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;&amp;quot;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;&amp;amp;quot;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;h3 id=&quot;control-and-shift&quot;&gt;Control and Shift&lt;&#x2F;h3&gt;
&lt;p&gt;With the ASCII table control and shift keys an be implemented using simple addition and substraction.&lt;&#x2F;p&gt;
&lt;p&gt;The lowercase character can be obtained by adding 32 (0x20) to the code of the corresponding uppercase character and the control key goes 64 (0x40) in the opposite direction.&lt;&#x2F;p&gt;
&lt;p&gt;With that &lt;kbd&gt;ctrl+c&lt;&#x2F;kbd&gt; maps to code 3 &quot;End of Text&quot;. And &lt;kbd&gt;ctrl+d&lt;&#x2F;kbd&gt; maps to &quot;End of Transmission&quot;. You may know those shortcuts from the terminal, they hopefully make a bit more sense now.&lt;&#x2F;p&gt;
&lt;p&gt;Control characters are sometimes written down&#x2F;printed as the character one gets when adding 64 to the control characters value prefixed by an &lt;code&gt;^&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This maps escape to &lt;code&gt;^]&lt;&#x2F;code&gt; and the nullbyte to &lt;code&gt;^@&lt;&#x2F;code&gt;. (You have probably seen those when opening a binary file in a text editor.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;unicode&quot;&gt;Unicode&lt;&#x2F;h2&gt;
&lt;p&gt;Creating a unicode table andkeeping it updated is out of scope here, besides that: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;List_of_Unicode_characters&quot;&gt;Wikipedia has a List of Unicode Characters&lt;&#x2F;a&gt; and there are the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.unicode.org&#x2F;charts&#x2F;&quot;&gt;offical Unicode Character Code Charts&lt;&#x2F;a&gt;. (Plus a whole lot of other unicode tables out there.)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Notation Hint:&lt;&#x2F;b&gt; Unicode codepoints (that is the number assigned in the unicode standard, not the binary character representation) are written &lt;code&gt;U+&amp;lt;hex&amp;gt;&lt;&#x2F;code&gt; where &lt;code&gt;&amp;lt;hex&amp;gt;&lt;&#x2F;code&gt; is the codepint number in hexadecimal. Example: &lt;code&gt;U+1f603&lt;&#x2F;code&gt; for 😃.&lt;&#x2F;p&gt;
&lt;p&gt;There is also a little commandline tool called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arp242&#x2F;uni&quot;&gt;&lt;code&gt;uni&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, that is pretty good at providing a searchable unicode table.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Shell Snippets</title>
        <published>2022-10-16T00:00:00+00:00</published>
        <updated>2026-05-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/shell/"/>
        <id>https://slatecave.net/notebook/shell/</id>
        
        <summary type="text">Some shellcode I catch myself writing over and over again.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/shell/">&lt;p&gt;The snippets on this site are intended for getting copied and being useful so please do that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;argument-parser&quot;&gt;Argument Parser&lt;&#x2F;h2&gt;
&lt;p&gt;A simple argument parser for bash.&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;bash&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 the script fail when a command fails&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;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;&#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; Settings variables go here&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;FOO&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;foo&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-entity z-name z-function&quot;&gt;show_help&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-entity z-name&quot;&gt;cat&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;EOF&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;Usage: foo &amp;lt;&amp;lt;options&amp;gt;&amp;gt; &amp;lt;bar&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-string&quot;&gt;Blubber blah blah ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;EOF&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-keyword z-control&quot;&gt;while&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;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&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 z-logical&quot;&gt; -gt&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&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; do&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;	case&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-keyword z-control&quot;&gt; in&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string z-regexp&quot;&gt;		-&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-regexp&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-regexp&quot;&gt;f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-regexp&quot;&gt;o&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-regexp&quot;&gt;o&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-variable z-other&quot;&gt;			FOO&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-variable z-parameter&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter&quot;&gt;2&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-support z-function z-builtin&quot;&gt;			shift&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 2&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;		&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string z-regexp&quot;&gt;		-&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-regexp&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-regexp&quot;&gt;h&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-regexp&quot;&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-regexp&quot;&gt;l&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-regexp&quot;&gt;p&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; show_help&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; exit&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;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;		*)&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-builtin&quot;&gt; printf&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;Unknown option: %s\n&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-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-builtin&quot;&gt; exit&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;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;	esac&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;done&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; Logic goes here&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;echo&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;FOO&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;h3 id=&quot;set-e-and-shift&quot;&gt;&lt;code&gt;set -e&lt;&#x2F;code&gt; and &lt;code&gt;shift&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;set -e&lt;&#x2F;code&gt; option tells the shell to stop executing a script once a command fails. This is important when using a &lt;code&gt;shift&lt;&#x2F;code&gt; in this kind of while loop where the loop exiting depends on the &lt;code&gt;shift&lt;&#x2F;code&gt; working.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; The shift may fail if it is told to move 2 arguments out (&lt;code&gt;shift 2&lt;&#x2F;code&gt;) but there is only one given. In the example above that would be fulfilled when passing &lt;code&gt;--foo&lt;&#x2F;code&gt; as the last argument (a relatively easy to make mistake). If the shift silently failed the argument counter would never reach 0 and the script would be stuck in an endless loop.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mktemp-tempdir-but-for-not-just-gnu&quot;&gt;&lt;code&gt;mktemp --tempdir&lt;&#x2F;code&gt; but for not just gnu&lt;&#x2F;h2&gt;
&lt;p&gt;With the GNU and busybox coreutils the &lt;code&gt;--tempdir&lt;&#x2F;code&gt; option of mktemp is very useful when using &lt;code&gt;mktemp&lt;&#x2F;code&gt; with a template name and you want it to stay in the standard tempdir. The following snippet also does this for other &lt;code&gt;mktemp&lt;&#x2F;code&gt; implementations:&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;mktemp&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-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;span class=&quot;z-variable z-other&quot;&gt;TEMPDIR&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;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;tmp&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&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;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; your-template-here.XXXXXXXXX&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;hook-helper&quot;&gt;Hook Helper&lt;&#x2F;h2&gt;
&lt;p&gt;This little function calls its argument as an ad-hoc shellscript if it is non-empty, if it returns a nonzero, one should call the default action.&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; on fail do default action&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;run_hook&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-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-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-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; bash&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;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;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;figure&gt;
	&lt;figcaption&gt;Example of how this little helper is intended to be used&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-storage z-modifier&quot;&gt;export&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; VARIABLE_WHICH_IS_USEFUL_FOR_HOOK&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;something&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-name&quot;&gt;run_hook&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;HOOK_COMMAND&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-keyword z-operator&quot;&gt;|&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; default_action&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;p&gt;An &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;example of this being used&lt;&#x2F;a&gt; can be found in my dotfiles with the filedialog. (Fun fact: It was created for this script)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;grep-regex-match-helper&quot;&gt;Grep Regex Match Helper&lt;&#x2F;h2&gt;
&lt;p&gt;This function pipes its first argument into a &lt;code&gt;grep -q&lt;&#x2F;code&gt; command which is given the rest of the arguments.&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 z-function&quot;&gt;matches&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;	TEXT&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-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;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function z-builtin&quot;&gt;	shift&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&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;	printf&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\n&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-other&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;TEXT&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; 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;q&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;@&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-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Example usage: &lt;code&gt;matches &quot;$FOO&quot; &quot;^[0-9]$&quot; &amp;amp;&amp;amp; echo &quot;FOO is a number! :)&quot; || FOO=0&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It can also be useful to implement some kind of heuristics based on which patterns occur in a text.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bash-arrays&quot;&gt;Bash Arrays&lt;&#x2F;h2&gt;
&lt;p&gt;Bash has arrays so one doesn&#x27;t have to care about delimiters too much.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note on compatibility:&lt;&#x2F;b&gt; These arrays only work in bash which isn&#x27;t isntalled everywhere (dash is a popular alternative). Please use &lt;code&gt;#!&#x2F;bin&#x2F;bash&lt;&#x2F;code&gt; instead of &lt;s&gt;&lt;code&gt;#!&#x2F;bin&#x2F;sh&lt;&#x2F;code&gt;&lt;&#x2F;s&gt; to indicate that your script requires bash to work.&lt;&#x2F;p&gt;
&lt;p&gt;Create an array:&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;empty_array&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-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;array&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-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;text&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;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;can go&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-support z-function z-builtin&quot;&gt; echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; here&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-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-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Appending to an exisiting Array:&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;array&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-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;foo&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;array&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-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;add&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-string&quot;&gt;multiple&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-string&quot;&gt;elements&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;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These can then be accessed with 0 based indices:&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;echo&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;span class=&quot;z-variable z-other&quot;&gt;array&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&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;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-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; prints out: can go&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To get the number of elements in an array:&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;echo&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;span class=&quot;z-keyword z-operator&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;array&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;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;span class=&quot;z-support z-function z-builtin&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-punctuation&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-operator&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;array&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;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;An array can expand to multiple arguments:&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;printf&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;-&amp;gt; %s&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;span class=&quot;z-variable z-other&quot;&gt;array&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;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;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Compare with the output of:&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;printf&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;-&amp;gt; %s&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; foo&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; bar&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; baz&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which is also useful for &lt;code&gt;for&lt;&#x2F;code&gt;-loops:&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-keyword z-control&quot;&gt;for&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-control&quot;&gt; in&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;span class=&quot;z-variable z-other&quot;&gt;array&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;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-punctuation&quot;&gt; ;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; do&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;	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;-&amp;gt; &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;i&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 z-control&quot;&gt;done&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And to clean up:&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; remove element at index 1&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;uset&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; array[1]&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; remove the whole array&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;uset&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; array&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There is more to bash arrays (including associative arrays) …&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;www.gnu.org&amp;#x2F;software&amp;#x2F;bash&amp;#x2F;manual&amp;#x2F;html_node&amp;#x2F;Arrays.html&quot; &gt;
		Check out The Bash Reference Manual on Arrays
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;see-also&quot;&gt;See Also&lt;&#x2F;h2&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;ansi-escape-sequences&quot;&gt;ANSI Escape Sequences&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;decoration-action-next&quot; href=&quot;&#x2F;notebook&#x2F;portable-shell&quot;&gt;Portable Shellscripting&lt;&#x2F;a&gt;&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>Screwing around with Sliderland</title>
        <published>2022-06-14T00:00:00+00:00</published>
        <updated>2022-09-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/sliderland-screw/"/>
        <id>https://slatecave.net/creations/sliderland-screw/</id>
        
        <summary type="text">Things I made in blinrys awesome Sliderland playground.</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/sliderland-screw/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sliderland.blinry.org&#x2F;&quot;&gt;Sliderland&lt;&#x2F;a&gt; is a coding playground made by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blinry.org&#x2F;&quot;&gt;blinry&lt;&#x2F;a&gt; that evaluates some javascript and displays the result using 64 sliders (the original used 64 html sliders, but they were replaced in favour of a canvas that draws things that look like sliders because of browser compatibility). &lt;del datetime=&quot;2024-01-07&quot;&gt;I also host a Sliderland on sliderland.slatecave.net that has a dark mode and behaves more like a scrollable page.&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Some things I made with Sliderland follow, feel free to tweak them have fun and share them! (mentioning my name somewhere would be nice but isn&#x27;t required)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;screw-it-demo&quot;&gt;Screw it! Demo&lt;&#x2F;h2&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;sliderland.blinry.org&amp;#x2F;#%7B%0A%20%20%2F%2F%20sliders%0A%20%20var%20sliders%20%3D%2064%3B%0A%20%20%2F%2F%20speed%20%28negative%20values%20work%20too!%29%0A%20%20var%20z%20%3D%20t*3%3B%0A%20%20%2F%2F%20scale%20%280%20to%20.5%29%0A%20%20var%20scale%20%3D%20.2%3B%0A%20%20%2F%2F%20split%20%28how%20split%20up%20are%20two%20strands%20of%20waves%3F%29%0A%20%20var%20split%20%3D%200%3B%0A%20%20%2F%2F%20multiply%20%28make%20a%20screw%20of%20screws!%29%0A%20%20var%20multiply%20%3D%200%3B%0A%20%20%2F%2F%20number%20of%20edges%20%28waves%29%20%28looks%20less%20good%20for%20n%20%3E%205%29%0A%20%20var%20n%20%3D%200%3B%0A%20%20%2F%2F%20frequency%20%28when%20left%20at%200%20get%20set%20with%20some%20magic%20values%20that%20look%20pretty%29%0A%20%20var%20f%20%3D%200%0A%20%20%2F%2Ftransition%20clock%0A%20%20var%20tz%20%3D%20t%2F5%20%2B%20%28x*.2%29%3B%0A%20%20%2F%2F%20transition%20take%20how%20lmuch%20of%20a%20tick%3F%20%280%20to%201%29%0A%20%20var%20tp%20%3D%200.02%3B%0A%20%20%0A%20%20%2F%2F%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%205%7Csplit%0A%20%20var%20n_over_tz%20%3D%20%5B%200%2C%201%2C%202%2C%203%2C%204%2C%205%2C%201%2C%202%2C-2%2C-2%2C-2%2C%0A%20%20%2F%2F%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-2%2C-2%2C%203%2C%20-3%2C-3%2C%20-3%2C%204%2C-4%2C%20-4%2C%205%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-3%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%5D%3B%0A%20%20%0A%20%20%2F%2F%20restart%20when%20information%20about%20how%20waves%20ends%0A%20%20var%20tz%20%3D%20tz%25n_over_tz.length%3B%0A%20%20%0A%20%20%2F%2F%20linear%20transform%20from%20time%2C%20to%20time%2C%20current%20time%2C%20value%20a%2C%20value%20b%0A%20%20function%20ltr%28f%2Ct%2Cc%2Ca%2Cb%29%20%7B%0A%20%20%20%20var%20m%20%3D%20%28c-f%29%2F%28t-f%29%3B%0A%20%20%20%20return%20%28a*%281-m%29%2Bb*m%29%0A%20%20%7D%0A%20%20%0A%20%20%0A%20%20%2F%2F%20turn%20animated%20transitions%20on%0A%20%20if%20%28true%29%20%7B%0A%20%20%20%20n%20%3D%20n_over_tz%5BMath.ceil%28tz%29%5D%3B%20%2F%2Fgoes%20until%20n%3D5%20then%20repeats%0A%20%20%20%20if%20%28n_over_tz%5BMath.round%28tz%2B1%29%5D%20%3E%200%29%20%7B%0A%20%20%20%20%20%20if%20%28tz%251%20%3C%20%28tp%2F2%29%20%7C%7C%20%28tz%251%29%20%3E%20%281-%28tp%2F2%29%29%29%20%7B%0A%20%20%20%20%20%20%20%20%2F%2Fcalculate%20animation%20progress%20%280%20to%201%29%0A%20%20%20%20%20%20%20%20at%20%3D%20%28%28tz%2B%28tp%2F2%29%29%251%29%2Ftp%3B%0A%20%20%20%20%20%20%20%20%2F%2Fshrink%20and%20come%20up%20with%20new%20wave%20count%0A%20%20%20%20%20%20%20%20scale%20%3D%20scale*%28cos%28PI*2*at%29*.5%2B.5%29%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20if%20%28n%20%3C%200%29%20%7B%0A%20%20%20%20%20%20n%20%3D%20-n%3B%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20if%20%28tz%20%3E%205.2%20%26%26%20tz%20%3C%2011%29%20%7B%0A%20%20%20%20%20%20if%20%28tz%20%3C%205.7%29%20%7B%0A%20%20%20%20%20%20%20%20split%20%3D%20ltr%285.2%2C5.7%2Ctz%2C0%2C1%29%3B%0A%20%20%20%20%20%20%20%20scale%20%3D%20ltr%285.2%2C5.7%2Ctz%2C.2%2C.1%29%3B%0A%20%20%20%20%20%20%7D%20else%20if%20%28tz%20%3C%207.5%29%20%7B%0A%20%20%20%20%20%20%20%20split%20%3D%201%3B%0A%20%20%20%20%20%20%20%20scale%20%3D%20.1%3B%0A%20%20%20%20%20%20%7D%20else%20if%20%28tz%20%3C%2010%29%20%7B%0A%20%20%20%20%20%20%20%20split%20%3D%20cos%28ltr%287.5%2C10%2Ctz%2C0%2CPI*2%29*2%2Bx%29%3B%0A%20%20%20%20%20%20%20%20scale%20%3D%20.1%3B%0A%20%20%20%20%20%20%7D%20else%20if%20%28tz%20%3C%2010.5%29%20%7B%0A%20%20%20%20%20%20%20%20split%20%3D%20ltr%2810%2C10.5%2Ctz%2C1%2C0%29%3B%0A%20%20%20%20%20%20%20%20scale%20%3D%20.1%3B%0A%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20scale%20%3D%20ltr%2810.5%2C11%2Ctz%2C.1%2C.2%29%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20if%20%28tz%20%3E%2012%29%20%7B%0A%20%20%20%20%20%20if%20%28tz%20%3C%2013%29%20%7B%0A%20%20%20%20%20%20%20%20scale%20%3D%20ltr%2812%2C13%2Ctz%2C.2%2C.3%29%3B%0A%20%20%20%20%20%20%7D%20else%20if%20%28tz%20%3C%2014%29%20%7B%0A%20%20%20%20%20%20%20%20multiply%20%3D%20ltr%2813%2C14%2Ctz%2C1%2C2%29*%28x%2B.5%29%3B%0A%20%20%20%20%20%20%20%20scale%20%3D%20.3%3B%0A%20%20%20%20%20%20%7D%20else%20if%20%28tz%20%3C%2019%29%20%7B%0A%20%20%20%20%20%20%20%20multiply%20%3D%202*%28x%2B.5%29%3B%0A%20%20%20%20%20%20%20%20scale%20%3D%20.3%3B%0A%20%20%20%20%20%20%20%20f%20%3D%201%3B%0A%20%20%20%20%20%20%7D%20else%20if%20%28tz%20%3C%2020%29%7B%0A%20%20%20%20%20%20%20%20multiply%20%3D%20ltr%2819%2C20%2Ctz%2C2%2C1%29%3B%0A%20%20%20%20%20%20%20%20scale%20%3D%20ltr%2819%2C20%2Ctz%2C.3%2C.2%29%3B%0A%20%20%20%20%20%20%20%20f%20%3D%201%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20if%20%28tz%20%3E%2020%29%20%7B%0A%20%20%20%20%20%20n%20%3D%20ltr%2820%2C%2030%2C%20tz%2C%205%2C%200%29%3B%0A%20%20%20%20%20%20scale%20%3D%20ltr%2820%2C%2030%2C%20tz%2C%20.2%2C%200%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20if%20%28tz%20%3C%20.5%29%20%7B%0A%20%20%20%20%20%20n%20%3D%20ltr%280%2C%20.5%2C%20tz%2C%200.9%2C%201%29%3B%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20%0A%20%20%2F%2F%20frequency%20%28some%20magic%20values%20here%20that%20make%20it%20look%20okayish%29%0A%20%20if%20%28f%20%3D%3D%200%29%20%7B%0A%20%20%20%20f%20%3D%20%287*%283%2Fn%29%29%3B%0A%20%20%7D%0A%20%20%0A%20%20%2F%2F%20n%3A%20number%20of%20waves%0A%20%20%2F%2F%20f%3A%20frequency%0A%20%20%2F%2F%20t%3A%20current%20time%20%28maybe%20you%20want%20to%20manipulate%20it%29%0A%20%20%2F%2F%20max_i%3A%20how%20wide%20the%20field%20currently%20is%0A%20%20%2F%2F%20i%3A%20at%20which%20%22pixel%22%20we%20are%0A%20%20%2F%2F%20returns%3A%20a%20value%20between%20-1%20and%201%0A%20%20function%20screw%28n%2C%20f%2C%20t%2C%20max_i%2C%20i%29%20%7B%0A%20%20%20%20var%20x%20%3D%20i%2Fmax_i%3B%0A%20%20%20%20%2F%2F%20breakpoint%20when%20something%20goes%20into%20the%20background%0A%20%20%20%20var%20b%20%3D%201%3B%0A%20%20%20%20%2F%2Fugly%20hack%20%28maybe%20someone%20more%20skilled%20than%20me%20in%20math%20can%20figure%20out%20why%29%0A%20%20%20%20if%20%28n%20%3D%3D%203%29%20%7B%0A%20%20%20%20%20%20b%20%3D%20.865%3B%0A%20%20%20%20%7D%20if%20%28n%20%3E%3D%204%29%20%7B%0A%20%20%20%20%20%20b%20%3D%20Math.pow%282%2Fn%2C2%2FPI%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20var%20a%20%3D%200%3B%0A%20%20%20%20var%20am%20%3D%200%3B%0A%20%20%20%20%0A%20%20%20%20for%20%28var%20j%20%3D%200%3B%20j%20%3C%20n%3B%20j%2B%2B%29%20%7B%0A%20%20%20%20%20%20%2F%2Funcomment%20the%20break%20statement%20below%20when%20messing%20with%20the%20loop%0A%20%20%20%20%20%20%2F%2Fyour%20browser%20window%20will%20FREEZE%20otherwise!%0A%20%20%20%20%20%20%2F%2Fbreak%3B%0A%20%20%20%20%20%20%0A%20%20%20%20%20%20%2F%2F%20which%20wave%20are%20we%20currently%20on%3F%0A%20%20%20%20%20%20var%20m%20%3D%20%28%28i%2Bround%28n%2F2-%28n%252-1%29%29*j%29%25n%29%2Fn%3B%0A%20%20%20%20%20%20%2F%2F%20offset%20for%20this%20specific%20wave%0A%20%20%20%20%20%20var%20o%20%3D%20x*f%2B%28PI*2*m%29%3B%0A%20%20%20%20%20%20%2F%2F%20if%20the%20wave%20should%20be%20in%20the%20background%20don&amp;#x27;t%20set%20a%0A%20%20%20%20%20%20if%20%28cos%28o%2Bt%29%20%3E%20-b%29%20%7B%0A%20%20%20%20%20%20%20%20a%20%3D%20sin%28o%2Bt%29%3B%0A%20%20%20%20%20%20%20%20am%20%3D%20m%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20return%20%7By%3A%20a%2C%20m%3A%20am%7D%3B%0A%20%20%7D%0A%20%20%0A%20%20if%20%28split%20!%3D%200%29%20%7B%0A%20%20%20%20var%20abs_split%20%3D%20Math.ceil%28abs%28split%29%2B1%29%0A%20%20%20%20return%20screw%28n%2C%20f%2C%20z%2C%20sliders%2Fabs_split%2C%20Math.floor%28i%2Fabs_split%29%29.y*scale%2B.5%2B%28%28%28i%252%29-.5%29*.5*split%29%3B%0A%20%20%7D%20else%20if%20%28multiply%20%3E%201%29%20%7B%0A%20%20%20%20var%20abs_multiply%20%3D%20Math.ceil%28abs%28multiply%29%29%3B%0A%20%20%20%20var%20s%20%3D%20screw%28n%2C%20f%2C%20z%2C%20sliders%2C%20i%29%3B%0A%20%20%20%20return%20screw%28abs_multiply%2C%20f*10*abs_multiply%2C%20z*abs_multiply%2C%2064%2Fabs_multiply%2C%20i%2Fabs_multiply%29.y*scale*%28.2%2Fabs%28multiply%29%29%2B%28s.y*scale%2B.5%29%3B%0A%20%20%7D%20else%20%7B%0A%20%20%20%20return%20screw%28n%2C%20f%2C%20z%2C%20sliders%2C%20i%29.y*scale%2B.5%3B%0A%20%20%7D%0A%0A%7D&quot; &gt;
		The &quot;final&quot; Screw demo that cycles through a few wavy things that look a bit like screws.
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;sliderland.blinry.org&amp;#x2F;#%7B%0A%20%20%2F%2F%20speed%20%28negative%20values%20work%20too!%29%0A%20%20var%20z%20%3D%20t*2%3B%0A%20%20%2F%2F%20number%20of%20edges%20%28waves%29%20%28looks%20less%20good%20for%20n%20%3E%205%29%0A%20%20var%20n%20%3D%203%3B%0A%20%20%2F%2F%20wavelength%20%28some%20magic%20values%20here%20that%20make%20it%20look%20okayish%29%0A%20%20var%20l%20%3D%20%287*%283%2Fn%29%29%3B%0A%0A%20%20%2F%2F%20breakpoint%20when%20something%20goes%20into%20the%20background%0A%20%20var%20b%20%3D%20sqrt%282%2Fn%29%3B%0A%0A%20%20var%20a%20%3D%200%3B%0A%20%20%0A%20%20for%20%28var%20j%20%3D%200%3B%20j%20%3C%20n%3B%20j%2B%2B%29%20%7B%0A%20%20%20%20%2F%2Funcomment%20the%20break%20statement%20below%20when%20messing%20with%20the%20loop%0A%20%20%20%20%2F%2Fyour%20browser%20window%20will%20FREEZE%20otherwise!%0A%20%20%20%20%2F%2Fbreak%3B%0A%20%20%20%20%0A%20%20%20%20%2F%2F%20which%20wave%20are%20we%20currently%20on%3F%0A%20%20%20%20var%20m%20%3D%20%28%28i-j%29%25n%29%2Fn%3B%0A%20%20%20%20%2F%2F%20offset%20for%20this%20specific%20wave%0A%20%20%20%20var%20o%20%3D%20x*l%2B%28PI*2*m%29%3B%0A%20%20%20%20%2F%2F%20if%20the%20wave%20should%20be%20in%20the%20background%20don&amp;#x27;t%20set%20a%0A%20%20%20%20if%20%28cos%28o%2Bz%29%20%3E%20-b%29%20%7B%0A%20%20%20%20%20%20a%20%3D%20sin%28o%2Bz%29%3B%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20return%20a*.2%2B.5%3B%0A%0A%7D&quot; &gt;
		A barebones screw that is a bit easier to understand.
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;roofs-and-birds&quot;&gt;Roofs and Birds&lt;&#x2F;h2&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;sliderland.blinry.org&amp;#x2F;#%7B%0A%20%20%2F%2Fnumber%20of%20bars%20on%20screen%0A%20%20let%20bars%20%3D%204%0A%20%20%2F%2Foffset%20from%200%0A%20%20let%20off%20%3D%20t*.5%0A%20%20%0A%20%20%2F%2Fgenerate%20the%20bars%0A%20%20main_wave%20%3D%20sin%28%28-t%2Bfloor%28%28x%2Boff%29*bars%29%29*PI*.5%29%0A%20%20%0A%20%20%2F%2F%20a%20function%20that%20generates%20a%20triangle%20wave%20for%20the%20%22roofs%22%0A%20%20function%20triangle_wave%28x%29%20%7B%0A%20%20%20%20x%20%3D%20%28x%2FPI%29%252-1%0A%20%20%20%20if%20%28x%20%3C%20-.5%29%20%7B%0A%20%20%20%20%20%20return%20x*2%2B2%0A%20%20%20%20%7D%20else%20if%20%28x%20%3E%20.5%29%20%7B%0A%20%20%20%20%20%20return%20x*2-2%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20return%20-x*2%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20%0A%20%20%2F%2Fgenerate%20a%20triangle%20wave%20that%20matches%20the%20moving%20bars%20and%20mix%20it%20with%20the%20bars%20to%20create%20a%20smooth%20effect%2C%20also%20scaled%20with%20the%20bars%20to%20point%20in%20the%20right%20direction%0A%20%20return%20%28abs%28triangle_wave%28%28%28x%2Boff%29*bars%29*PI%29%29*main_wave*.3%2Bmain_wave*.7%29*.5%2B.5%0A%7D%0A&quot; &gt;
		A scrolling wave of bars with roofs on them.
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;sliderland.blinry.org&amp;#x2F;#%7B%0A%0A%20%20%2F%2F%20a%20function%20that%20generates%20a%20triangle%20wave%20for%20the%20%22roofs%22%0A%20%20function%20triangle_wave%28x%29%20%7B%0A%20%20%20%20x%20%3D%20abs%28%28x%2FPI%29%252%29-1%0A%20%20%20%20if%20%28x%20%3C%20-.5%29%20%7B%0A%20%20%20%20%20%20return%20x*2%2B2%0A%20%20%20%20%7D%20else%20if%20%28x%20%3E%20.5%29%20%7B%0A%20%20%20%20%20%20return%20x*2-2%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20return%20-x*2%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20%0A%20%20%2F%2Fnumber%20of%20bars%20on%20screen%0A%20%20let%20bars%20%3D%205%0A%20%20%2F%2Foffset%20from%200%0A%20%20let%20off%20%3D%20%28-t*.5%29%0A%20%20%0A%20%20%2F%2Fgenerate%20the%20bars%0A%20%20main_wave%20%3D%20sin%28%28-t%2Bfloor%28%28x%2Boff%29*bars%29%29*PI*.8%29%0A%20%20%0A%20%20%2F%2F%20if%20the%20offset%20is%20negative%20we%20need%20a%20fifferent%20offset%20for%20the%20triangle%20wave%20to%20make%20it%20point%20in%20the%20right%20direction%0A%20%20let%20offset_correct%20%3D%200%0A%20%20if%20%28off%20%3C%200%29%20offset_correct%20%2B%3D%20.5%0A%0A%20%20%2F%2Fhow%20much%20of%20the%20bar%20is%20a%20roof%0A%20%20let%20roof_factor%3D.3%0A%20%20%2F%2F%20glitch%20effect%20every%20few%20seconds%0A%20%20if%20%28%28i%254%29%2F4%20%3C%20sin%28t*.5%29%29%20roof_factor%20%3D%20-roof_factor%20%0A%0A%20%20%2F%2Fgenerate%20a%20triangle%20wave%20that%20matches%20the%20moving%20bars%20and%20mix%20it%20with%20the%20bars%20to%20create%20a%20smooth%20effect%2C%20also%20scaled%20with%20the%20bars%20to%20point%20in%20the%20right%20direction%0A%20%20%2F%2F%20return%20triangle_wave%28-x*PI*4%29*.5%2B.5%0A%20%20return%20%28abs%28triangle_wave%28%28%28x%2Boff%29*bars%2B.5%29*PI-%28.5*PI%29%29%29*main_wave*roof_factor%2Bmain_wave*%281-abs%28roof_factor%29%29%29*.5%2B.5%0A%7D%0A&quot; &gt;
		Same demo as above with a glitch effect switching between bird and roof mode.
	&lt;&#x2F;a&gt;
&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>
    <entry xml:lang="en">
        <title>Swaylock Mobile</title>
        <published>2022-03-03T00:00:00+00:00</published>
        <updated>2023-05-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/swaylock-mobile/"/>
        <id>https://slatecave.net/creations/swaylock-mobile/</id>
        
        <summary type="text">Sxmo with sway is great but unfortunately it lacks a proper lockscreen, This is my attempt to fix that situation by adding a keypad and some mobile related cosmetic features to swaylock.</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/swaylock-mobile/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;So far I have implemented:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A flat typing indicator that is (in my opinion) a better fit for small screens&lt;&#x2F;li&gt;
&lt;li&gt;A numeric keypad at the bottom, currently only reacting to touch input that is not very configurable&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Also this is my first wayland project and my first attempt at writing more than a &quot;Hello World&quot; in C.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;log&quot;&gt;Log&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;2022-03-06-initial-release&quot;&gt;2022-03-06 Initial release&lt;&#x2F;h3&gt;
&lt;p&gt;The first goal was to get a working prototype that looks acceptable (Some alignments are still off). I have reached this by implementing a flat indicator that works a bit like the ring but is a line with the width configured using the radius and if necessary with a text-box below.&lt;&#x2F;p&gt;
&lt;p&gt;I was able to lock my phone by manually calling it and unlock it using the new keypad, next step is to integrate it with Sxmo so that I finally don&#x27;t have to rely on the incompetence of a potential attacker for the security of my phone.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2022-03-08-schlock&quot;&gt;2022-03-08 schlock&lt;&#x2F;h3&gt;
&lt;p&gt;Apparently I&#x27;m not the first to attempt to write a lockscreen with numeric keypad based on swaylock, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;telent&#x2F;schlock&quot;&gt;schlock&lt;&#x2F;a&gt;, written by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ww.telent.net&#x2F;&quot;&gt;@telent_net&lt;&#x2F;a&gt; appeared about two weeks ago. (Thank you for the link goes to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.willowbarraco.fr&#x2F;&quot;&gt;Willow Barraco&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;It also is targeted at the touchscreen + numeric pin usecase but our approaches under the hood seem to be completely different.&lt;&#x2F;p&gt;
&lt;p&gt;While the overall approach with schlock is to replace the UI of swaylock with a virtual numpad and a custom indicator (see schlock&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;telent&#x2F;schlock&#x2F;blob&#x2F;main&#x2F;render.c&quot;&gt;render.c&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;telent&#x2F;schlock&#x2F;blob&#x2F;main&#x2F;pinentry.c&quot;&gt;pinentry.c&lt;&#x2F;a&gt;) the my swaylock-mobile trys to adapt and expand the UI to include touchscreen functionality so that my fork remains useful on non-touchscreens. However, the outcome is mostly the same for touchscreens.&lt;&#x2F;p&gt;
&lt;p&gt;On the password matching side schlock is a bit more creative than swaylock-mobile, it seems to (I&#x27;m not sure, judging based on having looked at the commit diffs for about half an hour) have a separate code-path that matches against the hash of a pin in a file (that could be a backend and it would be awesome!) while mine works like the phosh lockscreen and simply asks pam (or whatever your swaylock backend is) if the entered pin is the users password (ab)using the existing mechanism for keyboards.&lt;&#x2F;p&gt;
&lt;p&gt;There is no better here, just ideas from two screenlockers aimed at a phone that runs linux with their own ideas on how to get from a common starting point to a common goal.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2022-03-19-integrating-with-sxmo-1-9-0&quot;&gt;2022-03-19 Integrating with sxmo 1.9.0&lt;&#x2F;h3&gt;
&lt;p&gt;I wrote a &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;integrating-my-screenlocker-with-sxmo-part-1&#x2F;&quot;&gt;blogpost about how screenlocking in sxmo works&lt;&#x2F;a&gt; pre 1.9.0 and post 1.9.0 and how I&#x27;d integrate swaylock-mobile (or any other screenlocker) with it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2023-02-26-curently-abandonned&quot;&gt;2023-02-26 Curently abandonned&lt;&#x2F;h3&gt;
&lt;p&gt;Because of some real-life happenings and some other projects I declare this as officially abandonned, I currently don&#x27;t need my phone. That doesn&#x27;t mean, that I won&#x27;t pick it up again.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2023-05-28-sxmo-swaylock&quot;&gt;2023-05-28 sxmo_swaylock&lt;&#x2F;h3&gt;
&lt;p&gt;KaffeinatedKat merged the swaylock-mobile keypad into his &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;KaffeinatedKat&#x2F;sxmo_swaylock&quot;&gt;sxmo_swaylock&lt;&#x2F;a&gt; project after I &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mortie&#x2F;swaylock-effects&#x2F;issues&#x2F;100&quot;&gt;proposed it to be merged into swaylock-effects&lt;&#x2F;a&gt;. Thank you for adopting it!&lt;&#x2F;p&gt;
&lt;p&gt;Yes I&#x27;m very okay with someone else adoping the code and&#x2F;or idea too!&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>My current Smarthome Setup</title>
        <published>2021-08-24T00:00:00+00:00</published>
        <updated>2022-04-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/smarthome/"/>
        <id>https://slatecave.net/creations/smarthome/</id>
        
        <summary type="text">A raspberry pi under my desk that switches screens and audio on and off. commandline edition!</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/smarthome/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;features&quot;&gt;Features&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Can turn GPIOs on and off (I use that for routing audio and switching relays for power outlets)&lt;&#x2F;li&gt;
&lt;li&gt;Pings my machines every few seconds to find out if they are on (I have planned for when that assumption falls flat)&lt;&#x2F;li&gt;
&lt;li&gt;Gemini based API (for keyboard shortcuts) and dashboard&lt;&#x2F;li&gt;
&lt;li&gt;Scriptable using anything that can read from and write to stdio (I use lua)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;documentation&quot;&gt;Documentation&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-action-next&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;codeberg.org&amp;#x2F;slatian&amp;#x2F;smarthome&quot; &gt;
		Detailed documentation is in the README
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Lunar Widgets - Lua scriptable Gtk Widgets</title>
        <published>2021-07-29T00:00:00+00:00</published>
        <updated>2021-07-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/lunar-widgets/"/>
        <id>https://slatecave.net/creations/lunar-widgets/</id>
        
        <summary type="text">Lua scripting for Gtk without gobject introspection, similar to eww, useful for prototyping</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/lunar-widgets/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;This started out as a simple program to show custom keyboards (the test.lua example is a custom keyboard for newsboat) that could be useful on a touchscreen but is now going in the general direction of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;elkowar&#x2F;eww&quot;&gt;Eww&lt;&#x2F;a&gt; (This project is far away from what Eww can do).&lt;&#x2F;p&gt;
&lt;p&gt;The API is a bit more powerful than documented in the README, it&#x27;s probably best to look at the examples and the sourcecode to find out what else can be done. I once wrote a prototype for a notification display that actually worked (notifications got fed in via stdin).&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>DDB - Object Key Vale Store</title>
        <published>2021-07-11T00:00:00+00:00</published>
        <updated>2021-07-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/ddb/"/>
        <id>https://slatecave.net/creations/ddb/</id>
        
        <summary type="text">DDB is a simple in memory key value store with the goal to serve as IPC (Inter Process Communication) infrastructure between simple and less simple scripts.</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/ddb/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;Usage can include piping system usage information into multiple receiver scripts, storing the last wheater report for offline usage or exposing an interface for a background service. A bit like dbus but a lot simpler.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-does-it-exist&quot;&gt;Why does it exist ?&lt;&#x2F;h2&gt;
&lt;p&gt;There wasn&#x27;t really an easy and obvious way to let two scripts in bash, lua, python or whatever talk to each other, especially in many to many communication scenarios. That&#x27;s why DDB (I wanted to use it for scripts on my desktop) was written in its first version, listening on a TCP socket on localhostand granting access to a hash table and speaking a line based protocol with spaces as field seperators with (horrible) bash scripts as client implementations.&lt;&#x2F;p&gt;
&lt;p&gt;If you are interested in the protocol, its documented in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;ddb-object-key-value-store&#x2F;src&#x2F;branch&#x2F;main&#x2F;README.md#protocol&quot;&gt;README&lt;&#x2F;a&gt; and can be considered stable.&lt;&#x2F;p&gt;
&lt;p&gt;Features that I needed in DDB:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Introspection of some kind&lt;&#x2F;li&gt;
&lt;li&gt;Ability to use standard-io for most scripts (that way you don&#x27;t need any fancy debugging tools)&lt;&#x2F;li&gt;
&lt;li&gt;A protocol wich can be implemented in a pretty simple way&lt;&#x2F;li&gt;
&lt;li&gt;Storage integrated into the ipc mechanism&lt;&#x2F;li&gt;
&lt;li&gt;Ability to subscribe to events&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;usage&quot;&gt;Usage&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m currently (&lt;time&gt;2023-05-28&lt;&#x2F;time&gt;) using it as the message bus behind &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;creations&#x2F;smarthome&#x2F;&quot;&gt;my little smarthome&lt;&#x2F;a&gt;, but it is probably going to the attic soon.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Vala project setup script</title>
        <published>2021-05-27T00:00:00+00:00</published>
        <updated>2021-05-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/vala-project-setup-script/"/>
        <id>https://slatecave.net/creations/vala-project-setup-script/</id>
        
        <summary type="text">This project is a lua script that automates the process of setting up a project with vala, meson and git. Similar to what happens when yo tell cargo or go to create a new project. It also sets up some helper scripts and a README with build instructions.</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/vala-project-setup-script/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;When experimenting with gtk and Vala I notices that I was copy-pasting files to bootstrap new projects and experiments all the time, replacing names by hand and … making mistakes. So I put everything into a lua script that helps me with bootstrapping Vala based projects (which at the time of writing, mid 2022 I still do on occasion)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;features&quot;&gt;Features&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;automatic-generation-of-meson-build-files&quot;&gt;Automatic generation of meson.build files&lt;&#x2F;h3&gt;
&lt;p&gt;The script generates two buildfiles for meson, one in the projects root where meson finds it and a second one in the src folder that contains a list of all the files and your dependency list you probably want to extend.&lt;&#x2F;p&gt;
&lt;p&gt;It also gives you a script that you can call to update the list of files to be compiled (list of &lt;code&gt;*.vala&lt;&#x2F;code&gt; files in the src directory) which is good enough for most projects.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;one-command-to-build-and-run&quot;&gt;One command to build and run&lt;&#x2F;h3&gt;
&lt;p&gt;Building with meson is a multi command process, you have to tell meson to generate the build directory then you have to tell ninja to do the actual building, find the result and run it … too many braincycles wasted for my taste, this script gives you a &lt;code&gt;build&lt;&#x2F;code&gt; and a &lt;code&gt;run&lt;&#x2F;code&gt; script that can do all the work for you (the run script invoking the buildscript on every call)&lt;&#x2F;p&gt;
&lt;p&gt;Since you are in charge as soon as the script exits you can do whatever the hell you want with those.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;generates-an-initial-readme-md-wich-explains-how-to-use-the-build-system&quot;&gt;Generates an initial README.md wich explains how to use the build-system&lt;&#x2F;h3&gt;
&lt;p&gt;Probably most often asked question by everyone: &lt;q&gt;How do I build that thing?&lt;&#x2F;q&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This thing generates a README.md skeleton for you. (you still have to fill it with information, but at least you don&#x27;t have to copypasta your explanation of how to use meson)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;optionally-generates-a-hello-world-program&quot;&gt;Optionally generates a hello world program&lt;&#x2F;h3&gt;
&lt;p&gt;Sometimes its nice to have a bit of code that reminds one of the Language syntax … , this also helps with testing the setup script itself.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;sets-up-a-git-repository-and-makes-the-initial-commit&quot;&gt;Sets up a git repository and makes the initial commit&lt;&#x2F;h3&gt;
&lt;p&gt;Another two commands that you type in everytime and are everytime the same commands, so while I was at it I automated it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;example-invocation&quot;&gt;Example invocation&lt;&#x2F;h2&gt;
&lt;figure&gt;
&lt;samp&gt;
&lt;code&gt;$ &lt;kbd&gt;setup_project.lua&lt;&#x2F;kbd&gt;&lt;&#x2F;code&gt;
New Vala Project&lt;br&gt;
This program will setup a new vala project using meson&lt;br&gt;
-------------------------------------------------------&lt;br&gt;
Please enter the name of your project&lt;br&gt;
&amp;gt; &lt;kbd&gt;slate&lt;&#x2F;kbd&gt;&lt;br&gt;
Please enter the reverse domain name of the projects home&lt;br&gt;
&amp;gt; &lt;kbd&gt;net.slatecave&lt;&#x2F;kbd&gt;&lt;br&gt;
Create a main.vala file? (y&#x2F;N)&lt;br&gt;
&amp;gt; &lt;kbd&gt;y&lt;&#x2F;kbd&gt;&lt;br&gt;
rename.sh is a script that is able to run a single sed command across every *.vala file in the src&#x2F; directory replacing anything with everything, wich is useful for renaming classes and interfaces but can also do a very excellent job at ruining your day without asking if you screw up and don&#x27;t have a backup!&lt;br&gt;
-- If you enable it make sure you have a versioning system ready!! --&lt;br&gt;
Add rename.sh? (Use a versioning&#x2F;backup system!) (y&#x2F;N)&lt;br&gt;
&amp;gt; &lt;kbd&gt;y&lt;&#x2F;kbd&gt;&lt;br&gt;
Initialize git repository? (y&#x2F;N)&lt;br&gt;
&amp;gt; &lt;kbd&gt;y&lt;&#x2F;kbd&gt;&lt;br&gt;
Add a remote git repository? (y&#x2F;N)&lt;br&gt;
&amp;gt; &lt;kbd&gt;y&lt;&#x2F;kbd&gt;&lt;br&gt;
Ok, create your remte repo and give me the (clone) address.&lt;br&gt;
&amp;gt; &lt;kbd&gt;https:&#x2F;&#x2F;example.org&#x2F;git&#x2F;slate&lt;&#x2F;kbd&gt;&lt;br&gt;
-------------------------------------------------------&lt;br&gt;
Project Executable Name: slate&lt;br&gt;
Project id: net.slatecave.slate&lt;br&gt;
Local project folder: &#x2F;home&#x2F;baschdel&#x2F;Development&#x2F;site_generator&#x2F;src&#x2F;projects&#x2F;vala-project-setup-script&lt;br&gt;
+ Add a main.vala file&lt;br&gt;
+ Add the rename.sh bulksed script&lt;br&gt;
+ Initialize a git repository with remote https:&#x2F;&#x2F;example.org&#x2F;git&lt;br&gt;
Is this information correct? (y&#x2F;N)&lt;br&gt;
&amp;gt; &lt;kbd&gt;N&lt;&#x2F;kbd&gt;&lt;br&gt;
Please restart the script then.&lt;br&gt;
&lt;&#x2F;samp&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;I hope this helps people understand the code (you shouldn&#x27;t let code that some random wierdo from the Internet wrote manage your projects without making sure that code does what you want).&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>A wrapper for a terraria server with some perks</title>
        <published>2021-05-05T00:00:00+00:00</published>
        <updated>2021-05-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/terraria-server-wrapper/"/>
        <id>https://slatecave.net/creations/terraria-server-wrapper/</id>
        
        <summary type="text">Adds automatic shutdown when not needed, Matterbridge support and exposes Server commands to the ingame chat.</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/terraria-server-wrapper/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;h2 id=&quot;how-it-came-into-existence&quot;&gt;How it came into existence&lt;&#x2F;h2&gt;
&lt;p&gt;Long story short: A friend convinced me and a few other friends to try out &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;terraria.org&#x2F;&quot;&gt;Terraria&lt;&#x2F;a&gt;, we played together using his PC as the server and immedately ran into a strange problem related to us being in germany and demanding bandwidth from our ISPs.&lt;&#x2F;p&gt;
&lt;p&gt;Setting up a server on a cloudserver wasn&#x27;t a big problem, but I wnated something that shut the Server down when nobody used it while we were in School. So I wrote a few lines of python … and called that thing &lt;code&gt;terraria-supervisor.py&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The other features were a: &lt;q&gt;Wouldn&#x27;t it be cool if?&lt;&#x2F;q&gt;&lt;&#x2F;p&gt;
&lt;p&gt;First we got annoyed by the time it took to get dark (or become day again), and I wasn&#x27;t very keen on logging into my Server every time so I added the cheaty part which redirected the server output to the ingame chat and in reverse read the ingame chat and looked for something command like and pipes that into the admin console using a pcall.&lt;&#x2F;p&gt;
&lt;p&gt;The chatty part was me already having a Matterbridge set up for our little community (because I don&#x27;t like Discord) and on the other end having a script that can read and write our ingame chat.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-few-tips-on-game-server-administration&quot;&gt;A few tips on Game Server administration&lt;&#x2F;h2&gt;
&lt;p&gt;Unfortunately that server didn&#x27;t last very long until it was abandonned, here is the lesson I learned:&lt;&#x2F;p&gt;
&lt;p&gt;Disable cheating and bringing in stuff from the outside (&lt;em&gt;and write that rule along with the reasoning down somewhere!&lt;&#x2F;em&gt;), someone will eventually join with their level 9001 character or turn the cheatclient on, you all have a couple of hours of fun beat that one boss in no time at all and then be greeted with a gameworld that is too difficult for everyone playing fair (and boring for the others), a rollback feels like loosing progress, both options kill the world on the server and the players (including YOU, the admin) won&#x27;t come back.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Dragonstone - Gemini&#x2F;Gopher Browser</title>
        <published>2019-11-03T00:00:00+00:00</published>
        <updated>2019-11-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/dragonstone/"/>
        <id>https://slatecave.net/creations/dragonstone/</id>
        
        <summary type="text">Dragonstone is a browser for the Gopher and Gemini protocols built with Gtk 3.</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/dragonstone/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;It started as a Gopher browser inspired by the Pocket Gopher Android App, later support for the gemini and finger protocols was added. It also has theming support for documents. I use it as my primary gopher&#x2F;gemini browser.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-little-background-and-history&quot;&gt;A little background and history&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;so-why-does-someone-write-a-browser-at-all&quot;&gt;So why does someone write a browser at all?&lt;&#x2F;h3&gt;
&lt;p&gt;The project started as a coincidence: At the end of 2019 I lwaenewd about a protocol called gopher and started exploring some gopherholes … On an Android Phone with an app called Pocket Gopher which looked nice, was useable but had no cache at all and I wanted a desktop app anyway. At the same time I found a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;youtube.com&#x2F;playlist?list=PLriKzYyLb28mn2lS3c5yqMHgLREi7kR9-&quot;&gt;Vala Tutorial by Alessandro Castellani&lt;&#x2F;a&gt; and needed a project to turn all the things I&#x27;ve learned into something useful …&lt;&#x2F;p&gt;
&lt;p&gt;While the original plan was to write a gopher browser with local file browsing capabilities I discovered the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gemini.circumlunar.space&#x2F;&quot;&gt;Gemini protocol&lt;&#x2F;a&gt;, and wrote &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dragonstone&#x2F;commit&#x2F;b66b58e23f6d9962719507355ecf007f227f7505&quot;&gt;some extra code to handle gemini requests&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Fun fact: A few days later Julien Blanchard started working on the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sr.ht&#x2F;~julienxx&#x2F;Castor&#x2F;&quot;&gt;Castor browser&lt;&#x2F;a&gt;, where I later copied the idea to use a GtkTextView for document rendering (dragonstones terrible link implementation was my idea to get rid of some performance issues with the button in textview approach).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;request-infrastructure&quot;&gt;Request infrastructure&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;b&gt;Or: Why making a browser is easy but hard to get right&lt;&#x2F;b&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The request infrastructure of Dragonstone was originally based on the idea that a request with an uri would be passed tp a resource store, processed and then be returned to the tab wich selects a rendering widget based on the success and file type. (simple gopher browser with the capability to view local files)&lt;&#x2F;p&gt;
&lt;p&gt;Letting the widgets see (and touch, uploading in dragonstone is a giant hack) the requests was a GIGANTIC mistake, UI code should NEVER do any application logic …&lt;&#x2F;p&gt;
&lt;p&gt;It also turned out (earlier actually) that thinking in therms of resource stores was also a flawed Idea, caching is required across almost all networking protocols and sometimes a resource store needs extra information like a certificate, the &quot;session&quot; was born a rewrite of a lot of code later to have this functionality in seperate blocks.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;failed-rewrites&quot;&gt;Failed rewrites&lt;&#x2F;h3&gt;
&lt;p&gt;There were two attempts to rewrite Dragonstone:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;browser-experiment.nightcat&quot;&gt;The Nightcat Experiment&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;browser-experiment.slate&quot;&gt;The Slate Experiment&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;(Note that I&#x27;ll reuse both names and I&#x27;ll refer to them as experiments and eventually update the git repo. You probybly have noticed, that I reused Slate for this website and my new nick)&lt;&#x2F;p&gt;
&lt;p&gt;Nightcat was an excellent Request framework, except that I realized too late that When streaming resurces there could be a result before the final result and that killed it because it was built with the assumption that the browser waits for the result with only a progress indicator.&lt;&#x2F;p&gt;
&lt;p&gt;Slate on literally the other end reused Dragonstones flawed request framework in an attempt to decouple the UI from the requests entirely before eventually replacing the requests framework with a better one. It even reused Dragonstones document renderer, unfortunately this approach takes time and I put it on hold in favour of some other project. (It is unuseable in its current state, but maybe I&#x27;ll pick it back up some day)&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>A Tetris clone</title>
        <published>2019-05-21T00:00:00+00:00</published>
        <updated>2019-05-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/tetris/"/>
        <id>https://slatecave.net/creations/tetris/</id>
        
        <summary type="text">A simple Tetris game written in Python that started as a scool exercise.</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/tetris/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;It uses the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cs1graphics.org&#x2F;&quot;&gt;cs1graphics library&lt;&#x2F;a&gt; for painting and is the project I learned python with.&lt;&#x2F;p&gt;
&lt;p&gt;Information about gow to play and a little overview for Modding the game is in the README.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-bit-about-the-engine&quot;&gt;A bit about the Engine&lt;&#x2F;h2&gt;
&lt;p&gt;The engine is split into two parts:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The renderer which gets an array of squares the preview, score and some text for an optional overlay and draws this into a window.&lt;&#x2F;li&gt;
&lt;li&gt;The other part is the game mechanics part which implements the game logic.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The tile rotations are calculated on the fly from an array of relative square positions, so if you want to try out some crazy shapes go ahed (The function is a generator so in theory one could also generate crazy tiles on the fly)&lt;&#x2F;p&gt;
&lt;p&gt;Because of the split theming (alias changing the colors) is also pretty simple. Also I remember the black-white version looking rad!&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>A simple TicTacToe game</title>
        <published>2018-07-07T00:00:00+00:00</published>
        <updated>2018-07-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/tic-tac-toe/"/>
        <id>https://slatecave.net/creations/tic-tac-toe/</id>
        
        <summary type="text">Written for the commandline this is my first game I&#x27;ve written myself, still fun to have around in 2020.</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/tic-tac-toe/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;h2 id=&quot;how-to-play&quot;&gt;How to play&lt;&#x2F;h2&gt;
&lt;figure&gt;
&lt;figcaption&gt;A sceenshot of a game currently in progress, playing field rendered in ascii-art at the top, a little guide which input number corresponds with wich field and a prompt asking O for their move.&lt;&#x2F;figcaption&gt;
&lt;pre&gt;&lt;samp&gt;
                 ### ##\          &#x2F;## ###                 
                 ###  \##\      &#x2F;##&#x2F;  ###                 
                 ###    \##\  &#x2F;##&#x2F;    ###                 
                 ###      \####&#x2F;      ###                 
                 ###      &#x2F;####\      ###                 
                 ###    &#x2F;##&#x2F;  \##\    ###                 
                 ###  &#x2F;##&#x2F;      \##\  ###                 
                 ### ##&#x2F;          \## ###                 
##########################################################
##########################################################
                 ###                  ### ##\          &#x2F;##
                 ###                  ###  \##\      &#x2F;##&#x2F; 
                 ###                  ###    \##\  &#x2F;##&#x2F;   
                 ###                  ###      \####&#x2F;     
                 ###                  ###      &#x2F;####\     
                 ###                  ###    &#x2F;##&#x2F;  \##\   
                 ###                  ###  &#x2F;##&#x2F;      \##\ 
                 ###                  ### ##&#x2F;          \##
##########################################################
##########################################################
##\          &#x2F;## ###    __________    ###    __________   
 \##\      &#x2F;##&#x2F;  ###   &#x2F; ________ \   ###   &#x2F; ________ \  
   \##\  &#x2F;##&#x2F;    ###  &#x2F; &#x2F;        \ \  ###  &#x2F; &#x2F;        \ \ 
     \####&#x2F;      ### | |          | | ### | |          | |
     &#x2F;####\      ### | |          | | ### | |          | |
   &#x2F;##&#x2F;  \##\    ### | |          | | ### | |          | |
 &#x2F;##&#x2F;      \##\  ###  \ \________&#x2F; &#x2F;  ###  \ \________&#x2F; &#x2F; 
##&#x2F;          \## ###   \__________&#x2F;   ###   \__________&#x2F;  
789
456
123
O&#x27;s turn:
&lt;&#x2F;samp&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The game is pretty simple, X starts and then X and O make their moves by entering the number that corresponds to the field they want to place their marker on, if one of then gets a row of three they win, if the field fills up dbefore that happens the game is a draw.&lt;&#x2F;p&gt;
&lt;p&gt;Hint: Your Numpad probably has the same layout as the field numbers!&lt;&#x2F;p&gt;
</content>
    </entry>
</feed>

